在使用语义谓词时,我在错误恢复方面遇到了一些奇怪的行为。
对于将要解析的文本,我需要错误恢复(特别是单个令牌插入),有许多“单个缺失令牌”错误。
我还需要语义谓词,因为对于像ANTLR4:匹配所有输入选项一次这样的东西(第二种选择)。
但这两者似乎不太好(我以前见过这个,并请求帮助:ANTLR4 DefaultErrorStrategy未能注入丢失的令牌;然后我找到了答案;现在我没有了)。
让语法(如此简单,它匹配任意数量的"A",用空格分隔,以分号结尾):
grammar AAAGrammar;
WS : ' '+ -> channel(HIDDEN);
A : 'A';
SEMICOLON : ';';
aaaaa :
a* ';'
;
a :
A
;
运行在以下输入上的结果解析树是:
(aaaaa (a A) (a A) (a A) ;)
;(aaaaa (a A) (a A) (a A) <missing ';'>)
(这个在stderr上发出警告:第1行:5缺失';‘at '')。这正是我所期望的,也正是我想要的(第二个输入的缺失分号被正确地注入了)。
现在对语法进行简单的修改,在"a“规则中引入一个语义谓词(这个谓词是无害的,但我知道ANTLR4不应该--也不应该)在”a“规则中这样做:
a :
{true}? A
;
在相同的输入上再次运行它:- "A A;":(aaaaa (a A) (a A) (a A) ;)
;- "A A“:(aaaaa (a A) (a A) (a A))
(这个也在stderr上发出警告:第1行:5在输入‘没有可行的替代方案’)。
,所以语义谓词完全混淆了缺少的令牌注入.
这是意料之中的吗?
为什么?
是否有任何ANTLR4语法技巧来获得错误恢复,而不删除sempred?
编辑:(回复@CoronA评论)
在这里,生成的解析器之间的diff -u
(没有语义谓词和带有语义谓词):
--- 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“(没有分号),没有语义谓词的版本
while (_la==A) {
{
{
setState(4); a();
}
}
setState(9);
_errHandler.sync(this);
_la = _input.LA(1);
}
这个街区3次,然后继续到
setState(10); match(SEMICOLON);
match(SEMICOLON)
注入一个丢失的令牌。
现在请注意,带有语义谓词的版本摆脱了_la = _input.LA(1)
(展望),转而使用_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);
}
这个街区三次,但它不例外地离开块。最后一个_alt = getInterpreter().adaptivePredict(_input,0,_ctx)
抛出org.antlr.v4.runtime.NoViableAltException
,完全跳过match(SEMICOLON)
。
发布于 2015-05-06 03:52:30
请理解,DefaultErrorStrategy在识别解析异常的规则和来源方面采取了一种天真的方法。
特别是,在错误恢复例程范围内计算谓词非常困难,因此不能作为DefaultErrorStrategy处理的一部分来完成。
考虑一下测试语法的这个变体:
aaaaa : a* SEMI EOF ;
a : ( { true }? B )? A ;
A : 'A';
B : 'B';
SEMI: ';';
WS : ' '+ -> channel(HIDDEN) ;
在输入AAA
上,打印的错误消息是
line 1:5 no viable alternative at input '<EOF>'
([] ([4] A) ([4] A) ([4] A))
即使谓词B
是可选的,也没有简单的方法可以确保谓词是无关的。并且没有简单的方法可以在错误恢复操作的上下文中重新运行谓词来计算其输出。因此,这里唯一有效的运行时结论是,错误不能仅在一个规则(或子规则)中被识别为存在。
当然,您可以扩展DefaultErrorStrategy以解决特定于您的语法或比默认策略所能处理的更复杂的问题。
与扩展DefaultErrorStrategy相结合,考虑扩展RecognitionException以更好地理解原始异常发生的确切位置和方式--注意getExpectedTokens()方法。
正如您可能意识到的,将所有可能的错误表单作为解析的一部分来处理可能会变得非常复杂。通常,当错误是离散的、定义良好且易于识别时,解析器内的自动校正是合适的。否则,将它们视为在分析阶段要纠正的语义错误。
https://stackoverflow.com/questions/30021472
复制相似问题