Sono un novizio ANTLR4 completo, quindi per favore perdona la mia ignoranza. Mi sono imbattuto in this presentation dove è definita una grammatica di espressioni aritmetiche molto semplice. Sembra che:Modello visitatore ANTLR4 su semplice esempio aritmetico
grammar Expressions;
start : expr ;
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
INT : ('0'..'9')+ ;
WS : [ \t\r\n]+ -> skip ;
Che è grande perché genererà un semplice albero binario che può essere attraversato tramite il modello visitatore come spiegato nelle diapositive, ad esempio, ecco la funzione che visita il expr
:
public Integer visitOpExpr(OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left/right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unkown opeator " + op);
}
}
La prossima cosa che vorrei aggiungere è il supporto per le parentesi. Così ho modificato il expr
come segue:
expr : '(' expr ')' #opExpr
| left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
Sfortunatamente, il codice precedente non riesce perché quando incontrano parentesi i tre attributi op
, left
e right
sono nulli (non riesce con NPE).
Penso che potrei ovviare a ciò definendo un nuovo attributo, ad esempio parenthesized='(' expr ')'
, e poi gestirlo nel codice visitatore. Tuttavia, mi sembra eccessivo avere un intero tipo di nodo in più per rappresentare un'espressione tra parentesi. Una soluzione più semplice, ma più brutto è quello di aggiungere la seguente riga di codice all'inizio del metodo visitOpExpr
:
if (ctx.op == null) return visit(ctx.getChild(1)); // 0 and 2 are the parentheses!
Non mi piace quanto sopra a tutti perché è molto fragile e fortemente dipendente dalla struttura grammaticale.
Mi chiedo se c'è un modo per dire a ANTLR di "mangiare" semplicemente le parentesi e trattare l'espressione come un bambino. È lì? C'è un modo migliore per farlo?
Nota: Il mio obiettivo finale è quello di estendere l'esempio per includere le espressioni booleane che si possono contenere espressioni aritmetiche, ad esempio, (2+4*3)/10 >= 11
, cioè, un rapporto (<,>, ==, ~ =, ecc.) tra le espressioni aritmetiche è possibile definire un'espressione booleana atomica. Questo è dritto in avanti e ho già la grammatica abbozzato ma ho lo stesso problema con la parentesi, vale a dire, ho bisogno di essere in grado di scrivere cose del genere (Vorrei anche aggiungere il supporto per le variabili):
((2+4*x)/10 >= 11) | (x>1 & x<3)
EDIT: corretta la precedenza dell'espressione parentesi, le parentesi hanno sempre precedenza più alta.
Vedo. Quindi immagino che non ci sia modo di fare in modo che ANTLR si sbarazzi del nodo intermedio? In linea di principio c'è anche una questione di efficienza spaziale, qui non c'è? –
Non è del tutto sicuro se so cosa intendi, ma non c'è modo di gestire l'alternativa tra parentesi: ANTLR è solo lì per l'analisi, tutte le altre logiche/valutazioni devono essere gestite esplicitamente da te [in una classe visitatore/ascoltatore]. –
Voglio dire se scrivi questo: '((((((((((2 + 3))))))))))', invece di '2 + 3', la sintassi è ovviamente valida, ma il lo spazio preso dall'albero è molto più grande a causa di tutti i nodi delle parentesi. Sono solo sorpreso che non ci sia un modo per definire un cortocircuito in modo che l'espressione precedente venga convertita in quest'ultimo. –