Код посетителя / слушателя для цикла while в ANTLR 4

до сих пор я искал всю "окончательную справочную книгу ANTLR 4", а также многие сайты для ответа на мой вопрос, и я до сих пор не могу найти ничего по этой теме.

Я использую ANTLR 4 для создания подмножества языка C, который выполняет некоторые основные функции. Я понятия не имею, как реализовать простой цикл while в моем классе посетителей. До сих пор я получил это в своей грамматике:

grammar Expr;

prog:   stat+ ;

stat:   expr NEWLINE                # printExpr
    |   ID '=' expr NEWLINE         # assign
    |   loop NEWLINE                # whileLoop
    |   relational NEWLINE          # relat
    |   NEWLINE                     # blank 
    ;

expr:   expr op=('*'|'/') expr      # MulDiv
    |   expr op=('+'|'-') expr      # AddSub
    |   INT                         # int
    |   ID                          # id
    |   '(' expr ')'                # parens
    ;

relational:     expr op=(GREATER|LESS) expr     # GreaterEqual
          ;

loop:   'while' '('relational')' NEWLINE? '{'stat*'}'   #while
    ;

GREATER : '>' ;
LESS : '<' ;
MUL :   '*' ; // assigns token name to '*' used above in grammar
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
ID  :   [a-zA-Z]+ ;      // match identifiers
INT :   [0-9]+ ;         // match integers
NEWLINE:'r'? 'n' ;     // return newlines to parser (is end-statement signal)
WS  :   [ t]+ -> skip ; // toss out whitespace

таким образом я могу иметь несколько операторов внутри некоторое время петля. Мой класс посетителей выглядит так:

public class EvalVisitor extends ExprBaseVisitor<Integer> {

/** "memory" for our calculator; variable/value pairs go here */
Map<String, Integer> memory;

public EvalVisitor() {
    memory = new HashMap<String, Integer>();
}

/** ID '=' expr NEWLINE */
@Override
public Integer visitAssign(ExprParser.AssignContext ctx) {      
    String id = ctx.ID().getText();  // id is left-hand side of '='
    int value = super.visit(ctx.expr());   // compute value of expression on right        
    memory.put(id, value);           // store it in our memory
    return value;
}

/** expr NEWLINE */
@Override
public Integer visitPrintExpr(ExprParser.PrintExprContext ctx) {
    Integer value = super.visit(ctx.expr()); // evaluate the expr child
    System.out.println(value);         // print the result
    return 0;                          // return dummy value
}

/** INT */
@Override
public Integer visitInt(ExprParser.IntContext ctx) {
    return Integer.valueOf(ctx.INT().getText());
}

/** ID */
@Override
public Integer visitId(ExprParser.IdContext ctx) {
    String id = ctx.ID().getText();
    if ( memory.containsKey(id) ) return memory.get(id);        
    return 0;
}

/** expr op=('*'|'/') expr */
@Override
public Integer visitMulDiv(ExprParser.MulDivContext ctx) {
    int left = super.visit(ctx.expr(0));  // get value of left subexpression
    int right = super.visit(ctx.expr(1)); // get value of right subexpression
    if ( ctx.op.getType() == ExprParser.MUL ) return left * right;
    return left / right; // must be DIV
}

/** expr op=('+'|'-') expr */
@Override
public Integer visitAddSub(ExprParser.AddSubContext ctx) {
    int left = super.visit(ctx.expr(0));  // get value of left subexpression
    int right = super.visit(ctx.expr(1)); // get value of right subexpression        
    if ( ctx.op.getType() == ExprParser.ADD ) return left + right;
    return left - right; // must be SUB

}

/** '(' expr ')' */
@Override
public Integer visitParens(ExprParser.ParensContext ctx) {
    return super.visit(ctx.expr()); // return child expr's value
}

@Override
public boolean visitGreaterEqual(GreaterEqualContext ctx) {
    int left = super.visit(ctx.expr(0));
    int right = super.visit(ctx.expr(1));

    if(ctx.op.getType() == ExprParser.GREATER) {
        return left > right;
    }
    else {
        return left < right;
    }

}

@Override
public Integer visitWhileLoop(WhileLoopContext ctx) {

    if(visit(ctx.getRuleContext())) {

    }

    return super.visitWhileLoop(ctx);
}

}

большая часть кода в классе посетителей я взял из книги, потому что я только начинаю использовать ANTLR 4. Мне трудно поверить, что, кроме грамматики цикла while, нет упоминания о том, как реализовать каких-либо посетителей/слушателей или действий для простого цикла While в книге, написанной Теренсом Парром. Может ли кто-нибудь помочь мне написать Java-код посетителя/слушателя на некоторое время?

1 ответов


мне трудно поверить, что, кроме грамматики для цикла while, нет упоминания о том, как реализовать каких-либо посетителей/слушателей или действий для простого цикла While в книге, написанной Теренсом Парром.

это потому, что ссылка ANTLR касается ANTLR, которая касается синтаксического анализа, а не стадии после парсинг.

вы не можете сделать это:

@Override
public boolean visitGreaterEqual(GreaterEqualContext ctx) {
    ...

вы уже объявлено, что ваш посетитель возвращается Integers, так что каждое правило должно возвращаться. Либо сделайте пользовательскую обертку Value, который инкапсулирует значения вашего языка (числа, строки, логические значения), или просто вернуть 0 когда выражение отношения ложно:

@Override
public Integer visitGreaterEqual(ExprParser.GreaterEqualContext ctx) {
    int left = this.visit(ctx.expr(0));
    int right = this.visit(ctx.expr(1));

    if (ctx.op.getType() == ExprParser.GREATER) {
        return left > right ? 1 : 0; // 0 is false (all other values are true)
    }
    else {
        return left < right ? 1 : 0;
    }
}

затем вы можете написать свой while следующим образом:

@Override
public Integer visitWhile(ExprParser.WhileContext ctx) {

    // Evaluate the relational expression and continue the while
    // loop as long as it is true (does not equal zero).
    while (this.visit(ctx.relational()) != 0) {

        // Evaluate all statements inside the while loop.
        for (ExprParser.StatContext stat : ctx.stat()) {
            this.visit(stat);
        }
    }

    // 0 now also is false, so maybe return null instead which would be
    // some sort of VOID value (or make a proper Value class).
    return 0;
}

обратите внимание, что приведенный выше код завершится ошибкой при вложении while заявления, потому что внутреннее время вернется 0 причинять наружное пока к хватит петлять. В таких случаях вам лучше создать пользовательский Value класс и ввести какой-то Value.VOID экземпляр, который не приведет к остановке цикла.

запуск следующего основного метода:

public static void main(String[] args) throws Exception {

    String expression = "n = 1\n" +
            "while (n < 10) {\n" +
            "  n\n" +
            "  n = n + 1\n" +
            "}\n";
    ExprLexer lexer = new ExprLexer(new ANTLRInputStream(expression));
    ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
    new EvalVisitor().visit(parser.prog());
}

выведет:

1
2
3
4
5
6
7
8
9

Также посмотрите на этот демо-язык, который использует ANTLR4 и if и while заявления, а также пользовательским Value объект:https://github.com/bkiers/Mu