7

我的任务是编写简单的解析器生成器,所以我编写了类似 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等)错误“没有可行的选择”,但它并没有真正帮助我。

4

2 回答 2

19

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

由于ID解析器中从未实际引用过令牌,因此我建议进行以下更改之一。这些选项中的任何一个都可以解决您所描述的问题,因此选择完全取决于您对最终语法外观的偏好。

前言

您需要将空格引用从 更改SPACE+SPACE*,否则该规则将要求bar和之间至少有一个空格字符;

选项1

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

选项 2

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

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

    pRule1 : ((id | LBRACE lRule1 PIPE lRule1 RBRACE) modificator? SPACE+)+ ;
    

不相关的旁注

如果您删除and规则中最外层的+闭包,而是将它们添加到规则引用本身,您的语法可能更容易阅读,就像这样。请注意,我按照前言中的描述更改了引用。lRulepRule1SPACE

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* ;
于 2013-05-31T14:36:47.437 回答
3

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

表示解析器无法根据剩余的输入决定采用两条或多条路径中的哪一条。它跟踪违规输入的起始标记,并且还知道当错误[发生]时解析器在各个路径中的位置。

就我而言,我在解析之前调用了 lexer.nextToken()以进行调试。反过来,没有lexer.reset()在输入 EOF错误时导致没有可行的替代方案。

于 2018-07-04T13:27:24.310 回答