评估同质AST中的IF子树

时间:2013-01-11 16:13:55

标签: tree antlr antlr3 abstract-syntax-tree

我正在构建一个同类AST的树步行者(所有节点都有相同的类),评估if语句的正确方法是什么?

我的AST if if是这样的: enter image description here

我会在解析IF块时,依次评估他的CONDBLOCK个孩子,如果其中一个是真的,那么树步行者不会评估其余的。

更清楚的是,我的树步行者是这样的:

ifStat       : ^(IF { jump=false; } condition* defcond?) 
condition    : { if (jump) return retval; } ^(CONDBLOCK exp block) { jump=$exp.value; }
defcond      : ^(DEFAULT block)

我的问题是,如果在示例中$op=+所以必须执行第一个CONDBLOCK,我不想评估任何其他内容,我想要执行第一个CODEBLOCK并继续在我的AST树中评估if之后的块。

现在我已经用condition规则实现了一个标志,如果标志为真则返回(这意味着另一个块已被执行)。

return retval;完全停止了执行,我想在不评估剩余条件的情况下上升。我怎么能这样做?

1 个答案:

答案 0 :(得分:2)

来自AST的涉及分支或跳转的任何类型的运行时评估可能会变得丑陋。您可能需要考虑将AST转换为一系列更常规的操作并按顺序执行它们。这是一个额外的步骤,但它会让你摆脱像这样的堵塞,我认为它比AST评估者更容易验证和调试。

在此过程中,这是一种跳过评估后续conditiondefcond规则的方法。我坚持你所拥有的结构,这意味着评估有两个不同的阶段:匹配阶段(exp)和执行阶段(block)。这是值得注意的,因为阶段在子图的不同部分处理,并且没有自然的跳转方式,因此需要在整个if语句中跟踪它们。

这是一个简单的类来管理跟踪单个if评估:

class Evaluation {
    boolean matched = false;
    boolean done = false;
}

如果matched为真且done为false,则会执行下一个block。执行后,done设置为true。当matcheddone都为真时,block语句的其余部分不再执行if

以下是处理此问题的树解析器规则:

ifStat       
@init { Evaluation eval = new Evaluation(); }
             : ^(IF condition[eval]* defcond[eval]?) 
             ;

condition [Evaluation eval]
             : ^(CONDBLOCK exp {if ($exp.value) eval.matched = true;} evalblock[eval])
             ;

defcond [Evaluation eval] 
             : ^(DEFAULT {eval.matched = true;} evalblock[eval]) //force a match
             ;

evalblock [Evaluation eval]     
             : {eval.matched && !eval.done}? //Only do this when a condition is matched but not yet executed
                block                //call the execution code
                {eval.done = true;}  //evaluation is complete.
             | ^(CODEBLOCK .*)  //read the code subgraph (nothing gets executed)                
             ;

以下是我用来测试它的语法和代码:

TreeEvaluator.g(组合语法产生AST)

grammar TreeEvaluator;

options { 
    output = AST;
}

tokens { 
    CONDBLOCK;
    CODEBLOCK;
    DEFAULT;
}


compilationUnit : condition+ EOF;
condition   : cif elif* celse? -> ^(IF cif elif* celse?);
cif         : IF expr block -> ^(CONDBLOCK expr block);
elif        : ELIF expr block -> ^(CONDBLOCK expr block);
celse       : ELSE block -> ^(DEFAULT block); 
expr        : ID EQ^ ID;
block       : LCUR ID RCUR -> ^(CODEBLOCK ID);

IF  : 'if';
ELIF: 'elif';
ELSE: 'else';
LCUR: '{';
RCUR: '}';
EQ  : '==';
ID  : ('a'..'z'|'A'..'Z')+;
WS  : (' '|'\t'|'\f'|'\r'|'\n')+ {skip();};

AstTreeEvaluatorParser.g(树解析器)

tree grammar AstTreeEvaluatorParser;

options { 
    output = AST;
    tokenVocab = TreeEvaluator;
    ASTLabelType = CommonTree;
}

@members { 
    private static final class Evaluation {
        boolean matched = false; 
        boolean done = false;
    }

    private java.util.HashMap<String, Integer> vars = new java.util.HashMap<String, Integer>();

    public void addVar(String name, int value){
        vars.put(name, value);
    }

}

compilationUnit : ifStat+;

ifStat       
@init { Evaluation eval = new Evaluation(); }
             : ^(IF condition[eval]* defcond[eval]?) 
             ;

condition [Evaluation eval]
             : ^(CONDBLOCK exp {if ($exp.value) eval.matched = true;} evalblock[eval])
             ;

defcond [Evaluation eval] 
             : ^(DEFAULT {eval.matched = true;} evalblock[eval]) //force a match
             ;

evalblock [Evaluation eval]     
             : {eval.matched && !eval.done}? //Only do this when a condition is matched but not finished 
                block                //call the execution code
                {eval.done = true;}  //evaluation is complete.
             | ^(CODEBLOCK .*)  //read the code node and continue without executing
             ;

block        : ^(CODEBLOCK ID) {System.out.println("Executed " + $ID.getText());};

exp returns [boolean value]
            : ^(EQ lhs=ID rhs=ID)
                {$value = vars.get($lhs.getText()) == vars.get($rhs.getText());}
            ;

TreeEvaluatorTest.java(测试代码)

public class TreeEvaluatorTest {

    public static void main(String[] args) throws Exception {
        CharStream input = new ANTLRStringStream("if a == b {b} elif a == c {c} elif a == d {d} else {e}");
        TreeEvaluatorLexer lexer = new TreeEvaluatorLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        TreeEvaluatorParser parser = new TreeEvaluatorParser(tokens);

        TreeEvaluatorParser.compilationUnit_return result = parser.compilationUnit();

        if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
            throw new Exception("Syntax Errors encountered!");
        }

        AstTreeEvaluatorParser tparser = new AstTreeEvaluatorParser(new CommonTreeNodeStream(result.getTree()));
        tparser.addVar("a", 0);
        tparser.addVar("b", 2);
        tparser.addVar("c", 3);
        tparser.addVar("d", 4);
        AstTreeEvaluatorParser.compilationUnit_return tresult = tparser.compilationUnit();

    }
}

测试代码评估if a == b {b} elif a == c {c} elif a == d {d} else {e}。如果评估,则打印{}之间的ID。因此,如果a == b为真,则会打印"Executed b"

通过调用tparser.addVar(...)来分配变量值。在这种情况下,a不等于任何其他变量,因此会对{e}块进行评估。