I started working on a little project over the weekend that would involve needing to execute small user supplied programs on a webpage. As these programs would be fairly trivial (essentially just small bits of math) and as I was thinking it might be fun to provide a way to share them around, it made sense to look in to something a little safer and more constrained than just letting the user type some JavaScript and then evaling it.
Looking around for something to use for this I stumbled upon PEG.js, a JavaScript parser generator project, and I quickly got sidetracked with playing around with that. I ended up deciding to get my hands a little dirty and have some fun implementing a simple toy language. Having fairly little experience with parser generators and language implementation in general it took a little bit of time to get up and working with PEG.js, but after a while I started to grok it and found the whole thing fairly easy to work with.
As an example of how this works, consider the following simple grammar:
start = ((op:binop "\n"*) { return op })+ binop = left:[0-9]+ op:[+-/*] right:[0-9]+ { return { operator: op, args: [parseInt(left.join("")), parseInt(right.join(""))] } }
Running this through PEG.js results in a parser that can parse a series of binary operations between integers separated by newline characters. So, for example, the parser could then be fed this as input:
22+3 2+3
The parser that was generated will take this input and transform it in to a JSON abstract syntax tree that looks like this:
[ { "operator": "+", "args": [ 22, 3 ] }, { "operator": "+", "args": [ 2, 3 ] } ]
Pretty simply stuff and the PEG.js website has some good documentation on it as well as a convenient page where you can test out grammars - just type in your grammar, let it generate a parser, feed the parser with some input and it will spit out the resulting JSON AST right there on the page. Very convenient for testing things out.
After about a day of poking around with PEG.js I had put together a simple grammar and paired the generated parser with a simple interpreter. You can try out the working result here - it's very basic, so I don't think it needs a whole lot of explanation to use. I should warn though that I have put in little to no error reporting at this point.
All in all it was a fun distraction and I was happy to spend some time playing with something outside of my normal programming adventures. Next up, I'll be returning to the project that started me off on this tangent in the first place.