Search code examples
iossyntaxpegkit

PegKit: Grammar syntax which handles same work in different ways


I'm working on some code which uses PegKit and I've hit something I'm not sure how to figure out. I have a syntax that looks like this (simplified):

expr = runtimeExpr | objectExpr;
runtimeExpr = is? runtimeObject;
objectExpr = runtimeObject keyPath;
runtimeObject = '[' string ']';
is = 'is';
keyPath = string;

I'm looking for the following results:

[abc] -> runtime expr.
is [abc] -> runtime expr.
[abc].def -> object expr.

However what is occurring is the generated parser code looks like this:

if ([self predicts:STLOGEXPRESSIONPARSER_TOKEN_KIND_IS, 0]) {
    [self runtimeExpr_]; 
} else if ([self predicts:STLOGEXPRESSIONPARSER_TOKEN_KIND_OPEN_BRACKET, 0]) {
    [self objectExpr_]; 
}

Which effective says that in order to parse a runtime expr, it has to start with 'is'. Which means that [abc] is being passed as a object expr instead.

So what i need help with is understanding how to express this logic in the grammar syntax:

  1. If the string starts with a 'is', followed by a runtimeObject, or is only an runtimeObject, then process it as a runtimeExpr.

  2. Otherwise process it as an objectExpr.


Solution

  • Creator of PEGKit here.

    I believe the problem here is the leading optional is?. Any rule that begins with an optional prefix like this, and then subsequently matches something similar or identical to another rule (runtimeObject in this case) can cause problems.

    But the solution is easy. Just reorder things a bit. PEGKit is deterministic which means it will try the OR alternatives in the order you specify in the grammar. So in this case just place the longer alternative rule (objectExpr) first (before runtimeExpr in the expr rule).

    Try this, I believe everything will work out:

    expr = objectExpr | runtimeExpr;
    objectExpr = runtimeObject keyPath;
    runtimeExpr = is runtimeObject | runtimeObject;
    runtimeObject = '[' string ']';
    is = 'is';
    keyPath = string;
    

    Note the changes I made to both the expr and runtimeExpr rules. I suspect only the change to expr is necessary to solve this problem, but the change to runtimeExpr is harmless. Experimentation should tell you whether the runtimeExpr change is actually necessary.