Search code examples
javascriptpegpegjs

PEGJS : Nested pegjs grammar


start
  = intExp

intExp
  = andIntExp
  / orIntExp

andIntExp 
   = integer (andExp intExp)*

orIntExp 
   = integer (orExp intExp)*

andExp 
  = space* "and" space* { return "and";}

orExp 
  = space* "or" space*  { return "or";}

space 
  = [\n \t]

integer "integer"
  = digits:[0-9]+ { return parseInt(digits.join(""), 10); }

I want to parse input like

2 or 2 or 2 or 2 and 2 and 2 but 2 or 2 and 2 is invalid. In short I dont want and and or to occur together within the input. Is there any way to do this with peg, without involving javascript and storing previously seen variable (For which I already have a solution) ?


Solution

  • Parsing expression grammars are deterministic. They try the first match and fail on first mismatch, also, they don't backtrack. You can just negate ambiguous expressions and your grammar will work as expected:

    Start
      = IntExpr
    
    IntExpr
      = OrIntExpr
      / AndIntExpr
    
    OrIntExpr
      = Integer _ !"and" ("or" _ OrIntExpr)?
    
    AndIntExpr
      = Integer _ ("and" _ AndIntExpr)?
    
    Integer "integer"
      = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
    
    _ "space"
      = [\n \t]*
    

    Our OurExpr refuses matching if receives and and jumps to the next option.

    The following rules have been tested:

    1 and 2 and 3 => PASS

    1 => PASS

    4 or 2 or 1 => PASS

    9 and 2 or 3 => FAIL