【问题标题】:if then else conditional evaluationif then else 条件评估
【发布时间】:2011-07-08 20:39:24
【问题描述】:

我有一种语言,基本上是为了将列映射到数组中的新结构。该语言旨在让产品经理无需了解大量编程细节即可定义映射。我确信这里还有很多需要改进的地方,但这就是我所拥有的。

该语言大部分都有效。我遇到的问题是条件语句。

我的解析器有以下规则:

conditionalexpr :  IF^ LPAREN! (statement) RPAREN! THEN! LCURLY! statement RCURLY! (ELSE! LCURLY! statement RCURLY!)?;

这可以生成具有三个孩子的树。

如果条件不允许,我的问题是避免评估语句。

我很天真地做了:

conditionalexpr returns[Object o]: 
  ^(IF a=statement b=statement (c=statement)?)
  {
    $o = (Boolean)$a.o ? $b.o : $c.o != null ? $c.o : "";
  }
  ;

显然这行不通。

我一直在玩语法谓词,但我无法使它们正常工作。

语句当前返回一个对象。大多数语言处理字符串,但我也需要支持布尔值和数字(整数和十进制)。

如果我添加类似 {$a.o}?=> 我在生成的代码中得到一个 $a。

我查看了 antlr-interest 列表,但这个问题在那里并没有得到很好的回答,很可能是因为这对他们来说似乎很明显。

我愿意发布完整的语法,但为了简短起见,我把它省略了。

【问题讨论】:

    标签: java antlr antlr3


    【解决方案1】:

    如果您不希望评估某些子树,则需要让树规则返回节点而不是实际值。您可以扩展 CommonTree 并提供自定义 TreeAdaptor 来帮助构建您自己的节点,但就个人而言,我发现创建自定义节点类(或多个类)并改用它们是最简单的。一个演示来澄清:

    T.g

    grammar T;
    
    options {
      output=AST;
    }
    
    tokens {
      ASSIGNMENT;
    }
    
    parse
      :  statement+ EOF -> statement+
      ;
    
    statement
      :  ifStatement
      |  assignment
      ;
    
    ifStatement
      :  IF a=expression THEN b=expression (ELSE c=expression)? -> ^(IF $a $b $c?)
      ;
    
    assignment
      :  ID '=' expression -> ^(ASSIGNMENT ID expression)
      ;
    
    expression
      :  orExpression
      ;
    
    orExpression
      :  andExpression (OR^ andExpression)*
      ;
    
    andExpression
      :  equalityExpression (AND^ equalityExpression)*
      ;
    
    equalityExpression
      :  relationalExpression (('==' | '!=')^ relationalExpression)*
      ;
    
    relationalExpression
      :  atom (('<=' | '<' | '>=' | '>')^ atom)*
      ;
    
    atom
      :  BOOLEAN
      |  NUMBER
      |  ID
      |  '(' expression ')' -> expression
      ;
    
    IF      : 'if';
    THEN    : 'then';
    ELSE    : 'else';
    OR      : 'or';
    AND     : 'and';
    BOOLEAN : 'true' | 'false';
    ID      : ('a'..'z' | 'A'..'Z')+;
    NUMBER  : '0'..'9'+ ('.' '0'..'9'+)?;
    SPACE   : (' ' | '\t' | '\r' | '\n') {skip();};
    

    Main.java

    我创建了一个具有eval(): Object 方法的Node 接口,还创建了一个抽象类BinaryNode,它实现了Node,并且总是有2 个孩子。正如您在这些 Java 类之后的树语法中所见,所有规则现在都返回 Node

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        String source = "a = 3   b = 4   if b > a then b==b else c==c";
        TLexer lexer = new TLexer(new ANTLRStringStream(source));
        TParser parser = new TParser(new CommonTokenStream(lexer));
        TWalker walker = new TWalker(new CommonTreeNodeStream(parser.parse().getTree()));
        Node root = walker.walk();
        System.out.println(root.eval());
      }
    }
    
    interface Node {
      Object eval();
    }
    
    abstract class BinaryNode implements Node {
    
      protected Node left;
      protected Node right;
    
      public BinaryNode(Node l, Node r) {
        left = l;
        right = r;
      }
    }
    
    class AtomNode implements Node {
    
      private Object value;
    
      public AtomNode(Object v) {
        value = v;
      }
    
      @Override
      public Object eval() {
        return value;
      }
    }
    
    class OrNode extends BinaryNode {
    
      public OrNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Boolean)super.left.eval() || (Boolean)super.right.eval();
      }
    }
    
    class AndNode extends BinaryNode {
    
      public AndNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Boolean)super.left.eval() && (Boolean)super.right.eval();
      }
    }
    
    class LTNode extends BinaryNode {
    
      public LTNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Double)super.left.eval() < (Double)super.right.eval();
      }
    }
    
    class LTEqNode extends BinaryNode {
    
      public LTEqNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Double)super.left.eval() <= (Double)super.right.eval();
      }
    }
    
    class GTNode extends BinaryNode {
    
      public GTNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Double)super.left.eval() > (Double)super.right.eval();
      }
    }
    
    class GTEqNode extends BinaryNode {
    
      public GTEqNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return (Double)super.left.eval() >= (Double)super.right.eval();
      }
    }
    
    class EqNode extends BinaryNode {
    
      public EqNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return super.left.eval().equals(super.right.eval());
      }
    }
    
    class NEqNode extends BinaryNode {
    
      public NEqNode(Node left, Node right) { super(left, right); }
    
      @Override
      public Object eval() {
        return !super.left.eval().equals(super.right.eval());
      }
    }
    
    class VarNode implements Node {
    
      private java.util.Map<String, Object> memory;
      private String var;
    
      VarNode(java.util.Map<String, Object> m, String v) {
        memory = m;
        var = v;
      }
    
      @Override
      public Object eval() {
        Object value = memory.get(var);
        if(value == null) {
          throw new RuntimeException("Unknown variable: " + var);
        }
        return value;
      }
    }
    
    class IfNode implements Node {
    
      private Node test;
      private Node ifTrue;
      private Node ifFalse;
    
      public IfNode(Node a, Node b, Node c) {
        test = a;
        ifTrue = b;
        ifFalse = c;
      }
    
      @Override
      public Object eval() {
        return (Boolean)test.eval() ? ifTrue.eval() : ifFalse.eval();
      }
    }
    

    TWalker.g

    tree grammar TWalker;
    
    options {
      tokenVocab=T;
      ASTLabelType=CommonTree;
    }
    
    @members {
      private java.util.Map<String, Object> memory = new java.util.HashMap<String, Object>();
    }
    
    walk returns [Node n]
      :  (statement {$n = $statement.n;})+
      ;
    
    statement returns [Node n]
      :  ifStatement {$n = $ifStatement.n;}
      |  assignment  {$n = null;}
      ;
    
    assignment
      :  ^(ASSIGNMENT ID expression) {memory.put($ID.text, $expression.n.eval());}
      ;
    
    ifStatement returns [Node n]
      :  ^(IF a=expression b=expression c=expression?) {$n = new IfNode($a.n, $b.n, $c.n);}
      ;
    
    expression returns [Node n]
      :  ^(OR a=expression b=expression)   {$n = new OrNode($a.n, $b.n);}
      |  ^(AND a=expression b=expression)  {$n = new AndNode($a.n, $b.n);}
      |  ^('==' a=expression b=expression) {$n = new EqNode($a.n, $b.n);}
      |  ^('!=' a=expression b=expression) {$n = new NEqNode($a.n, $b.n);}
      |  ^('<=' a=expression b=expression) {$n = new LTEqNode($a.n, $b.n);}
      |  ^('<' a=expression b=expression)  {$n = new LTNode($a.n, $b.n);}
      |  ^('>=' a=expression b=expression) {$n = new GTEqNode($a.n, $b.n);}
      |  ^('>' a=expression b=expression)  {$n = new GTNode($a.n, $b.n);}
      |  BOOLEAN                           {$n = new AtomNode(Boolean.valueOf($BOOLEAN.text));}
      |  NUMBER                            {$n = new AtomNode(Double.valueOf($NUMBER.text));}
      |  ID                                {$n = new VarNode(memory, $ID.text);}
      ;
    

    如果您现在运行主类并评估:

    a = 3   
    b = 4   
    if b > a then 
      b==b 
    else 
      c==c
    

    true 正在打印到控制台:

    bart@hades:~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool T.g
    bart@hades:~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool TWalker.g
    bart@hades:~/Programming/ANTLR/Demos/T$ javac -cp antlr-3.3.jar *.java
    bart@hades:~/Programming/ANTLR/Demos/T$ java -cp .:antlr-3.3.jar Main
    true
    

    但是如果你检查b &lt; a是否导致else被执行,你会看到如下:

    Exception in thread "main" java.lang.RuntimeException: Unknown variable: c
            at VarNode.eval(Main.java:140)
            at EqNode.eval(Main.java:112)
            at IfNode.eval(Main.java:160)
            at Main.main(Main.java:11)
    

    对于更复杂的语言结构(范围、函数等)的实现,请参阅my blog

    祝你好运!

    【讨论】:

    • 它不起作用的原因是这意味着 then 和(可选的)else 子句都将被评估。这可能导致不良后果。仅在必要时才应评估该代码,而不是在所有情况下。
    • 好的,工作很忙,所以花了一段时间才回到这里,但答案看起来很不错。感谢您的帮助
    猜你喜欢
    • 2016-11-27
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2020-06-01
    • 1970-01-01
    • 2017-06-04
    • 1970-01-01
    • 2013-03-05
    相关资源
    最近更新 更多