I have written this grammar:
expr : multExpr ( ('+' | '-') multExpr )*;
multExpr : atom ( ('*' | '/') atom )*;
atom : INT | FLOAT | ID | '(' expr ')';
condition : cond ('or' cond)*;
cond : c1 ('and' c1)*;
c1 : ('not')? c2;
c2 : '(' condition ')' | boolean;
boolean : expr (relop expr | ²) | 'true' | 'false';
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
I have omitted the lexer rules for INT,FLOAT,ID as it is obvious.
The problem is c2 rule, it is ambiguous because of '(', I could not find the solution, can you offer me a solution?
Why not simply do:
expr : orExpr;
orExpr : andExpr ('or' andExpr)*;
andExpr : relExpr ('and' relExpr)*;
relExpr : addExpr (relop addExpr)?;
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
addExpr : multExpr (('+' | '-') multExpr)*;
multExpr : unaryExpr (('*' | '/') unaryExpr)*;
unaryExpr : 'not'? atom;
atom : INT | FLOAT | ID | 'true' | 'false' | '(' expr ')';
The unary not
usually has a higher precedence than you're trying to do now.
This will allow for expressions like 42 > true
, but checking such semantics can come when you're walking the AST/tree.
EDIT
The input "not(a+b >= 2 * foo/3.14159) == false"
would now be parsed like this (ignoring spaces):
And if you set the output to AST and mix in some tree rewrite operators (^
and !
):
options {
output=AST;
}
// ...
expr : orExpr;
orExpr : andExpr ('or'^ andExpr)*;
andExpr : relExpr ('and'^ relExpr)*;
relExpr : addExpr (relop^ addExpr)?;
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
addExpr : multExpr (('+' | '-')^ multExpr)*;
multExpr : unaryExpr (('*' | '/')^ unaryExpr)*;
unaryExpr : 'not'^ atom | atom;
atom : INT | FLOAT | ID | 'true' | 'false' | '('! expr ')'!;
you'd get: