2011-03-08 64 views
2

我有表达一个平凡的ANTLR语法像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.c0.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)*部分从来没有匹配,我不明白为什么。我尝试将自己的规则添加到Tree Pattern Match的自上而下和自下而上的部分。如果我从一个过滤器匹配器切换到一个完整的树匹配器并添加规则来适当地分支'+'的情况,它会起作用,但是当重写只是一个更大的语法的较小片段时,这变得站不住脚。任何指针将不胜感激。

更新:使用antlr 3.3

回答

2

问题的关键在于解析不能正确生成一个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看起来是正确的。

output AST

+0

太棒了!感谢您发布解决方案+1。 – 2011-03-08 22:33:22

1

我不熟悉这种(较新的)模式树行走。我在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:

enter image description here

我想象你的树漫步者遍历所说的树,这并不让我感到惊讶,它不会产生所需的结果,因为它只是一个带有单根的节点的平坦列表。

+0

有趣的是,该错误信息不会antlr3.3显示出来(这是我使用) – Matt 2011-03-08 20:58:12

+0

@马特,嗯,这是不是一个严重的警告:_are_毕竟生成的词法分析器和。什么_is_严重(AFAIK)是一个平坦的AST产生的事实。至少,我觉得你的树图案语法不喜欢它... :) – 2011-03-08 21:01:47

+0

我看到使用DOTTreeGenerator同样的事情,但是当我使用ANTLR作品我看到一个完整的解析树。 – Matt 2011-03-08 21:17:56