1

I'm very new to writing grammars (first time ever to be exact) and I'd like to create a grammar that can return an AST for basic logic statements. So far I have a grammar that can handle AND, OR logic (I simply modified the basic calculator example from the official pegjs site). Here is what the grammar currently does:

The statement

item1 && item2

Returns the AST

{
   "type": "AND",
   "left": "item1",
   "right": "item2"
}

The statement:

(item1 || item2) && item3

Returns the AST

{
   "type": "AND",
   "left": {
      "type": "OR",
      "left": "item1",
      "right": "item2"
   },
   "right": "item3"
}

Here is the grammar that I have so far. It can be pasted directly into the online pegjs parser (http://pegjs.majda.cz/online).

start
  = logical_or

logical_or
  = left:logical_and ws+ "||" ws+ right:logical_or { return {type: "OR", left:left, right:right} }
  / logical_and

logical_and
  = left:primary ws+ "&&" ws+ right:logical_and { return {type: "AND", left:left, right:right} }
  / primary

primary
  = token
  / "(" logical_or:logical_or ")" { return logical_or; }

token 
  = token:[a-zA-Z0-9_]+ { return token.join(""); }

ws 
  = [ \t]

What I'd like to do is add support for the NOT operator (!). So for example I'd like to be able to parse the following statements:

item1 && !item2
!(item1 && item2 && item3)

My first question is how do you typically represent the NOT operator in an AST? It seems like the not operator can be applied to an entire left or right branch of the AST but unlike OR and AND will not have both a left and right branch.

The second question is how do you support a NOT operator in a PEGJS grammar?

Thank you very much for your time!

Edit: Fixed the AST representation

Sarus
  • 3,303
  • 1
  • 23
  • 27
  • The second statement example is parsed incorrectly (outer operator should be `&&`, because of the brackets). – hon2a Dec 14 '14 at 19:41
  • @hon2a which one exactly is wrong? They look fine to me. – Pointy Dec 14 '14 at 19:44
  • You're showing AST for `item1 && (item2 || item3)` instead of `(item1 && item2) || item3`. – hon2a Dec 14 '14 at 19:46
  • I haven't used pegjs specifically, but it looks like you simply need rule `"!" ws* inner:primary { return {type: "NOT", inner: inner}; }` for `primary`. BTW, your grammar doesn't tolerate whitespace on endings and in parentheses - you might want to change it. – zch Dec 14 '14 at 19:55
  • @hon2a Thank you for pointing that out. I think the grammar works correctly. I just pasted the wrong AST in there for the example. Updated it. – Sarus Dec 14 '14 at 20:35

1 Answers1

3

You just need to introduce a layer between "AND" and "primary":

logical_and
  = left:factor ws+ "&&" ws+ right:logical_and { return {type: "AND", left:left, right:right} }
  / factor

factor
  = "!" ws* operand:factor { return {type: "NOT", operand: operand } }
  / primary

Since ! is a unary operator, "left" and "right" don't really make sense; I used "operand".

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Actually, you don't need or want this layer: `!!expr` is a valid expression. It's enough to add a rule to `primary`. – zch Dec 14 '14 at 19:46
  • Maybe you mean `"!" operand:factor`. I haven't tested it. – soktinpk Dec 14 '14 at 19:49
  • @zsh yes it could all be done in "primary", but personally I like adding non-terminals (do you still call them that in PEG?) perhaps because I'm not properly "lazy" (in the good way) when doing parsers. – Pointy Dec 14 '14 at 19:52
  • Since !! is the equivalent of no operator at all, do you try to simplify that in the grammar (i.e., in the lexer/parser) or do you typically let the interpreter work it out? – Sarus Dec 14 '14 at 20:46
  • @Sarus I wouldn't do that in the grammar directly, but maybe some people would. Seems like it'd be simpler to work on the AST by doing structural transformations. – Pointy Dec 14 '14 at 20:50
  • @pointy This works well and once you see the grammar it seems so simple and yet is still hard for me to wrap my head around without an example haha. Thank you very much for your assistance. – Sarus Dec 14 '14 at 20:56