我对解析诸如 (B32|B5)&B31
之类的表达式很感兴趣。
我的目标是找出这个表达式被评估的顺序。所以我的预期结果将是 B2
然后是 |B5
和最后一个 &B31
我的表达式可以有特殊字符。使用 *
、 =
和 {
。所以 exp 可以是 B31*{A1,A2}|B35
。在这种情况下,我希望 B31*{A1,A2}
作为一个 token ,它首先被评估,然后是 B35
。
我创建了以下语法。
grammar Expr;
prog: (expr NEWLINE)* ;
expr: '(' expr ')'
| expr ('&'|'|') expr
| ID
;
NEWLINE:'\r'? '\n' ;
// lexer/terminal rules start with an upper case letter
ID
:
(
'a'..'z'
| 'A'..'Z'
| '0'..'9' | ' '
| ('+'|'-'|'*'|'/'|'_')
| '='
| '~'
| '('
| ')'
| '{'
| '}'
| ','
)+
;
WS : [ \t]+ -> skip ;
我用
Expr.g4
编译了上面的 -visitor
以便生成一个访问者。然后我创建了一个访问者类来遍历每个表达式并将其捕获在列表中。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import org.antlr.v4.runtime.tree.ParseTree;
public class EvaluationVisitor extends ExprBaseVisitor<Value> {
public List<EvalExpression> exprList = new ArrayList<EvalExpression>();
public HashMap<String, EvalExpression> evalExprMap= new HashMap<String, EvalExpression>();
public Value visitProg(ExprParser.ProgContext ctx) {
return visitChildren(ctx);
}
public Value visitExpr(ExprParser.ExprContext ctx) {
if (ctx.getChildCount() == 3) {
String exprEval = ctx.getText();
String leftExpr = ctx.getChild(0).getText();
String token = ctx.getChild(1).getText();
String rightExpr = ctx.getChild(2).getText();
//System.out.println(" exprEval =" + exprEval);
//System.out.println("<" + leftExpr + "> " + token + " <" + rightExpr + ">");
EvalExpression evalExprObj = new EvalExpression(exprEval, leftExpr, token, rightExpr);
exprList.add(evalExprObj);
evalExprMap.put(exprEval, evalExprObj);
}
return visitChildren(ctx);
}
public List<EvalExpression> getExprList() {
return exprList;
}
public void setExprList(List<EvalExpression> exprList) {
this.exprList = exprList;
}
public HashMap<String, EvalExpression> getEvalExprMap() {
return evalExprMap;
}
public void setEvalExprMap(HashMap<String, EvalExpression> evalExprMap) {
this.evalExprMap = evalExprMap;
}
}
EvalExpression
类如下public class EvalExpression {
private String expressionEvaluated;
private String leftExpr;
private String token;
private String rightExpr;
public EvalExpression(String expressionEvaluated, String leftExpr, String token,
String rightExpr) {
super();
this.expressionEvaluated = expressionEvaluated;
this.leftExpr = leftExpr;
this.token = token;
this.rightExpr = rightExpr;
}
public String getExpressionEvaluated() {
return expressionEvaluated;
}
public void setExpressionEvaluated(String expressionEvaluated) {
this.expressionEvaluated = expressionEvaluated;
}
public String getLeftExpr() {
return leftExpr;
}
public void setLeftExpr(String leftExpr) {
this.leftExpr = leftExpr;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
Value
如下public class Value {
public static Value VOID = new Value(new Object());
final Object value;
public Value(Object value) {
this.value = value;
}
public Boolean asBoolean() {
return (Boolean)value;
}
public Double asDouble() {
return (Double)value;
}
public String asString() {
return String.valueOf(value);
}
public boolean isDouble() {
return value instanceof Double;
}
@Override
public int hashCode() {
if(value == null) {
return 0;
}
return this.value.hashCode();
}
@Override
public boolean equals(Object o) {
if(value == o) {
return true;
}
if(value == null || o == null || o.getClass() != value.getClass()) {
return false;
}
Value that = (Value)o;
return this.value.equals(that.value);
}
@Override
public String toString() {
System.out.println("---------Inside Value to String --------------");
return String.valueOf(value);
}
}
现在终于我写了一个测试程序来打印出 token 列表和我需要查看它们的顺序
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import com.inmedius.antlr.ExprLexer;
import com.inmedius.antlr.ExprParser;
import com.inmedius.antlr.eval.EvalExpression;
import com.inmedius.antlr.eval.EvaluationVisitor;
import com.inmedius.antlr.eval.ExpressionTestVisitor;
public class EvalExprTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
try {
//String src = "(B1=p & A4=p | A8=p) | (A6=p | ~A5=c)";
String src = "(B32|B5)&B31";
CharStream stream = (CharStream) (new ANTLRInputStream(src));
ExprLexer lexer = new ExprLexer(stream);
TokenStream tokens = new CommonTokenStream(lexer);
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
ParseTree tree = parser.prog();
if (!src.contains("&") && !src.contains("|")) {
System.out.print("exp=" + src);
} else {
EvaluationVisitor visitor = new EvaluationVisitor();
visitor.visit(tree);
List<EvalExpression> exprOrderList = visitor.getExprList();
HashMap<String, EvalExpression> evalMap = visitor.getEvalExprMap();
for (EvalExpression eval : exprOrderList) {
System.out.println(" Expr =" + eval.getRightExpr() + " "
+ eval.getToken());
if (evalMap.get(eval.getLeftExpr()) == null) {
System.out.println(" Expr =" + eval.getLeftExpr());
}
}
}
} catch (Exception e) {
e.printStackTrace(System.out);
throw e;
}
}
}
public String getRightExpr() {
return rightExpr;
}
public void setRightExpr(String rightExpr) {
this.rightExpr = rightExpr;
}
}
我的问题是当我运行
EvalExprTest
并在程序中我用 String src = "(B32|B5)&B31"
测试它时。我得到以下结果。 Expr =B31 &
Expr =B5) |
Expr =(B32
我的目标是获得一个优先级,这样括号中的表达式将首先被评估。但它似乎总是从最右边的表达式遍历树,在这种情况下它是
B31
。有人可以帮忙吗?语法正确吗?访问者实现是正确的吗?
最佳答案
目前 (
和 )
是 ID
中允许的字符,因此您的示例字符串将被分解为以下标记:
(B32
|
B5)
&
B31
另一个问题是您没有区分表达式规则中
&
和 |
的优先级。这意味着像 X|Y&B
这样的表达式在您的语言中等同于 (X|Y)&B
,其中大多数语言会优先考虑 &
以使其等同于 X|(Y&B)
。要更正这些项目,您可能需要执行以下操作。
'('
规则中删除 ')'
和 ID
。如果您想要命名 token ,可以选择添加以下内容。LPAREN : '(';
RPAREN : ')';
expr
规则,分别处理 &
和 |
。expr: '(' expr ')'
| expr '&' expr
| expr '|' expr
| ID
;
关于Antlr4 优先级和关联性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15791859/