Parsing a language that describes addition with integers, variables, and optional parentheses to any depth. Variables can be chained using a dot notation.
// Example 1
1 + (2 + ((3 + 4))) + a + 5 + b.c
// Example 2
a.(b+c) => Chain(Var, Add(Var, Var))
// Example 3
(a+b).c => Chain(Add(Var, Var), Var) // Desired tree
(a+b).c => Add(Var, Var)⚠(".")⚠(Var) // Actual tree
The following grammar works for all permutations I’ve come up with except for Example 3 above.
@precedence { boost @left }
@top Program { expr }
// Chain and Var in expr are competing with Chain and Var in chainOperand
expr { maybeTree<!boost Chain | !boost Var | Int> }
Add<t> { maybeGrouped<t !boost plus t> }
Chain { maybeGrouped<chainOperand !boost dot chainOperand> }
chainOperand { maybeTree<Chain | Var> }
maybeGrouped<t> {
(!boost open maybeGrouped<t> !boost close) | t
}
maybeTree<t> {
Add<maybeTree<t>> | maybeGrouped<t>
}
@tokens {
Int { std.digit+ }
Var { std.asciiLetter+ }
plus { '+' }
dot { '.' }
ws { std.whitespace+ }
open { '(' }
close { ')' }
}
@skip { ws }
Moving the precedence !boost for Chain and Var from expr to chainOperand fixes the parse issue in Example 3 but causes many other permutations to fail, e.g. a + b => Chain(Add(Var,Var),⚠(""))
.
Omitting !boost in expr and chainOperand creates a reduce/reduce error.
Any advice on how to resolve this precedence problem or correctly describe this language a different way?