我认为没有一个简单的方法可以做到这一点。你可以这样做:
函数
:id周期函数
->^(函数ID)
我的身份证
;
< /代码>
但这只会使最后一个节点成为根节点,并使所有其他节点成为其子节点。例如,以下来源:
a.b.c.d.e
< /代码>
将生成以下树:
e
//\\
DC-B-A
< /代码>
我看不到简单的修复方法,因为当您第一次分析a.b.c.d.e时,,a
will b e thei d
an db.c.d.e
the recursive call tofunction
:。
a.b.c.d.e
+ + -+
γ
|`------>函数
γ
`----------->ID
< /代码>
从而导致b.c.d.e
将a
作为其子级。然后当b变为id时,它也会作为孩子添加到a旁边。在您的情况下,应将a作为儿童删除,然后添加到b的儿童列表中。但是,在Anltr中,这是不可能的(至少在语法中不是很干净)。
编辑
好吧,作为一个工作,我有一些优雅的想法,但这并不是我希望的那样。因此,作为一个不那么优雅的解决方案,您可以将重写规则中的lastnode匹配为根:
函数
:(id'')*last=id->^($last)
;
< /代码>
然后在alist中收集所有可能的前面节点(children)。
函数
:(children+=id'')*last=id->^($last)
;
< /代码>
并在解析器中使用自定义成员方法“inject”这些childreninto the root of your tree(going from right to left in yourlist!)
函数
:(children+=id'')*last=id反向($children,(commontree)$last.tree);->^($last)
;
< /代码>
一个小演示:
grammar reversetree;
选项{
输出=AST;
}
令牌{
根;
}
@成员{
私有void reverse(列表节点,commontree根){
if(nodes==null)返回;
对于(int i=nodes.size()-1;i>=0;i--){
commonTree temp=(commonTree)节点。get(i);
root.addchild(温度);
根=温度;
}
}
}
解析
:function+eof->^(根函数+)
;
功能
:(children+=id'')*last=id反向($children,(commontree)$last.tree);->^($last)
;
身份证件
身份证
;
身份证件
:('A'..'Z''A'..'Z')+
;
空间
:''skip();
;
< /代码>
还有一点测试课:
import org.antlr.runtime.*;
导入org.antlr.runtime.tree.*;
导入org.antlr.StringTemplate.*;
公务舱主{
公共静态void main(string[]args)引发异常{
antlrstringstream in=新的antlrstringstream(“a.b.c.d.e stack.overflow.horse singlenode”);
reverseTreeLexer lexer=新的reverseTreeLexer(in);
commontokenstream tokens=新的commontokenstream(lexer);
ReversetTreeParser Parser=新的ReversetTreeParser(标记);
reverseTreeParser.parse_返回返回值=parser.parse();
commonTree树=(commonTree)返回值。gettree();
dotTreegenerator gen=new dotTreegenerator();
stringtemplate st=gen.todot(树);
系统.out.println(st);
}
}
< /代码>
它将生成一个ast,看起来像:
![](“https://i.stack.imgur.com/ernwr.png”alt=“alt)
对于输入字符串:
“a.b.c.d.e stack.overflow.horse singlenode”
< /代码>
按如下方式制定规则:
function
: ID PERIOD function
-> ^(function ID)
| ID
;
但这只会使最后一个节点成为根节点,并使所有其他节点成为其子节点。例如,以下来源:
a.b.c.d.e
将导致以下树:
e
/ / \ \
d c b a
我看不出一个简单的修复方法,自从你第一次分析a.b.c.d.e
,a
将是ID
和b.c.d.e
对的递归调用function
:
a.b.c.d.e
| +-----+
| |
| `-----> function
|
`----------> ID
结果是B.C.D.E
将有一
作为它的孩子。那时b
成为身份证件
,它也作为子级添加到一
. 在你的情况下,一
应作为子级删除,然后添加到乙
孩子们。但是,在Anltr中,这是不可能的(至少,在语法中不是以一种干净的方式)。
编辑
好吧,作为一个工作,我有一些优雅的想法,但这并不是我希望的那样。因此,作为一个不那么优雅的解决方案,您可以将last
节点作为重写规则的根:
function
: (id '.')* last=id -> ^($last)
;
然后收集前面所有可能的节点(children
在AList
使用+=
操作员:
function
: (children+=id '.')* last=id -> ^($last)
;
并使用解析器中的自定义成员方法“注入”这些儿童
在你的树根(从右到左表
!):
function
: (children+=id '.')* last=id {reverse($children, (CommonTree)$last.tree);} -> ^($last)
;
一个小演示:
grammar ReverseTree;
options {
output=AST;
}
tokens {
ROOT;
}
@members {
private void reverse(List nodes, CommonTree root) {
if(nodes == null) return;
for(int i = nodes.size()-1; i >= 0; i--) {
CommonTree temp = (CommonTree)nodes.get(i);
root.addChild(temp);
root = temp;
}
}
}
parse
: function+ EOF -> ^(ROOT function+)
;
function
: (children+=id '.')* last=id {reverse($children, (CommonTree)$last.tree);} -> ^($last)
;
id
: ID
;
ID
: ('a'..'z' | 'A'..'Z')+
;
Space
: ' ' {skip();}
;
还有一点测试课:
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
public class Main {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("a.b.c.d.e Stack.Overflow.Horse singleNode");
ReverseTreeLexer lexer = new ReverseTreeLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ReverseTreeParser parser = new ReverseTreeParser(tokens);
ReverseTreeParser.parse_return returnValue = parser.parse();
CommonTree tree = (CommonTree)returnValue.getTree();
DOTTreeGenerator gen = new DOTTreeGenerator();
StringTemplate st = gen.toDOT(tree);
System.out.println(st);
}
}
它将生成一个AST,如下所示:
![alt text](https://i.stack.imgur.com/ERNwr.png)
对于输入字符串:
"a.b.c.d.e Stack.Overflow.Horse singleNode"