ANTLR 4中while循环的访问者/监听器代码

时间:2014-04-16 03:21:26

标签: java antlr antlr4

到目前为止,我已经搜索了整个" The Definitive ANTLR 4 Reference"预订以及许多网站以回答我的问题,我仍然无法找到关于该主题的任何内容。

我正在使用ANTLR 4创建一个C语言的子集,它执行一些基本功能。我不知道的是如何在我的Visitor类中实现一个简单的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

这样我可以在while循环中有多个语句。我的访客类看起来像这样:

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循环的语法之外,没有提到如何实现任何访客/听众或Terence Parr撰写的书中简单的While循环的动作。有人可以帮我写一个While循环的访客/监听器Java代码吗?

1 个答案:

答案 0 :(得分:11)

  

我发现很难相信,除了while循环的语法之外,没有提到如何在Terence Parr编写的书中为一个简单的While循环实现任何访问​​者/监听器或动作。

这是因为ANTLR引用是关于ANTLR,它是关于解析的,而不是关于解析后的阶段。

你不能这样做:

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

您已经宣布您的访问者返回Integer,因此这是每条规则应返回的内容。要么创建一个自定义包装器Value来封装你的语言的值(数字,字符串,布尔值),或者只是在关系表达式为false时返回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语句时上面的代码会失败,因为内部while会返回0,导致外部while停止循环。在这种情况下,您最好创建一个自定义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