title: Irony: How to make BNF Grammar Shift-Reduce Parsing for expressions, but with flat list levels for operators of the same priority? (picture attached)
body: <p>I'm trying to create a Grammar in Irony.NET (code attached below), which would parse simple expressions like this one:</p>
  <p><code>&quot;a + b * c - d&quot;</code> , but in a more flat tree format, <strong>where operators of the same priority are grouped (with their arguments) under one node</strong>:</p>
  <p><a href="https://i.stack.imgur.com/2Z5xI.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/2Z5xI.png" alt="Desired Ast Tree format vs. undesired" /></a></p>
  <p>I've tried many options (including adding things like <code>PreferShiftHere()</code> to many places in the rules) and I'm always getting either a Shift-Reduce Conflict or a parsing syntax error. What am I missing?</p>
  <p><strong>To clarify, the requirements are that:</strong></p>
  <p>Every {sum} node should:</p>
  <ul>
  <li>be flat (do not contain another {sum} in its children, but all arguments and {op1} operators under one node)</li>
  <li>have at least 2 arguments and one {op1} operator between them (not be a single argument)</li>
  <li>be able to contain {mul} as arguments (i.e. higher priority operator expressions, the {v2})</li>
  </ul>
  <p>Every {mul} node should:</p>
  <ul>
  <li>be flat (as {sum}, but use {op2} operators)</li>
  <li>have at least 2 arguments (as the {sum})</li>
  </ul>
  <p><strong>The code I have for testing this:</strong>
  (just run the static <code>TestGrammar.Test();</code> method)</p>
  <pre><code>public class TestGrammar : Grammar
  {
      public TestGrammar()
          : base(false)
      {
          // 1. Terminals
          RegexBasedTerminal a = new RegexBasedTerminal(&quot;a&quot;, &quot;a&quot;);
          RegexBasedTerminal b = new RegexBasedTerminal(&quot;b&quot;, &quot;b&quot;);
          RegexBasedTerminal c = new RegexBasedTerminal(&quot;c&quot;, &quot;c&quot;);
          RegexBasedTerminal d = new RegexBasedTerminal(&quot;d&quot;, &quot;d&quot;);
          RegexBasedTerminal e = new RegexBasedTerminal(&quot;e&quot;, &quot;e&quot;);
          RegexBasedTerminal f = new RegexBasedTerminal(&quot;f&quot;, &quot;f&quot;);
  
          // 2. Non-terminals
          var expr = new NonTerminal(&quot;expr&quot;);
          var sum = new NonTerminal(&quot;sum&quot;);
          var mul = new NonTerminal(&quot;mul&quot;);
          var v = new NonTerminal(&quot;var&quot;);
          var v2 = new NonTerminal(&quot;var/mul&quot;);
          var op1 = new NonTerminal(&quot;op1&quot;);
          var op2 = new NonTerminal(&quot;op2&quot;);
  
          // 3. BNF rules
          expr.Rule = sum;
          sum.Rule = v2 + MakePlusRule(sum, op1 + v2);
          mul.Rule = v + MakePlusRule(mul, op2 + v);
          v.Rule = a | b | c | d | e | f;
          v2.Rule = v | mul;
          op1.Rule = ToTerm(&quot;+&quot;) | &quot;-&quot;;
          op2.Rule = ToTerm(&quot;*&quot;) | &quot;/&quot;;
  
          this.Root = expr;       // Set grammar root
  
          // 4. Operators precedence
          RegisterOperators(10, op1);
          RegisterOperators(20, op2);
      }
  
  
      public static void Test()
      {
          var grammar = new TestGrammar();
          var language = new LanguageData(grammar);
  
          if (language.Errors.Any(e =&gt; e.Level &gt; GrammarErrorLevel.Warning))
          {
              MessageBox.Show(string.Join(&quot;\r\n\r\n&quot;, language.Errors.Select(e =&gt; $&quot;{e.Level}: {e.Message}&quot;)), &quot;Grammar error&quot;, MessageBoxButton.OK, MessageBoxImage.Error);
          }
  
          var parser = new Parser(language);
          parser.Context.TracingEnabled = true;
          ParseTree parseTree = parser.Parse(&quot;a + b * c - d&quot;);
          var debug = string.Join(&quot;\r\n&quot;, parser.Context.ParserTrace.Select(t =&gt; $&quot;{(t.IsError ? &quot;ERROR &quot; : &quot;&quot;)}{t.State}: {t.Input} -&gt; {t.Message}&quot;));
          if (parseTree.Root != null)
              MessageBox.Show(DisplayTree(parseTree.Root));
          MessageBox.Show(debug);
      }
  
      private static string DisplayTree(ParseTreeNode node, int level = 1)
      {
          string result = &quot;&quot;;
          for (int i = 0; i &lt; level; i++)
              result += &quot;    &quot;;
  
          result += node;
  
          foreach (ParseTreeNode child in node.ChildNodes)
              result += &quot;\r\n&quot; + DisplayTree(child, level + 1);
  
          return result;
      }
  }
  </code></pre>
  <p>(This code requires only Irony and Irony.Interpreter nugets.)</p>
  <p><strong>The trace I got for the example above is this below.</strong> It looks like when it reaches the &quot;v2,op1,v2&quot; stack state, it immediately reduces this to &quot;sum&quot; and then it sees op1 (the '-' sign) and doesn't attach it to the sum node because it is already reduced. When I had it without the mul node (<code>sum.Rule = v + MakePlusRule(sum, op1 + v);</code>) everything was working perfectly, it was accepting all 3 or 4 variables and operators between them under one &quot;sum&quot; node), but with this &quot;v2&quot; and &quot;mul&quot; added, it just stopped working. I don't understand how the States get created in Irony, so I could really use some help for this. Thanks!</p>
  <pre><code>S0: a-&gt;Shift to S5
  S5:  -&gt; Reduce on 'var -&gt; a '
  S0: var-&gt;Popped state from stack, pushing var
  S4: +(Key symbol) -&gt; Reduce on 'var/mul -&gt; var '
  S0: var / mul-&gt;Popped state from stack, pushing var/mul
  S3: +(Key symbol) -&gt; Shift to S16
  S16:  -&gt; Reduce on 'op1 -&gt; + '
  S3: op1-&gt;Popped state from stack, pushing op1
  S15: b-&gt;Shift to S6
  S6:  -&gt; Reduce on 'var -&gt; b '
  S15: var-&gt;Popped state from stack, pushing var
  S4: *(Key symbol) -&gt; Shift to S22
  S22:  -&gt; Reduce on 'op2 -&gt; * '
  S4: op2-&gt;Popped state from stack, pushing op2
  S21: c-&gt;Shift to S7
  S7:  -&gt; Reduce on 'var -&gt; c '
  S21: var-&gt;Popped state from stack, pushing var
  S29:  -&gt; Reduce on 'Unnamed0 -&gt; op2 var '
  S4: Unnamed0-&gt;Popped state from stack, pushing Unnamed0
  S18:  -&gt; Reduce on 'mul -&gt; var Unnamed0 '
  S15: mul-&gt;Popped state from stack, pushing mul
  S11:  -&gt; Reduce on 'var/mul -&gt; mul '
  S15: var / mul-&gt;Popped state from stack, pushing var/mul
  S26:  -&gt; Reduce on 'Unnamed2 -&gt; op1 var/mul '
  S3: Unnamed2-&gt;Popped state from stack, pushing Unnamed2
  S13:  -&gt; Reduce on 'sum -&gt; var/mul Unnamed2 '
  S0: sum-&gt;Popped state from stack, pushing sum
  S2:  -&gt; Reduce on 'expr -&gt; sum '
  S0: expr-&gt;Popped state from stack, pushing expr
  ERROR S1: -(Key symbol) -&gt; Syntax error, unexpected input.
  S1: -(Key symbol) -&gt; RECOVERING: popping stack, looking for state with error shift
  S0: -(Key symbol) -&gt;FAILED TO RECOVER
  </code></pre>