ANTLR:没有可行的替代错误

时间:2013-05-31 12:11:32

标签: antlr parser-generator

我有一个编写简单解析器生成器的任务,所以我编写了类似ANTLR的语法并尝试解析像“foo:bar;”这样的简单文件,但得到了以下输出:

[@0,0:2='foo',<1>,1:0]
[@1,3:3=':',<16>,1:3]
[@2,4:6='bar',<1>,1:4]
[@3,7:7=';',<18>,1:7]
[@4,8:7='<EOF>',<-1>,1:8]
line 1:0 no viable alternative at input 'foo'
(rule foo : bar ;)

我的语法看起来像

grammar parsGen;

gram : rule SEMICOLON (NEWLINE+ rule SEMICOLON)* ;

rule : lRule | pRule ;

lRule : LRULEID COLON lRule1 ;
lRule1 : (((LRULEID | STRING | SET) | LBRACE lRule1 PIPE lRule1 RBRACE) modificator? SPACE+)+ ;

pRule : PRULEID COLON pRule1 ;
pRule1 : (((LRULEID | PRULEID) | LBRACE lRule1 PIPE lRule1 RBRACE) modificator? SPACE+)+ ;

modificator : PLUS | ASTERISK | QUESTION ;

ID : LRULEID | PRULEID ;

LRULEID : UPPERLETTER (UPPERLETTER | LOWERLETTER | DIGIT)* ;
PRULEID : LOWERLETTER (UPPERLETTER | LOWERLETTER | DIGIT)* ;

STRING : ('\''.*?'\'') ;
SET : '\''.*?'\'..\''.*?'\'' ;

UPPERLETTER : [A-Z] ;
LOWERLETTER : [a-z] ;
DIGIT : [0-9] ;

NEWLINE : '\r\n'|'\n'|'\r' ;

PLUS : '+' ;
ASTERISK : '*' ;
QUESTION : '?' ;

LBRACE : '(' ;
RBRACE : ')' ;

SPACE : ' ' ;

COLON : ':' ;

PIPE : '|' ;

SEMICOLON : ';' ;

那么我哪里可以犯错?我试图搜索到处(谷歌,SO等)错误“没有可行的选择”,但它并没有真正帮助我。

2 个答案:

答案 0 :(得分:16)

ANTLR词法分析器在使用解析器之前完全分配明确的令牌类型。当多个令牌类型可以匹配令牌时,出现在语法中的第一个令牌就是使用的令牌。对于您的语法,令牌不能同时具有类型ID和类型LRULEID。由于输入foo与这两个词法分析器规则匹配,因此首先出现在语法中,因此您的标记为:IDCOLONID,{{1} },SEMICOLON

由于解析器中从未实际引用<EOF>标记,因此我建议进行以下更改之一。 这些选项中的将解决您所描述的问题,因此选择完全取决于最终语法的外观。

<强>前言

您需要将空格参考从ID更改为SPACE+,或者规则要求 SPACE*和{{1}之间至少有一个空格字符}}

选项1

完全删除bar词法分析器规则。

选项2

  1. ;更改为解析器规则,因此它不会尝试将令牌类型ID分配给您的所有标识符。

    ID
  2. 通过引用ID更新id : LRULEID | PRULEID; 规则。

    pRule1
  3. 不相关的附注

    如果删除idpRule1 : ((id | LBRACE lRule1 PIPE lRule1 RBRACE) modificator? SPACE+)+ ; 规则中最外面的+闭包,并且将它们添加到规则引用本身,您可能更容易阅读,就像这样。请注意,我更改了lRule引用,如前言中所述。

    pRule1

答案 1 :(得分:2)

也来自http://www.antlr.org/api/Java/org/antlr/v4/runtime/NoViableAltException.html

  

指示解析器无法根据其余输入来决定采用两个或更多路径中的哪一条。它跟踪有问题的输入的起始标记,并在发生错误时知道解析器在各个路径中的位置。

就我而言,我在解析出于调试目的之前正在调用 lexer.nextToken()。反过来,如果没有 lexer.reset(),则会在输入EOF错误时导致没有可行的选择。