求解antlr左递归
我正在尝试使用ANTLR解析一种语言,它可以包含以下语法:求解antlr左递归,antlr,left-recursion,Antlr,Left Recursion,我正在尝试使用ANTLR解析一种语言,它可以包含以下语法: someVariable, somVariable.someMember, functionCall(param).someMember, foo.bar.baz(bjork).buffalo().xyzzy 这是我到目前为止提出的ANTLR语法,access\u操作抛出错误 以下规则集是相互左递归的[access\u operation,expression]: grammar Test; options { output
someVariable, somVariable.someMember, functionCall(param).someMember, foo.bar.baz(bjork).buffalo().xyzzy
这是我到目前为止提出的ANTLR语法,access\u操作
抛出错误
以下规则集是相互左递归的[access\u operation,expression]
:
grammar Test;
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
LHS;
RHS;
CALL;
PARAMS;
}
start
: body? EOF
;
body
: expression (',' expression)*
;
expression
: function -> ^(CALL)
| access_operation
| atom
;
access_operation
: (expression -> ^(LHS)) '.'! (expression -> ^(RHS))
;
function
: (IDENT '(' body? ')') -> ^(IDENT PARAMS?)
;
atom
: IDENT
| NUMBER
;
fragment LETTER : ('a'..'z' | 'A'..'Z');
fragment DIGIT : '0'..'9';
IDENT : (LETTER)+ ;
NUMBER : (DIGIT)+ ;
SPACE : (' ' | '\t' | '\r' | '\n') { $channel=HIDDEN; };
到目前为止,我能做的就是将access\u操作
规则重构为。”表达式
生成一个AST,其中access\u操作
节点只包含操作的右侧
我要找的是这样的东西:
在这种情况下,左边的递归问题如何解决?通过“错误的AST”,我会做一个半教育式的猜测,对于像“foo.bar.baz”
这样的输入,你会得到一个AST,其中foo
是bar
的根,作为一个孩子,而这个孩子又有baz
,这是AST中的一片叶子。您可能需要将其反转。但如果我是你,我就不会选择这样的AST:我会尽可能保持AST水平:
foo
/ | \
/ | \
bar baz ...
这样,评估就容易多了:您只需查找foo
,然后从左向右遍历它的子对象
快速演示:
语法测试;
选项{
输出=AST;
ASTLabelType=CommonTree;
}
代币{
身体;
通道
呼叫
参数;
}
开始
:body EOF->body
;
身体
:表达式(“,”表达式)*->^(主体表达式+)
;
表达
:原子
;
原子
:号码
|(识别->识别)(尾部->^(识别尾部)
|呼叫尾部?->^(呼叫识别呼叫尾部?)
)?
;
尾
:(访问)+
;
通道
:('.'标识->^(访问标识))(调用->^(调用标识调用))?
;
呼叫
:“(”(表达式(“,”表达式)*)?”->^(参数表达式*)
;
识别号:字母+;
数字:数字+;
空格:(“”|’\t’|’\r’|’\n’{$channel=HIDDEN;};
片段字母:('a'..'z'|'a'..'z');
片段数字:“0”…'9';
可通过以下方式进行测试:
import org.antlr.runtime.*;
导入org.antlr.runtime.tree.*;
导入org.antlr.stringtemplate.*;
公共班机{
公共静态void main(字符串[]args)引发异常{
字符串src=“someVariable,somVariable.someMember,functionCall(param).someMember,”+
“foo.bar.baz(bjork.buffalo().xyzy”;
TestLexer lexer=newtestlexer(newantlrstringstream(src));
TestParser=newtestparser(newcommontokenstream(lexer));
CommonTree=(CommonTree)解析器.start().getTree();
DOTTreeGenerator gen=新的DOTTreeGenerator();
StringTemplate st=gen.toDOT(树);
系统输出打印LN(st);
}
}
Main
的输出对应于以下AST:
编辑
由于您指出您的最终目标不是评估输入,而是需要将AST的结构与某个第三方API相一致,因此以下语法将创建一个AST,如您在编辑的问题中所述:
语法测试;
选项{
输出=AST;
ASTLabelType=CommonTree;
}
代币{
身体;
访问(OP);;
呼叫
参数;
LHS;
RHS;
}
开始
:body EOF->body
;
身体
:表达式(“,”表达式)*->^(主体表达式+)
;
表达
:原子
;
原子
:号码
|(ID->ID)((“(“参数”)”->^(调用ID参数))
('.'表达式->^(访问操作^(LHS^(调用ID参数))^(RHS表达式)))?
|'.'表达式->^(访问操作^(LHS ID)^(RHS表达式))
)?
;
params
:(表达式(','表达式)*)?->^(参数表达式*)
;
ID:字母+;
数字:数字+;
空格:(“”|’\t’|’\r’|’\n’{$channel=HIDDEN;};
片段字母:('a'..'z'|'a'..'z');
片段数字:“0”…'9';
如果运行Main
类,将创建以下AST:
atom
规则可能有点让人望而生畏,但您不能将其缩短太多,因为左侧的ID
需要对大多数备选方案可用。ANTLRWorks有助于可视化此规则可能采用的替代路径:
这意味着atom
可以是以下5种备选方案中的任意一种(及其相应的AST):
当输入
foo.bar.baz(bjork.buffalo().xyzy
)时,您希望语法做什么?“哪一个生成了错误的AST”,这个错误的AST是什么样子的?AST应该是什么样子呢?作为补充,ANTLR的下一个主要版本(v4)也将能够处理左递归!我不知道确切的发布日期,但我已经尝试过提前发布,非常酷!谢谢你的深入回答。修改后的语法解决了左递归问题。我不太清楚我说的“错误的AST”是什么意思。我用于AST遍历的API提供了一个类FieldAccess
,其签名为ASTNode dispatcher,ASTNode field
。我正在尝试创建的AST应该与本文中的API兼容。这意味着access
规则应该创建一个包含访问的左侧和右侧的ASTNode。这重新引入了左递归问题。但可能我在这里走错了路,我可以使用ANTLR中的其他方法使用修改后的语法访问access
规则的dispatcher
。我已经更新了问题并添加了我试图创建的AST。@RobertGrundler,检查我的编辑。将atom
规则左侧的ID
以及所有其余部分作为可选的第二个标记移动到右侧的想法非常好。它现在生成的AST与API所期望的完全相同。顺便说一句,我所说的API是提供在eclipse中实现脚本语言的方法的API。
+----------------------+--------------------------------------------------------+
| alternative | generated AST |
+----------------------+--------------------------------------------------------+
| NUMBER | NUMBER |
| ID | ID |
| ID params | ^(CALL ID params) |
| ID params expression | ^(ACCESS_OP ^(LHS ^(CALL ID params)) ^(RHS expression))|
| ID expression | ^(ACCESS_OP ^(LHS ID) ^(RHS expression) |
+----------------------+--------------------------------------------------------+