How do you build an AST (Abstract Syntax Tree) for left-associative operators using PEG.js?
I've tried to write some code based on the information I found on the internet, but I seem to have made a mistake.
The code I wrote generates an incorrect AST for most expressions.
Expression
12-6-4-2*1-1
Expected AST
{
"left": {
"left": {
"left": {
"left": 12,
"operator": "-",
"right": 6
},
"operator": "-",
"right": 4
},
"operator": "-",
"right": {
"left": 2,
"operator": "*",
"right": 1
}
},
"operator": "-",
"right": 1
}
Generated AST
{
"left": {
"left": {
"left": 12,
"operator": "-",
"right": 6
},
"operator": "-",
"right": 4
},
"operator": "-",
"right": {
"left": 2,
"operator": "*",
"right": {
"left": 1,
"operator": "-",
"right": 1
}
}
}
Code
{
function operator(first, rest) {
if (rest.length === 0) return first;
return { left: first, right: rest };
};
function makeOperator(left, operator, right) {
return { left: left, operator: operator[0], right: clean(right[1]) };
};
function clean(expression) {
if (!expression.right) return expression;
var result = makeOperator(expression.left, expression.right[0], expression.right[0]);
for (var counter = 1, len = expression.right.length; counter < len; counter++) {
result = makeOperator(result, expression.right[counter], expression.right[counter]);
}
return result;
};
}
Start = E
E
= expression:E1
{ return clean(expression); }
E1
= expression:E2 rest:(("+" / "-") E2)*
{ return operator(expression, rest); }
E2
= expression:Value rest:(("*" / "/") E1)*
{ return operator(expression, rest); }
Value
= Number
/ BracketedExpression
Number
= [1-9][0-9]*
{ return parseInt(text(), 10); }
BracketedExpression
= "(" expression:E1 ")"
{ return expression; }
I would really appreciate any help or example code on how to build ASTs for both left-associative and right-associative operators.
Edit: As @Bergi pointed out, the problem was that E2
used E1
as the expression for the rest of the operator list instead of Value
. However, the code that Bergi wrote is much simpler than mine.
clean
is supposed to do? – Dozyclean
takes an expression array and transform it into an AST. If you change it to justreturn expression;
, you will see what the expression is. – Gerdyclean
as a recursive post-processing transformation, you'd better call it fromoperator()
and not frommakeOperator()
(and omit theE
step entirely). That did confuse me a bit. – Dozy