in reply to Re^2: Order of Precedence in Parse::RecDescent grammar
in thread Order of Precedence in Parse::RecDescent grammar

That's another reason why it's simpler to have just binary operations at each level (instead of a longer sequence of same-precedence operations). ;)

Consider the generic outline:

lower_prec_expr : higher_prec_expr lower_prec_op lower_prec_expr | higher_prec expr
The problem is that you want your parse to give different semantic values for the different branches. If you take the 2nd branch, it's just a "pass-through" of the semantic value and you don't want to construct a new arrayref around it. If you take the first branch where there is actually an operation, you need to construct an arrayref with the appropriate things.

You've combined both of these branches into one production in the grammar like this:

lower_prec_expr : higher_prec_expr (lower_prec_op lower_prec_expr)(s?) { [ "put stuff in arrayref" ] }
So now you're putting things inside an arrayref even in the "pass-through" case, where there is no operator at this point in the parse.

To fix this, put a conditional in your semantic rule to distinguish these cases, i.e:

lower_prec_expr : higher_prec_expr (lower_prec_op lower_prec_expr)(s?) { @{$item[2]} ? [ ... ] : $item[1] }
That's kinda nasty. Alternatively, have two different productions explicitly in the grammar:
lower_prec_expr : higher_prec_expr (lower_prec_op lower_prec_expr)(s) { [ ... ] } | higher_prec_expr
I.e, if there *are* operators here (no question mark on the (s) quantifier), we build an arrayref for the semantic value with the first branch. Otherwise, the second branch has no semantic rule, so it's just a "pass-through" of the semantic value and does not add layers of arrayref.

The latter is probably the best choice. Just change the (s?)'s to (s)'s and add the other alternation to the grammar.

blokhead