В ANTLR 3 вы можете просто сделать следующее:
andExpression
: (andnotExpression -> andnotExpression)
(AND? a=andnotExpression -> ^(AndNode $andExpression $a))*
;
Любая идея, как это сделать в новой версии?
В ANTLR 3 вы можете просто сделать следующее:
andExpression
: (andnotExpression -> andnotExpression)
(AND? a=andnotExpression -> ^(AndNode $andExpression $a))*
;
Любая идея, как это сделать в новой версии?
Как упоминалось Сэмом (280Z28), ANTLR 4 не имеет операторов перезаписи.
При создании парсера ANTLR 4 создает несколько классов прослушивателя, которые вы можете использовать для прослушивания событий "enter" и "exit" всех правил анализатора.
Кроме того, ANTLR 4 поддерживает "прямые левые рекурсивные правила", поэтому ваши правила выражения могут быть определены в одном правиле, как показано ниже:
grammar Expr;
parse
: expression EOF
;
expression
: '(' expression ')'
| IDENTIFIER
| NOT expression
| expression AND? expression
| expression OR expression
;
LPAREN : '(';
RPAREN : ')';
NOT : 'NOT';
AND : 'AND';
OR : 'OR';
IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]*;
SPACE : [ \t\r\n]+ -> skip;
При анализе ввода "a b OR NOT c AND d"
будет создано следующее дерево разбора:
(изображение создано с помощью ANTLRWorks2, спасибо, Сэм! Очень впечатляющая IDE, мне это нравится!)
Создайте классы анализатора и слушателя:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Expr.g4
и создается следующий класс, который позволит вам "ходить" по дереву:
public class ExprBaseListener implements ExprListener {
@Override public void enterExpression(ExprParser.ExpressionContext ctx) { }
@Override public void exitExpression(ExprParser.ExpressionContext ctx) { }
@Override public void enterParse(ExprParser.ParseContext ctx) { }
@Override public void exitParse(ExprParser.ParseContext ctx) { }
@Override public void enterEveryRule(ParserRuleContext<Token> ctx) { }
@Override public void exitEveryRule(ParserRuleContext<Token> ctx) { }
@Override public void visitTerminal(TerminalNode<Token> node) { }
@Override public void visitErrorNode(ErrorNode<Token> node) { }
}
Теперь вам нужно будет проверить ExprParser.ExpressionContext
, чтобы узнать, какая из альтернатив в expression
соответствует, где "древовидные метки" пригождаются. Измените правило expression
следующим образом:
expression
: '(' expression ')' # EXPR
| IDENTIFIER # ID_EXPR
| 'NOT' expression # NOT_EXPR
| expression 'AND'? expression # AND_EXPR
| expression 'OR' expression # OR_EXPR
;
и обновите синтаксический анализатор и слушатели, и вы увидите, что ExprBaseListener
теперь выглядит следующим образом:
public class ExprBaseListener implements ExprListener {
@Override public void enterAND_EXPR(ExprParser.AND_EXPRContext ctx) { }
@Override public void exitAND_EXPR(ExprParser.AND_EXPRContext ctx) { }
@Override public void enterOR_EXPR(ExprParser.OR_EXPRContext ctx) { }
@Override public void exitOR_EXPR(ExprParser.OR_EXPRContext ctx) { }
@Override public void enterEXPR(ExprParser.EXPRContext ctx) { }
@Override public void exitEXPR(ExprParser.EXPRContext ctx) { }
@Override public void enterNOT_EXPR(ExprParser.NOT_EXPRContext ctx) { }
@Override public void exitNOT_EXPR(ExprParser.NOT_EXPRContext ctx) { }
@Override public void enterID_EXPR(ExprParser.ID_EXPRContext ctx) { }
@Override public void exitID_EXPR(ExprParser.ID_EXPRContext ctx) { }
@Override public void enterParse(ExprParser.ParseContext ctx) { }
@Override public void exitParse(ExprParser.ParseContext ctx) { }
@Override public void enterEveryRule(ParserRuleContext ctx) { }
@Override public void exitEveryRule(ParserRuleContext ctx) { }
@Override public void visitTerminal(TerminalNode node) { }
@Override public void visitErrorNode(ErrorNode node) { }
}
I.e., для каждой метки в expression
создается отдельный метод ввода и вывода.
Теперь скажем, что вас интересуют только события enter из выражения AND
. Вы можете создать собственный класс, который расширяет этот ExprBaseListener
и переопределяет enterAND_EXPR
:
public class ExprWalker extends ExprBaseListener {
@Override
public void enterAND_EXPR(ExprParser.AND_EXPRContext ctx) {
java.util.List<ExprParser.ExpressionContext> e = ctx.expression();
System.out.println("AND -> " + e.get(0).getText() + ", " + e.get(1).getText());
}
}
Чтобы проверить это все, создайте небольшой класс драйвера:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
public class Main {
public static void main(String[] args) throws Exception {
String input = "a b OR NOT c AND d";
ExprLexer lexer = new ExprLexer(new ANTLRInputStream(input));
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
ParseTree tree = parser.parse();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(new ExprWalker(), tree);
}
}
и запустите его:
java -cp antlr-4.0-complete.jar org.antlr.v4.Tool Expr.g4 javac -cp antlr-4.0-complete.jar *.java java -cp .:antlr-4.0-complete.jar Main
после чего на консоль вы увидите следующее:
AND -> a, bORNOTcANDd AND -> NOTc, d
ANTLR 4 не имеет оператора перезаписи или параметра output=AST
, такого как ANTLR 3. Деревья, созданные анализаторами ANTLR 4, представляют собой деревья синтаксического разбора, форма которых неявно определяется правилами грамматики.