Antlr4:另一个“没有可行的替代错误”

时间:2017-01-04 16:55:18

标签: parsing expression antlr grammar antlr4

我已经检查了围绕这个问题的类似问题,但似乎没有一个解决我的问题版本。 我最近刚刚开始使用Antlr4,一直都很顺利,直到遇到这个特定的障碍。

我的语法是一个基本的数学表达式语法,但出于某种原因,我注意到生成的解析器(?)无法从paser-rule“等于”走到paser-rule“expr”,以达到lexer-rule“ NAME”。

grammar MathCraze;

NUM : [0-9]+ ('.' [0-9]+)?;
WS  : [ \t]+ -> skip;
NL  : '\r'? '\n' -> skip;
NAME: [a-zA-Z_][a-zA-Z_0-9]*;
ADD: '+';
SUB : '-';
MUL : '*';
DIV : '/';
POW : '^';

equal
    : add # add1
    | NAME '=' equal # assign
    ;
add
    : mul # mul1
    | add op=('+'|'-') mul # addSub
    ;
mul
    : exponent # power1
    | mul op=('*'|'/') exponent # mulDiv
    ;
exponent
    : expr # expr1
    | expr '^' exponent # power
    ;
expr
    : NUM # num
    | NAME # name
    | '(' add ')' # parens
    ;

如果我传递一个单词作为输入,就像“变量”一样,解析器抛出上面的错误,但是如果我传递一个数字作为输入(比如“78”),解析器成功地遍历树(即,从规则“等于”“expr”)。

equal                 equal
 |                     |
add                   add
 |                     |
mul                   mul
 |                     |
exponent              exponent
 |                     |
expr                  expr
 |                     |
NUM                   NAME
 |                     | 
"78" # No Error      "variable" # Error! Tree walk doesn't reach here. 

我已经检查了我所知道的各种歧义,所以我可能在这里遗漏了一些东西。

我正在使用Antlr5.6,如果这个问题得到解决,我将不胜感激。提前谢谢。

2 个答案:

答案 0 :(得分:1)

虽然我无法解答您的问题,为什么解析器无法在NAME中找到expr我想指出使用Antlr4您可以直接使用在你的规则规范中左递归,这使你的语法更紧凑,提高了可读性 考虑到这一点,你的语法可以改写为

math:
    assignment
    | expression
;

    assignment:
        ID '=' (assignment | expression)
    ;

    expression:
        expression '^' expression
        | expression ('*' | '/') expression
        | expression ('+' | '-') expression
        | NAME
        | NUM
    ;

那个语法很快就会NAME作为expression的一部分,所以我想这会解决你的问题。

如果您真的对它为什么没有使用您的语法感兴趣,那么我首先要检查词法分析器是否已将输入与预期的令牌相匹配。之后我会看一下解析树,看看解析器对给定的令牌序列做了什么,然后尝试手动编码解析你的语法,在此期间你应该能够找到解析器所做的点。不同于你期望它做的事情。

答案 1 :(得分:1)

您的表达式层次结构是我们在手工或ANTLR v3中编写的解析器中使用的样式,从低优先级到高优先级。

正如Raven所说,ANTLR 4更强大。请注意电源规则中的<assoc = right>规范,该规范通常是右关联的。

grammar Question;

question
    :   line+ EOF
    ;

line
    :   expr   NL
    |   assign NL
    ;

assign
    :   NAME '=' expr                 # assignSingle
    |   NAME '=' assign               # assignMulti
    ;

expr // from high to low precedence
    :   <assoc = right> expr '^' expr # power
    |   expr op=( '*' | '/' ) expr    # mulDiv
    |   expr op=( '+' | '-' ) expr    # addSub
    |   '(' expr ')'                  # parens
    |   atom_r                        # atom
    ;

atom_r
    : NUM
    | NAME
    ;

NAME: [a-zA-Z_][a-zA-Z_0-9]*;
NUM : [0-9]+ ('.' [0-9]+)?;
WS  : [ \t]+  -> skip;
NL  : [\r\n]+ ;

使用-gui选项运行以查看解析树:

$ echo $CLASSPATH
.:/usr/local/lib/antlr-4.6-complete.jar
$ alias grun
alias grun='java org.antlr.v4.gui.TestRig'
$ grun Question question -gui data.txt

和此data.txt文件:

variable
78
a + b * c
a * b + c
a = 8 + (6 * 9)
a ^ b
a ^ b ^ c
7 * 2 ^ 5
a = b = c = 88

<强>加

使用原始语法并从equal规则开始,我有以下错误:

$ grun Q2 equal -tokens data.txt
[@0,0:7='variable',<NAME>,1:0]
[@1,9:10='78',<NUM>,2:0]
...
[@41,89:88='<EOF>',<EOF>,10:0]
line 2:0 no viable alternative at input 'variable78'

如果我从规则expr开始,则没有错误:

$ grun Q2 expr -tokens data.txt
[@0,0:7='variable',<NAME>,1:0]
...
[@41,89:88='<EOF>',<EOF>,10:0]
$ 

使用grun选项运行-gui,您将看到不同之处: 使用expr运行时,输入标记variable将在NAME中捕获,规则expr已满足并终止; 与equal一起运行它都是错误的。解析器尝试第一个替代等于 - &gt;添加 - &gt; mul - &gt;指数 - &gt; expr - &gt; NAME =&gt;好。它使用令牌variable并尝试使用下一个令牌78执行某些操作。它在每个规则中回滚,看它是否可以使用规则的alt做某事,但每个alt都需要一个运算符。因此,它到达equal并再次使用令牌variable启动,这次使用alt | NAME '='NAME使用令牌,然后规则需要'=',但输入为78并且不满足它。由于没有其他选择,它表示没有可行的选择。

$ grun Q2 equal -tokens data.txt
[@0,0:7='variable',<NAME>,1:0]
[@1,8:7='<EOF>',<EOF>,1:8]
line 1:8 no viable alternative at input 'variable'

如果variable是唯一的标记,同样的推理:第一个替代等于 - &gt;添加 - &gt; mul - &gt;指数 - &gt; expr - &gt; NAME =&gt;好的,消耗variable,返回equal,尝试需要'='的alt,但输入是EOF。这就是为什么它说没有可行的选择。

$ grun Q2 equal -tokens data.txt
[@0,0:1='78',<NUM>,1:0]
[@1,2:1='<EOF>',<EOF>,1:2]

如果78是唯一的令牌,请执行相同的推理:首先是替代等号 - &gt;添加 - &gt; mul - &gt;指数 - &gt; expr - &gt; NUM =&gt;好的,消耗78,返回equal。替代方案不是一种选择。满意吗?哎呀,EOF怎么样。

现在让我们为equal添加NUM替代:

equal
    : add # add1
    | NAME '=' equal # assign
    | NUM  '=' equal # assignNum
    ;

$ grun Q2 equal -tokens data.txt
[@0,0:1='78',<NUM>,1:0]
[@1,2:1='<EOF>',<EOF>,1:2]
line 1:2 no viable alternative at input '78'

第一种替代方法 - &gt;添加 - &gt; mul - &gt;指数 - &gt; expr - &gt; NUM =&gt;好的,消耗78,返回equal。现在还有一个替代NUM的alt,再次启动,这次使用alt | NUM '='NUM使用令牌78, 然后解析器需要'=',但输入是EOF,因此是消息。

现在让我们使用EOF添加一个新规则,让我们运行所有语法:

all : equal EOF ;

$ grun Q2 all -tokens data.txt
[@0,0:1='78',<NUM>,1:0]
[@1,2:1='<EOF>',<EOF>,1:2]
$ grun Q2 all -tokens data.txt
[@0,0:7='variable',<NAME>,1:0]
[@1,8:7='<EOF>',<EOF>,1:8]

输入对应于语法,并且没有更多消息。