What does Grammar Kit's "inner" and "left" rule modifiers do?

The documentation says that

  • with the left rule modifier, the "PSI node for this rule will enclose the one to the left."
  • with the inner rule modifier, the "PSI node for this rule will be injected into the one to the left," and

As I understand it, PSI nodes form a tree from ranges that define higher elements and subranges of those that define lower elements. So, like this:


Nodes    (WHILE_LOOP..........................................)
                   (BOOL_EXPR............)    (LOOP_BODY......)
                   (EXPR)     (EXPR......)    (STATEMENT......)
                              (ADD_EXPR..)     
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30


Text     "while (x < y + 1) doSomething()"



This raises several questions:


What does "the one to the left" mean in this context?


Let's say I apply the left rule modifier to LOOP_BODY. I see five possible "left" nodes:
  • Whitespace token at 17
  • Close-parentheses token at 16
  • ADD_EXPR node at 11–15
  • EXPR node at 11–15
  • BOOL_EXPR node at 7–15



What does it mean to "enclose"?


Let's assume that the rule is enclosing the last possibility above, BOOL_EXPR. Does that mean the tree will actually end up like this, with the underlined change?


Nodes    (WHILE_LOOP..........................................)
                   (LOOP_BODY.................................)
                   (BOOL_EXPR............)    (STATEMENT......)
                   (EXPR)     (EXPR......)
                              (ADD_EXPR..)     
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30


Would the close-parentheses token then become a child of LOOP_BODY instead of WHILE_LOOP?


What does it mean to "inject"?


Is this a class hierarchy, inheritance, or composition thing, or is inner just the opposite of left, so an inner BOOL_EXPR would result in a tree like this?


Nodes    (WHILE_LOOP..........................................)
                   (BOOL_EXPR.................................)
                   (EXPR)     (EXPR......)    (LOOP_BODY......)
                   (EXPR)     (ADD_EXPR..)    (STATEMENT......)
Tokens    while _ ( x    _ < _ y  _ + _ 1  ) _ doSomething ( ) EOF
Char Idx  0.........7....8.....11.......15.....18............. 30


And if that is the case, does left inner combine with both the left and the right nodes? And do they chain?

1 comment

The one to the left is just a synonym for PsiBuilder.getLatestDoneMarker().
In terms of BNF this refers (usually) to any non-private-rule-to-the-left in a sequence.

Consider after parsing some my_rule you have curMarker to finish & latestMarker obtained at the beginning as psiBuilder.getLatestDoneMarker();

1. left my_rule completion sequence:
curMarker.drop()
latestMarker.precede().done(my_rule_type);

2. left inner my_rule:
curMarker.done(my_rule_type);
latestMarker.precede().done(latestMarker.type)
latestMarker.drop();

3. private left = private left inner:
curMarker.drop()
latestMarker.precede().done(latestMarker.type)
latestMarker.drop();

Code references:
Store leftMarker on start: https://github.com/JetBrains/Grammar-Kit/blob/master/support/org/intellij/grammar/parser/GeneratedParserUtilBase.java?source=c#L347
Finish leftRule processing: https://github.com/JetBrains/Grammar-Kit/blob/master/support/org/intellij/grammar/parser/GeneratedParserUtilBase.java?source=c#L484



So the answers to your questions will be:
1. BOOL_EXPR node at 7–15
2. Sure. That's why left rules need special care.
3. The word 'inject' here is used to describe AST manipulations. Inner BOOL_EXPR in this context will lead to unpredictable errors.
    A better example would be inner LOOP_BODY, which would become a child of BOOL_EXPR as well as the closing parethesis.

To summarize: For stable results left rules should appear directly after some public rule with no pins applied.
Generally speaking the notion of left rules help make BNF more compact when describing AST just like the main goal of EBNF is to make BNF shorter.

You may copy-paste the example from https://github.com/JetBrains/Grammar-Kit/blob/master/TUTORIAL.md and play with all those modifiers in Live Preview mode.
As you will see the sample with left rules is a bit simpler than standard arithmetic BNF.

1

Please sign in to leave a comment.