I will probably implement support for left recursion in PEG.js - it is possible (see e.g. http://www.vpri.org/pdf/tr2007002_packrat.pdf). After that, the grammar could be rewritten to evaluate in the correct order.
(Another alternative - which works right now - is to change the parsing expressions to something like "additive ([+-] additive)*" and deal with the whole chain of operations with the same priority at once. I didn't use this in the example as I wanted it to be as simple as possible.)
First, you'll note the algorithm is extremely complicated--nothing like the simple top-down algorithm that makes PEGs so attractive. Not only is it complicated, it misses basic refactoring issues--some logic is duplicated across functions, and the functions interact in ugly ways.
Second, it doesn't even handle left recursion correctly. Throw a ruleset like this at their parser
A -> B "a"
B -> C "b"
C -> B / A / "c"
and it will explode into a million little pieces, because the authors did not account for any recursive rule having multiple recursion points. Don't even try something like
S -> A / B
A -> A "a" / B / a
B -> B "b" / A / "b"
What was your final result? Did you implement the left recursion in the way the paper describes, invented/found some other way or abandoned the whole idea?
It involves the memo entries being able to remember which left-recursive results they are dependent on. This way, when a left-recursive rule produces a result that is dependent on itself, it knows that this match can possibly be "grown" through repeated iterations. That's a basic sketch of the idea. Performance properties remain the same in the case of left-recursive rules that are not interdependent. I don't really know what they are like for large numbers of interdependent left-recursive rules--but if you have a language like that, better to use an Earley or GLR parser.
I'm still working on it. It passes a battery of test cases, two of which I posted above, but I'm not 100% confident in it just yet. Also, as posted above, I'm trying to get permission from the higher-ups to release the code into the wild.
The issue is that a top-down grammar cannot handle left-recursion, since it naturally leads to infinite recursion when being parsed from the top down.
I actually had to handle the left-recursion problem for a PEG I wrote in-house--right now I'm trying to convince my boss to let me open-source it :)
term ::= factor ( ('*' | '/') factor )* ;
If you introduce regex-like closure operators to model loops, life gets much easier, and you can build your trees the right way around.How applicable this is to PEGs is another matter though. I generally write my parsers by hand, and I parse expressions with operator precedence.
And for stuff that LL(1) can't handle, I think GLR is a better approach.
In the simplest version, each rule can be seen as a function that calls other rules, passing along a memo table as it goes. It's entirely stateless and functional, except for the memo table. In addition, each function is only a few lines.
Backtracking isn't a hack--it's the natural result of statelessness. There's no cursor that needs to be backtracked.
Asking the user to have to worry about this stuff, when he doesn't have to, is not good software design. Not all of us are writing high performance parsers for complicated languages.