Подтвердить что ты не робот

ANTLR 4 оператор ввода/перезаписи дерева ANTLR

В ANTLR 3 вы можете просто сделать следующее:

    andExpression
  :  (andnotExpression        -> andnotExpression)
     (AND? a=andnotExpression -> ^(AndNode $andExpression $a))* 
  ;

Любая идея, как это сделать в новой версии?

4b9b3361

Ответ 1

Как упоминалось Сэмом (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" будет создано следующее дерево разбора:

enter image description here

(изображение создано с помощью 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

Ответ 2

ANTLR 4 не имеет оператора перезаписи или параметра output=AST, такого как ANTLR 3. Деревья, созданные анализаторами ANTLR 4, представляют собой деревья синтаксического разбора, форма которых неявно определяется правилами грамматики.