Bison shift/reduce do 块冲突
Bison shift/reduce conflict for do block
我无法解决 shift/reduce 冲突。
我正在尝试编写 while 循环语法:
while expression do code() end
问题出在do关键字上
我的一个表达式是一个调用表达式,它也接受一个可选的回调块,例如:
function_call() do print("callback") end
这似乎又导致了 while 循环的 do 关键字中的 shift/reduce 冲突。
所以如果我这样做:
while call() do stuff() end
它反而试图将 do 与函数调用相匹配,并弄乱了整个解析。
Ruby 具有非常相似的语法,但它似乎正确地支持 while 循环而不是表达式的 do 关键字。在 Ruby 中,如果需要,您可以将回调放在括号中,这也是我想解决这个问题的理想方式:
while (call() do stuff() end) do more_stuff() end
我该如何克服这个问题?我在运算符优先级定义上搞砸了很多,但似乎没有任何效果,你会如何解决这个问题? ruby 是如何做到正确的?
这是重现问题的完整语法:
%token IDENT DO END WHILE
%%
program:
%empty
|
stmts
;
stmts:
stmt
|
stmts ';' stmt
|
stmts ';'
;
opt_stmts:
%empty
|
stmts
;
opt_semi:
%empty
|
';'
;
term:
';'
|
DO opt_semi
;
while_loop:
WHILE expr term opt_stmts END
|
WHILE term opt_stmts END
;
stmt:
expr
|
while_loop
;
expr:
IDENT
|
call
|
'(' expr ')'
;
do_block:
DO '|' args '|' opt_stmts END
|
DO opt_stmts END
;
call:
IDENT '(' args ')'
|
IDENT '(' args ')' do_block
|
IDENT do_block
;
args:
%empty
|
expr
|
args ',' expr
|
args ','
;
%%
旁注:我正在使用 Jison,它是 JavaScript 的野牛克隆,因为我正在为 JavaScript 编写编译器,但这应该不是问题,因为这是一个一般语法问题和我写的上面的最小片段也已通过原始 Bison 运行。
您已经确定了问题 -- 可能是 WHILE..DO 的一部分或可能是 call-DO 的一部分的 DO 的歧义。这特别困难,因为 DO 在这两种情况下都是可选的,但语法是这样的,一些 DO 可能只与 WHILE 或调用相关联,这取决于它们之后的各种事情,这意味着非歧义的情况不能无论如何都没有更多的前瞻性识别。它还遇到了一些 LALR 与 LR 问题,这也使它变得困难(并且也意味着你不能使用优先级来解决它。)
您可以通过重构语法来解决它,其方式与处理悬空 else 问题的方式大致相同——拆分 expr
规则并仅允许不以 call-DO 结尾的表达式在 WHILE 语句中。这样,WHILE 之后的 DO(而不是在括号中)将始终与 WHILE 相关联,而不是调用。它确实稍微改变了语言——带有可选 ';'
的 WHILE..DO 与中间的调用 DO(没有括号)将被拒绝为语法错误(第一个 DO 将与 WHILE 和第二个 DO 不能是 call-DO,因为 DO 后面紧跟着 ';'
.
对于您的示例,这意味着将 expr
规则拆分为 call-DO 大小写和所有其他表达式大小写:
expr: non_call_do_expr | call_withdo ;
non_call_do_expr: IDENT | call_nodo | '(' expr ')' ;
call_nodo: IDENT '(' args ')' ;
call_withdo:
IDENT '(' args ')' do_block |
IDENT do_block ;
如果您添加任何其他 expr
规则,则需要将它们添加到 non_call_do_expr
规则中 除非 它们(可以)以 call-DO 结束.在这种情况下,您需要针对这两种情况的两个版本,这与解决悬空 else 情况的语句所需要的完全一样。 while 然后变成:
while_loop:
WHILE non_call_do_expr term opt_stmts END |
WHILE term opt_stmts END ;
我无法解决 shift/reduce 冲突。
我正在尝试编写 while 循环语法:
while expression do code() end
问题出在do关键字上
我的一个表达式是一个调用表达式,它也接受一个可选的回调块,例如:
function_call() do print("callback") end
这似乎又导致了 while 循环的 do 关键字中的 shift/reduce 冲突。
所以如果我这样做:
while call() do stuff() end
它反而试图将 do 与函数调用相匹配,并弄乱了整个解析。
Ruby 具有非常相似的语法,但它似乎正确地支持 while 循环而不是表达式的 do 关键字。在 Ruby 中,如果需要,您可以将回调放在括号中,这也是我想解决这个问题的理想方式:
while (call() do stuff() end) do more_stuff() end
我该如何克服这个问题?我在运算符优先级定义上搞砸了很多,但似乎没有任何效果,你会如何解决这个问题? ruby 是如何做到正确的?
这是重现问题的完整语法:
%token IDENT DO END WHILE
%%
program:
%empty
|
stmts
;
stmts:
stmt
|
stmts ';' stmt
|
stmts ';'
;
opt_stmts:
%empty
|
stmts
;
opt_semi:
%empty
|
';'
;
term:
';'
|
DO opt_semi
;
while_loop:
WHILE expr term opt_stmts END
|
WHILE term opt_stmts END
;
stmt:
expr
|
while_loop
;
expr:
IDENT
|
call
|
'(' expr ')'
;
do_block:
DO '|' args '|' opt_stmts END
|
DO opt_stmts END
;
call:
IDENT '(' args ')'
|
IDENT '(' args ')' do_block
|
IDENT do_block
;
args:
%empty
|
expr
|
args ',' expr
|
args ','
;
%%
旁注:我正在使用 Jison,它是 JavaScript 的野牛克隆,因为我正在为 JavaScript 编写编译器,但这应该不是问题,因为这是一个一般语法问题和我写的上面的最小片段也已通过原始 Bison 运行。
您已经确定了问题 -- 可能是 WHILE..DO 的一部分或可能是 call-DO 的一部分的 DO 的歧义。这特别困难,因为 DO 在这两种情况下都是可选的,但语法是这样的,一些 DO 可能只与 WHILE 或调用相关联,这取决于它们之后的各种事情,这意味着非歧义的情况不能无论如何都没有更多的前瞻性识别。它还遇到了一些 LALR 与 LR 问题,这也使它变得困难(并且也意味着你不能使用优先级来解决它。)
您可以通过重构语法来解决它,其方式与处理悬空 else 问题的方式大致相同——拆分 expr
规则并仅允许不以 call-DO 结尾的表达式在 WHILE 语句中。这样,WHILE 之后的 DO(而不是在括号中)将始终与 WHILE 相关联,而不是调用。它确实稍微改变了语言——带有可选 ';'
的 WHILE..DO 与中间的调用 DO(没有括号)将被拒绝为语法错误(第一个 DO 将与 WHILE 和第二个 DO 不能是 call-DO,因为 DO 后面紧跟着 ';'
.
对于您的示例,这意味着将 expr
规则拆分为 call-DO 大小写和所有其他表达式大小写:
expr: non_call_do_expr | call_withdo ;
non_call_do_expr: IDENT | call_nodo | '(' expr ')' ;
call_nodo: IDENT '(' args ')' ;
call_withdo:
IDENT '(' args ')' do_block |
IDENT do_block ;
如果您添加任何其他 expr
规则,则需要将它们添加到 non_call_do_expr
规则中 除非 它们(可以)以 call-DO 结束.在这种情况下,您需要针对这两种情况的两个版本,这与解决悬空 else 情况的语句所需要的完全一样。 while 然后变成:
while_loop:
WHILE non_call_do_expr term opt_stmts END |
WHILE term opt_stmts END ;