我是ANTLR4的新手,请原谅我的无知。我遇到了这个演示,其中定义了一个非常简单的算术表达式语法。看起来像:
grammar Expressions;
start : expr ;
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
INT : ('0'..'9')+ ;
WS : [ \t\r\n]+ -> skip ;
这很棒,因为它将生成一个非常简单的二进制树,可以使用幻灯片中解释的访问者模式来遍历它,例如,下面是访问的函数expr
:
public Integer visitOpExpr(OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left / right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unkown opeator " + op);
}
}
我想添加的下一件事是对括号的支持。所以我修改了expr
如下:
expr : '(' expr ')' #opExpr
| left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| atom=INT #atomExpr
;
不幸的是,上面的代码失败了,因为遇到三个属性时会加上括号op
,left
并且right
它们为null(NPE失败)。
我认为我可以通过定义一个新属性(例如)来解决此问题parenthesized='(' expr ')'
,然后在访问者代码中进行处理。但是,对于我来说,拥有一个完整的额外节点类型来用括号来表示一个表达式似乎有点矫kill过正。一个更简单但更难看的解决方案是在该visitOpExpr
方法的开头添加以下代码行:
if (ctx.op == null) return visit(ctx.getChild(1)); // 0 and 2 are the parentheses!
我一点都不喜欢上面的内容,因为它非常脆弱并且高度依赖于语法结构。
我想知道是否有一种方法可以告诉ANTLR只是“吃掉”括号并像孩子一样对待表达式。在那儿?有一个更好的方法吗?
注意 :我的最终目标是将示例扩展为包括布尔表达式,这些布尔表达式本身可以包含算术表达式,例如(2+4*3)/10 >= 11
,即算术表达式之间的关系(<,>,==,〜=等)可以定义原子布尔表达式。这很简单,我已经勾勒出语法,但是括号也有同样的问题,即我需要能够编写类似的东西(我还将添加对变量的支持):
((2+4*x)/10 >= 11) | ( x>1 & x<3 )
编辑 :固定了括号表达式的优先级,括号始终具有更高的优先级。
当然可以,只是贴上不同的标签。毕竟,替代'(' expr ')'
方法不是#opExpr
:
expr : left=expr op=('*'|'/') right=expr #opExpr
| left=expr op=('+'|'-') right=expr #opExpr
| '(' expr ')' #parenExpr
| atom=INT #atomExpr
;
在访问者中,您将执行以下操作:
public class EvalVisitor extends ExpressionsBaseVisitor<Integer> {
@Override
public Integer visitOpExpr(@NotNull ExpressionsParser.OpExprContext ctx) {
int left = visit(ctx.left);
int right = visit(ctx.right);
String op = ctx.op.getText();
switch (op.charAt(0)) {
case '*': return left * right;
case '/': return left / right;
case '+': return left + right;
case '-': return left - right;
default: throw new IllegalArgumentException("Unknown operator " + op);
}
}
@Override
public Integer visitStart(@NotNull ExpressionsParser.StartContext ctx) {
return this.visit(ctx.expr());
}
@Override
public Integer visitAtomExpr(@NotNull ExpressionsParser.AtomExprContext ctx) {
return Integer.valueOf(ctx.getText());
}
@Override
public Integer visitParenExpr(@NotNull ExpressionsParser.ParenExprContext ctx) {
return this.visit(ctx.expr());
}
public static void main(String[] args) {
String expression = "2 * (3 + 4)";
ExpressionsLexer lexer = new ExpressionsLexer(new ANTLRInputStream(expression));
ExpressionsParser parser = new ExpressionsParser(new CommonTokenStream(lexer));
ParseTree tree = parser.start();
Integer answer = new EvalVisitor().visit(tree);
System.out.printf("%s = %s\n", expression, answer);
}
}
如果运行上面的类,您将看到以下输出:
2 *(3 + 4)= 14
问题内容: 我目前正在尝试在Antlr4访问者的帮助下开发JavaScript编译器。我已经用Java实现了这一点,但无法弄清楚如何用JavaScript做到这一点。也许有人可以回答我几个问题? 1:在Java中,有一个Visitor.visit函数。如果我说得对,那么使用Javascript是不可能的。有没有解决的办法? 2:我的Javascript访问者获得了所有生成的访问函数,但是当我使用c
主要内容:介绍,实现,ComputerPart.java,Keyboard.java,Monitor.java,Mouse.java,Computer.java,ComputerPartVisitor.java,ComputerPartDisplayVisitor.java,VisitorPatternDemo.java在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行
亦称: Visitor 意图 访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。 问题 假如你的团队开发了一款能够使用巨型图像中地理信息的应用程序。 图像中的每个节点既能代表复杂实体 (例如一座城市), 也能代表更精细的对象 (例如工业区和旅游景点等)。 如果节点代表的真实对象之间存在公路, 那么这些节点就会相互连接。 在程序内部, 每个节点的类型都由其所属的类来表示, 每个特定
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。 介绍 意图:主要将数据结构与数据操作分离。 主要解决:稳定的数据结构和易变的操作耦合问题。 何时使用:需要对一个对象结构中的对象进行
简介 访问者模式是一种将算法与对象结构分离的软件设计模式。 这个模式的基本想法如下:首先我们拥有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者对象;访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素作出不同的反应;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。 介绍 意图:主要将数据结构与数据操作分离。 主要解决:稳定的数据结构和易变的操作耦合问题。 何时使用:需要对一个对象结构中的对象进行