我有Antlr3解析器规则,如下所示:
ruleA:
(TOKEN_1) => TOKEN_1 ruleToken1
| (TOKEN_2) => TOKEN_2 ruleToken2
....
....
<many more such rules>
| genericRuleA
;
暂时假设适当地定义了令牌和规则。此外,genericRuleA
的定义方式使其对任何落在其上方的规则元素的内容表现得像“全能”。
因此,例如,genericRuleA
上面的子规则对应于命名函数,ruleToken1
和ruleToken2
捕获这些命名函数的调用方式。 genericRuleA
将捕获任何其他未命名的函数(例如用户定义的函数)。
这个语法的净效果是,如果ruleA
找到TOKEN_1
,它会占用ruleToken1
路径并在输入的其余部分不满足{{1}时报告错误}
在Antlr4中,我在取出语法谓词后最终得到以下解析器规则:
ruleToken1
这很有效,除非ruleA:
TOKEN_1 ruleToken1
| TOKEN_2 ruleToken2
....
....
<many more such rules>
| genericRuleA
;
的其余部分失败,解析器会自动选择ruleToken1
作为首选路径。所以现在,我有能够“重载”命名函数的副作用。这在某些情况下可能很有用,但在我的情况下,要求是表示不允许这种重载,即命名函数必须符合genericRuleA
中列出的特定结构,并在违反该结构时报告错误。正在编写此语法的系统不支持重载函数。
ruleToken1
必须满足除命名函数之外的任何其他内容。
我的第一个问题:是否有实现此转换的标准方式?
我看到的一种方法是创建一个与命名函数(TOKEN_1,TOKEN_2等)对应的标记列表;构造一个genericRuleA
函数,如果输入标记在此列表中具有成员资格,则返回@parser::member
。因此,假设true
已正确定义,规则将类似于:
isNamedFunction()
当你只有几个命名函数时,这可能会有效,但如果你有数百个(例如在TSQL中认为是内置函数),那么这个列表构建起来会相当麻烦。更不用说随着新命名函数的出现,我将不得不继续更新该列表。
所以我的后续问题是:假设上面概述的语义谓词方法是正确的方法,有没有办法以编程方式组装列表?
一种似乎有希望的方法是从ruleA:
TOKEN_1 ruleToken1
| TOKEN_2 ruleToken2
| {!isNamedFunction()}? genericRuleA
;
构建它。在这里,我将稍微重新构造规则,以便所有命名函数都属于一个规则(假设我们称之为getExpectedTokens()
),如下所示:
namedRuleA
然后从ruleA的ruleA:
namedRuleA
| genericRuleA
;
namedRuleA:
TOKEN_1 ruleToken1
| TOKEN_2 ruleToken2
....
....
<many more such rules>
;
[ATNState
]开始,我会沿着过渡走,直到我到达recognizer.getATN().states.get(recognizer.getState())
的{{1}},然后使用namedRuleA
来获得与ATNState
,getATN().getExpectedTokens(<namedRuleA's ATNState>, null)
等令牌类型相对应的IntervalSet
这似乎产生了正确的令牌集,并且似乎有意义,因为(我的理解是)TOKEN_1
及其转换是已知的并且在转换时间固定,即在.g4 - &gt;期间。 .java转换(或任何你的目标语言)。
我知道有时候转换是通过TOKEN_2
次调用动态确定的,例如当有语义谓词涉及时,我们假设我可以确保在ATNState
中不会使用语义谓词
我只是想知道其他人是否尝试过这种方法,如果可能存在我完全失踪的问题。