Asignación como expresión en gramática ANTLR
Pregunta
Estoy tratando de extender la gramática del pequeño idioma tratar la asignación como expresión. Por lo tanto, sería válido escribir
a = b = 1; // -> a = (b = 1)
a = 2 * (b = 1); // contrived but valid
a = 1 = 2; // invalid
La asignación difiere de otros operadores en dos aspectos. Es correcto asociativo (no es un gran problema), y su lado izquierdo tiene que ser una variable. Entonces cambié la gramática de esta manera
statement: assignmentExpr | functionCall ...;
assignmentExpr: Identifier indexes? '=' expression;
expression: assignmentExpr | condExpr;
No funciona, porque contiene una decisión no LL (*). También probé esta variante:
assignmentExpr: Identifier indexes? '=' (expression | condExpr);
Pero recibí el mismo error. Estoy interesado en
- Esta pregunta específica
- Dada una gramática con una decisión no ll (*), cómo encontrar las dos rutas que causan el problema
- Como arreglarlo
Solución
La clave aquí es que debe "asegurar" al analizador que dentro de una expresión, hay algo por delante que satisface la expresión. Esto se puede hacer usando un predicado sintáctico (el ( ... )=>
partes en el add
y mult
normas).
Una demostración rápida:
grammar TL;
options {
output=AST;
}
tokens {
ROOT;
ASSIGN;
}
parse
: stat* EOF -> ^(ROOT stat+)
;
stat
: expr ';' -> expr
;
expr
: add
;
add
: mult ((('+' | '-') mult)=> ('+' | '-')^ mult)*
;
mult
: atom ((('*' | '/') atom)=> ('*' | '/')^ atom)*
;
atom
: (Id -> Id) ('=' expr -> ^(ASSIGN Id expr))?
| Num
| '(' expr ')' -> expr
;
Comment : '//' ~('\r' | '\n')* {skip();};
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
que analizará la entrada:
a = b = 1; // -> a = (b = 1)
a = 2 * (b = 1); // contrived but valid
en el siguiente AST:
Otros consejos
Creo que puedes cambiar tu gramática de esta manera para lograr lo mismo, sin usar predicados sintácticos:
statement: Expr ';' | functionCall ';'...;
Expr: Identifier indexes? '=' Expr | condExpr ;
condExpr: .... and so on;
Alteré el ejemplo de Bart con esta idea en mente:
grammar TL;
options {
output=AST;
}
tokens {
ROOT;
}
parse
: stat+ EOF -> ^(ROOT stat+)
;
stat
: expr ';'
;
expr
: Id Assign expr -> ^(Assign Id expr)
| add
;
add
: mult (('+' | '-')^ mult)*
;
mult
: atom (('*' | '/')^ atom)*
;
atom
: Id
| Num
| '('! expr ')' !
;
Assign : '=' ;
Comment : '//' ~('\r' | '\n')* {skip();};
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
Y para la entrada:
a=b=4;
a = 2 * (b = 1);
obtienes siguiendo el árbol de análisis: