求解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

我正在尝试使用ANTLR解析一种语言,它可以包含以下语法:

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)                |
+----------------------+--------------------------------------------------------+