在使用语义谓词时,我在错误恢复中面临着一个有点奇怪的行为。
我需要错误恢复(特别是单标记插入),因为我将解析的文本有许多“单标记缺失”错误。
让语法(非常简单,它匹配任意数量的“a”,用空格分隔,以分号结尾):
grammar AAAGrammar;
WS : ' '+ -> channel(HIDDEN);
A : 'A';
SEMICOLON : ';';
aaaaa :
a* ';'
;
a :
A
;
运行以下输入,得到的解析树为:
(aaaaa(Aa)(Aa)(Aa)(Aa)(Aa);)
;(aaaa(A)(A)(A)(A)(A)
)
(此命令在stderr上发出警告:line 1:5 missing’;'at')。现在对语法做了一个简单的更改,在“a”规则中引入了一个语义谓词(这一个无伤大雅,但我理解ANTLR4没有--也不应该--对此进行评估),以使其:
a :
{true}? A
;
在相同的输入上再次运行它:-“A A A A;”:(aaaa(A)(A)(A)(A)(A)(A)
;-“A A A A”:(aaaaa(aa)(aa)(aa)(aa))
(该命令还在stderr上发出警告:第1行:5 input''处没有可行的替代方案)。
所以语义谓词完全搞砸了标记注入。
这是意料之中的吗?
为什么?
是否有任何ANTLR4语法技巧可以在不删除sempred的情况下恢复错误?
--- withoutsempred.java 2015-05-04 09:39:22.644069398 -0300
+++ withsempred.java 2015-05-04 09:39:13.400046354 -0300
@@ -56,22 +56,24 @@
public final AaaaaContext aaaaa() throws RecognitionException {
AaaaaContext _localctx = new AaaaaContext(_ctx, getState());
enterRule(_localctx, 0, RULE_aaaaa);
- int _la;
try {
+ int _alt;
enterOuterAlt(_localctx, 1);
{
setState(7);
_errHandler.sync(this);
- _la = _input.LA(1);
- while (_la==A) {
- {
- {
- setState(4); a();
- }
+ _alt = getInterpreter().adaptivePredict(_input,0,_ctx);
+ while ( _alt!=2 && _alt!=-1 ) {
+ if ( _alt==1 ) {
+ {
+ {
+ setState(4); a();
+ }
+ }
}
setState(9);
_errHandler.sync(this);
- _la = _input.LA(1);
+ _alt = getInterpreter().adaptivePredict(_input,0,_ctx);
}
setState(10); match(SEMICOLON);
}
@@ -101,7 +103,9 @@
try {
enterOuterAlt(_localctx, 1);
{
- setState(12); match(A);
+ setState(12);
+ if (!( true )) throw new FailedPredicateException(this, " true ");
+ setState(13); match(A);
}
}
catch (RecognitionException re) {
@@ -115,12 +119,25 @@
return _localctx;
}
+ public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
+ switch (ruleIndex) {
+ case 1: return a_sempred((AContext)_localctx, predIndex);
+ }
+ return true;
+ }
+ private boolean a_sempred(AContext _localctx, int predIndex) {
+ switch (predIndex) {
+ case 0: return true ;
+ }
+ return true;
+ }
+
public static final String _serializedATN =
- "\3\uacf5\uee8c\u4f5d\u8b0d\u4a45\u78bd\u1b2f\u3378\3\5\21\4\2\t\2\4\3"+
- "\t\3\3\2\7\2\b\n\2\f\2\16\2\13\13\2\3\2\3\2\3\3\3\3\3\3\2\4\2\4\2\2\17"+
- "\2\t\3\2\2\2\4\16\3\2\2\2\6\b\5\4\3\2\7\6\3\2\2\2\b\13\3\2\2\2\t\7\3\2"+
- "\2\2\t\n\3\2\2\2\n\f\3\2\2\2\13\t\3\2\2\2\f\r\7\5\2\2\r\3\3\2\2\2\16\17"+
- "\7\4\2\2\17\5\3\2\2\2\3\t";
+ "\3\uacf5\uee8c\u4f5d\u8b0d\u4a45\u78bd\u1b2f\u3378\3\5\22\4\2\t\2\4\3"+
+ "\t\3\3\2\7\2\b\n\2\f\2\16\2\13\13\2\3\2\3\2\3\3\3\3\3\3\3\3\2\4\2\4\2"+
+ "\2\20\2\t\3\2\2\2\4\16\3\2\2\2\6\b\5\4\3\2\7\6\3\2\2\2\b\13\3\2\2\2\t"+
+ "\7\3\2\2\2\t\n\3\2\2\2\n\f\3\2\2\2\13\t\3\2\2\2\f\r\7\5\2\2\r\3\3\2\2"+
+ "\2\16\17\6\3\2\2\17\20\7\4\2\2\20\5\3\2\2\2\3\t";
public static final ATN _ATN =
ATNSimulator.deserialize(_serializedATN.toCharArray());
static {
假设输入“A A A A”(不带分号),不带语义谓词的版本为
while (_la==A) {
{
{
setState(4); a();
}
}
setState(9);
_errHandler.sync(this);
_la = _input.LA(1);
}
此块3次,然后继续到
setState(10); match(SEMICOLON);
匹配(分号)
插入缺少的标记。
现在请注意,带有语义谓词的版本去掉了_la=_input.la(1)
(lookahead),并切换到更高级的预测_alt=getInterpreter().adaptivePredict(_input,0,_ctx)
。
对于相同的输入,带有语义谓词的版本如下:
_alt = getInterpreter().adaptivePredict(_input,0,_ctx);
while ( _alt!=2 && _alt!=-1 ) {
if ( _alt==1 ) {
{
{
setState(4); a();
}
}
}
setState(9);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,0,_ctx);
}
这个块3次,但它并不例外地离开块。最后一个_alt=getInterpreter().adaptivepredict(_input,0,_ctx)
抛出org.antlr.v4.runtime.noviablealtexception
,完全跳过匹配(分号)
。
了解DefaultErrorStrategy采用了一种简单的方法来识别解析异常的规则和来源。
特别是,计算错误恢复例程范围内的谓词非常困难,因此不将其作为DefaultErrorStrategy处理的一部分进行。
考虑一下测试语法的以下变体:
aaaaa : a* SEMI EOF ;
a : ( { true }? B )? A ;
A : 'A';
B : 'B';
SEMI: ';';
WS : ' '+ -> channel(HIDDEN) ;
line 1:5 no viable alternative at input '<EOF>'
([] ([4] A) ([4] A) ([4] A))
当然,您可以扩展DefaultErrorStrategy,以解决特定于您的语法或比默认策略所能处理的更复杂的问题。
结合扩展DefaultErrorStrategy,考虑扩展RecognitionException以更好地理解起源异常发生的确切位置和方式-请注意getExpectedTokens()方法。
您可能会意识到,作为解析的一部分,处理所有可能的错误形式可能会变得复杂。通常,在解析器中自动更正错误是离散的、定义良好的并且容易识别的。否则,将它们视为语义错误,以便在分析阶段进行纠正。
我有一个非常简单的语法,如下所示: (我需要使用语义谓词,因为我需要解析关键字可以用作标识符的语言)。 参考:https://github.com/antlr/antlr4/blob/master/doc/predicates.md
我开始为我朋友的服务器创建自己的Discord bot,我一直在处理同样的问题。 我写了一个简单的东西,在我和机器人之间进行第一次交互: 这应该在控制台中写入在bot连接的discord服务器上发送的任何消息的内容(它只连接到我的测试服务器) 我不明白为什么这不起作用,所以我开始寻找它并使用“调试”事件。 当运行时,控制台告诉我: 然后它不断发送心跳并承认它,直到我停止它。 告诉我事情不对劲的是:
关于antlr4的几个问题使用了书中没有提到的lexer谓词,例如28730446使用了head(String),42058127使用了getCharPositionInLine(),23465358使用了_input.la(1)等。是否有可用的lexer谓词列表及其文档?
在ANTLR v3中,句法谓词可以用来解决例如悬空的else问题。ANTLR4似乎接受具有类似歧义的语法,但在解析过程中它会报告这些歧义(例如,“line 2:29 reportAmbiguity d=0(e):ambigalts={1,2},input=...”)。它生成一个解析树,尽管存在这些歧义(根据文档,通过选择第一个备选方案)。但如果我想让它选择其他选择,我能做什么呢?换句话说,我如何显