【问题标题】:Antlr Tree Pattern Matching with Rewrite RulesAntlr 树模式匹配与重写规则
【发布时间】:2011-03-08 17:31:19
【问题描述】:

对于a.b.c + d.e.f 之类的表达式,我有一个简单的 antlr 语法:

grammar Test;
options {
    output=AST;
}
tokens {
    VARIABLE_ID;
    QUALIFIER_ID;
}

ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
DOT : '.';
WS : ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;} ;

variable_id  : id=ID   -> VARIABLE_ID[$id];
qualifier_id  : id=ID   -> QUALIFIER_ID[$id];

expr_start : expr EOF;
expr : var (options {greedy=true;} : '+' expr)*;

var : variable_id (DOT qualifier_id)*;

现在我想在这个语法上定义一个模式匹配器,把a.b.c变成0.1.2,所以我定义一个树模式匹配器如下

tree grammar TestWalker;
options {
    tokenVocab=Test;
    ASTLabelType=CommonTree;
    filter=true;
    backtrack=true;
}

@members {
    TokenRewriteStream tokens;

    public void setTreeNodeStream(TreeNodeStream input) {
        super.setTreeNodeStream(input);
        tokens = (TokenRewriteStream)input.getTokenStream(); 
    }
}

topdown : var;

variable_id [int i] : id=VARIABLE_ID {
    tokens.replace($id.getToken(), "" + $i);
};

qualifier_id [int i] : id=QUALIFIER_ID {
    tokens.replace($id.getToken(), "" + $i);
};

var 
@init { int index = 0; }
: variable_id[index] 
(   DOT 
    { ++index; }
    qualifier_id[index]
)*;

然后我整理了一个小测试程序:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {
    public static void main(String[] args) throws Exception {
        TestLexer lex = new TestLexer(new ANTLRInputStream(System.in));
        TokenStream tokens = new TokenRewriteStream(lex);

        TestParser parser = new TestParser(tokens);
        TestParser.expr_return expr = parser.expr();

        CommonTreeNodeStream nodes = new CommonTreeNodeStream((Tree)expr.getTree());
        nodes.setTokenStream(tokens);
        TestWalker walker = new TestWalker(nodes);
        walker.downup(expr.getTree());
        System.out.println(tokens.toString());
    }
}

当我使用基本输入运行这个程序时,我看到了令人惊讶的结果:
a.b.c -> 0.b.c
a.b + d.e -> 0.b + 0.e
等等。看来我的规则的(DOT qualifier_id)* 部分从不匹配,我不知道为什么。我尝试将我的规则添加到树模式匹配的自上而下和自下而上的部分。如果我从过滤器匹配器切换到整个树匹配器并为'+' 案例适当地添加规则到分支它可以工作,但是当重写只是更大语法的一个较小片段时,这将变得站不住脚。任何指针将不胜感激。

更新:使用 antlr 3.3

【问题讨论】:

    标签: pattern-matching antlr antlr3


    【解决方案1】:

    问题的症结(由@Bart 确定)是解析没有正确生成 AST。语法需要构建 AST(注意附加的 ^ 标记)。

    grammar Test;
    options {
        output=AST;
    }
    tokens {
        VARIABLE_ID;
        QUALIFIER_ID;
    }
    
    ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
    DOT : '.';
    WS : ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;} ;
    
    variable_id  : id=ID   -> VARIABLE_ID[$id];
    qualifier_id  : id=ID   -> QUALIFIER_ID[$id];
    
    expr_start : expr EOF;
    expr : var (options {greedy=true;} : '+'^ expr)*;
    
    var : variable_id (DOT^ qualifier_id)*;
    

    然后树模式匹配器需要在构造 AST 时遍历它。注意 expr 规则的结构和用于处理状态的参数。

    tree grammar TestWalker;
    options {
        tokenVocab=Test;
        ASTLabelType=CommonTree;
        filter=true;
        backtrack=true;
    }
    
    @members {
        TokenRewriteStream tokens;
    
        public void setTreeNodeStream(TreeNodeStream input) {
            super.setTreeNodeStream(input);
            tokens = (TokenRewriteStream)input.getTokenStream(); 
        }
    }
    
    topdown : expr[0];
    
    variable_id returns [int r] : id=VARIABLE_ID {
        $r = 0;
        tokens.replace($id.getToken(), "" + $r);
    };
    
    qualifier_id [int i] returns [int r] : id=QUALIFIER_ID {
        $r = $i + 1;
        tokens.replace($id.getToken(), "" + $r);
    };
    
    expr [int i] returns [int r]
        : v=variable_id { $r = $v.r; }
        | ^(DOT e=expr[$i] q=qualifier_id[$e.r] { $r = $q.r; } )
        ;
    

    现在输出将按预期运行:
    a.b + d.e + c.d.e.f
    0.1 + 0.1 + 0.1.2.3
    并且生成的 AST 看起来是正确的。

    【讨论】:

    • 太棒了!感谢您发布解决方案 +1。
    【解决方案2】:

    我不熟悉这种(相对较新的)模式树行走。我在 Wiki 上浏览了一下,但它不在我的 ANTLR 参考副本中。

    不过,您的测试语法并不是 100%:当我从中生成解析器时,我得到:

    java -cp antlr-3.2.jar org.antlr.Tool Test.g
    warning(200): Test.g:18:46: Decision can match input such as "'+'" using multiple alternatives: 1, 2
    As a result, alternative(s) 2 were disabled for that input
    

    当我可视化您的解析器使用您的类生成的 AST 时(添加一点点):

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            TestLexer lex = new TestLexer(new ANTLRStringStream("a.b + d.e"));
            TokenStream tokens = new TokenRewriteStream(lex);
    
            TestParser parser = new TestParser(tokens);
            TestParser.expr_return expr = parser.expr();
    
            CommonTreeNodeStream nodes = new CommonTreeNodeStream((Tree)expr.getTree());
            nodes.setTokenStream(tokens);
    
            CommonTree tree = (CommonTree)expr.getTree();
    
            DOTTreeGenerator gen = new DOTTreeGenerator();
            StringTemplate st = gen.toDOT(tree);
            System.out.println(st);
    
            TestWalker walker = new TestWalker(nodes);
            walker.downup(tree);
            System.out.println(tokens.toString());
        }
    }
    

    我看到输入 a.b + d.e 产生了 AST:

    我想你的 tree walker 正在遍历所述树,这并不让我感到惊讶,因为它只是一个具有单个根的节点的平面列表。

    【讨论】:

    • 有趣的是,antlr3.3 中没有显示该错误消息(这是我正在使用的)
    • @Matt,好吧,这不是一个严重的警告:毕竟,词法分析器和解析器 生成的。 严重的 (AFAIK) 是生成平坦 AST 的事实。至少,我认为你的树模式语法不喜欢它...... :)
    • 我使用 DOTTreeGenerator 看到了同样的情况,但是当我使用 antlr 作品时,我看到了完整的解析树。
    • @Matt,ANTLRWorks 会自行创建解析树(您无法使用)。要创建正确的 AST,请参阅previous Q&A
    • @Bart 谢谢,让我稍微解释一下,然后回复你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多