I wrote a quick PEG Parser for Julia with Packrat capabilities:
https://github.com/abeschneider/PEGParser
It's a first draft and needs a ton of work, testing, etc., but if this is
of interest to anyone else, here is a quick description.
Grammars can be defined using most of the standard EBNF syntax. For
example, a simple math grammar can be defined as:
@grammar mathgrammar begin
start = expr
number = r"([0-9]+)"
expr = (term + op1 + expr) | term
term = (factor + op2 + term) | factor
factor = number | pfactor
pfactor = ('(' + expr + ')')
op1 = '+' | '-'
op2 = '*' | '/'
end
To parse a string with the grammar:
(node, pos, error) = parse(mathgrammar, "5*(2-6)")
This will create an AST which can then be transformed to a value. Currently
this is accomplished by doing:
math = Dict()
math["number"] = (node, children) -> float(node.value)
math["expr"] = (node, children) ->
length(children) == 1 ? children : eval(Expr(:call, children[2],
children[1], children[3]))
math["factor"] = (node, children) -> children
math["pfactor"] = (node, children) -> children[2]
math["term"] = (node, children) ->
length(children) == 1 ? children : eval(Expr(:call, children[2],
children[1], children[3]))
math["op1"] = (node, children) -> symbol(node.value)
math["op2"] = (node, children) -> symbol(node.value)
Ideally, I would like to simplify this to using multi-dispatch on symbols
(see previous post), but for now this is the easiest way to define actions
based on node attributes.
Finally, to transform the tree:
result = transform(math, node) # will give the value of 20
Originally I was going to attach the transforms to the rules themselves
(similiar to boost::spirit). However, there were two reasons for not doing
this:
1. To implement the packrat part of the parser, I needed to cache the
results which meant building an AST anyways
2. It's nice to be apply to get different transforms for the same
grammar (e.g. you may want to transform the result into HTML, LaTeX, etc.)
The downside of the separation is that it adds some more complexity to the
process.