【问题标题】:Visitor/Listener code for a while loop in ANTLR 4ANTLR 4中while循环的访问者/听众代码
【发布时间】:2014-05-30 16:12:04
【问题描述】:

到目前为止,我已经搜索了整本“The Definitive ANTLR 4 Reference”一书以及许多网站来寻找我的问题的答案,但我仍然找不到关于该主题的任何内容。

我正在使用 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

这样我可以在一个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 代码吗?

【问题讨论】:

  • 您好,这是来自“语言实现模式”的更多内容:amazon.com/…
  • @TheANTLRGuy 我有那本书,快速浏览了一下,看看我是否能找到如何实现 while 循环,遗憾的是,它没有。
  • 您是否看过称为基于树的解释器的模式?第 230 页。我认为它并没有专门执行 while 循环,但它确实返回了 Colin,这要复杂得多。它清楚地显示了如何在树中移动虚拟程序计数器。

标签: java antlr antlr4


【解决方案1】:

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

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

你不能这样做:

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

您已经声明您的访问者返回Integers,所以这就是每条规则应该返回的内容。要么制作一个自定义包装器 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 和 ifwhile 语句以及自定义 Value 对象的演示语言:https://github.com/bkiers/Mu

【讨论】:

  • 非常感谢巴特。您的解决方案创造了奇迹!不过有一个问题——我试图弄清楚在访问者方法中正在做什么,这些方法使所有这些工作。以您对 if 语句的实现为例:您正在做的是将所有条件 + 它们的代码块收集在一个列表中。然后检查哪些条件为真,以便您可以访问与该特定条件相对应的语句块 - 这就是您合并条件 + 语句块的原因,以便轻松找到应该运行的块。我说的对吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 2022-01-05
  • 2014-01-22
相关资源
最近更新 更多