【问题标题】:Antlr4 Precedence and AssociativityAntlr4 优先级和关联性
【发布时间】:2013-04-03 15:41:43
【问题描述】:

我对解析 (B32|B5)&B31 之类的表达式很感兴趣。 我的目标是找出这个表达式被评估的顺序。所以我的预期结果将是B2 然后是|B5 最后是&B31

我的表达式可以有特殊字符。与*={。所以exp可以是B31*{A1,A2}|B35。在这种情况下,我希望 B31*{A1,A2} 作为一个令牌,它首先被评估,然后是 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 ; 

我用-visitor编译了上面的Expr.g4,这样就生成了一个访问者。 然后我创建了一个访问者类来遍历每个表达式并将其捕获到一个列表中。

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);
    }
}    

现在我终于写了一个测试程序来打印出令牌列表以及我需要查看它们的顺序

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)&amp;B31" 对其进行测试时。我得到以下结果。

 Expr =B31 &
 Expr =B5) |
 Expr =(B32

我的目标是获得优先级,以便首先计算括号中的表达式。但它似乎总是从最右边的表达式穿过树,在这种情况下它是B31

有人可以帮忙吗?语法正确吗?访问者的实现是否正确?

【问题讨论】:

    标签: antlr4


    【解决方案1】:

    目前()ID 中允许使用的字符,因此您的示例字符串将被分解为以下标记:

    • (B32
    • |
    • B5)
    • &amp;
    • B31

    另一个问题是您在表达式规则中没有区分&amp;| 的优先级。这意味着像X|Y&amp;B 这样的表达式在您的语言中将等同于(X|Y)&amp;B,而大多数语言会优先考虑&amp;,使其等同于X|(Y&amp;B)

    要更正这些项目,您可能需要执行以下操作。

    1. ID 规则中删除'('')'。如果您想要命名令牌,可以选择添加以下内容。

      LPAREN : '(';
      RPAREN : ')';
      
    2. 更正expr规则,分别处理&amp;|

      expr: '(' expr ')'
          | expr '&' expr
          | expr '|' expr
          | ID
          ;
      

    【讨论】:

    • 我们如何找到正在评估的终端节点的顺序。例如,采用 (B1=p & A4=p | A8=p) | 类型的复杂表达式。 (A6=p | ~A5=c)。我们如何找出在访问者实现中首先评估哪个终端节点。我只需要表达式的求值顺序,不需要实际求值并获取值。
    猜你喜欢
    • 2020-10-20
    • 2012-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    相关资源
    最近更新 更多