【发布时间】:2018-06-13 09:47:02
【问题描述】:
我正在使用 GNU bison 开发解析器,但我遇到了一个有趣的问题。我的实际问题有点不同,一般来说不太有趣,所以我会有点不同的说法,这样答案通常会更有用。
我需要根据表达式的类型来区分表达式,例如算术表达式和字符串表达式。它们的顶级非终结符有一些共同的祖先,比如
statement
: TOK_PRINT expression TOK_SEMICOLON {...}
;
expression
: arithmeticexpression {...}
| stringexpression {...}
;
现在我需要能够在两种表达式中都有变量
arithmeticexpression
: TOK_IDENTIFIER {...}
;
stringexpression
: TOK_IDENTIFIER {...}
;
(在 stringexpression 的情况下只允许使用 string 类型的变量,在算术表达式的情况下只允许使用 int 或 float 类型的变量) 但这显然会导致 R/R 冲突,这是语言所固有的——无法解决,因为语言是模棱两可的。
当然我可以淡化语言,这样只有一般的“表达式”对象在解析堆栈上传递,但是我必须在动作中做很多手动类型检查,我想避免。 此外,在我的实际用例中,通过语法到变量规则的路径是如此不同,以至于我不得不大量降低语言,以至于我会丢失很多语法规则(即丢失很多结构信息)并且需要将解析引擎手写到一些动作中。
我读过有关 GLR 解析的文章,听起来它可以解决我的问题。我正在考虑使用此功能:在对应变量类型错误的路径中使用上述语法和YYERROR。
arithmeticexpression
: TOK_IDENTIFIER {
if(!dynamic_cast<IntVariable*>(
symbol_table[*$<stringvalue>1]))
YYERROR;
}
;
stringexpression
: TOK_IDENTIFIER {
if(!dynamic_cast<StringVariable*>(
symbol_table[*$<stringvalue>1]))
YYERROR;
}
;
但是Bison Manual 说
- 在确定性 GLR 操作期间,YYERROR 的效果是相同的 作为它在确定性解析器中的效果。
- 延迟操作中的效果是相似的,但精确点 错误未定义;相反,解析器恢复为确定性 操作,选择一个未指定的堆栈继续 语法错误。
- 在语义谓词(参见语义谓词)中 非确定性解析,YYERROR 默默地修剪调用的解析 测试。
我不确定我是否理解正确 - 我是这样理解的:
- 不适用于此处,因为 GLR 解析不是确定性的
- 是我在上面的代码中的方式,但不应该这样做,因为 YYERROR 杀死的路径是完全随机的(如果我错了,请纠正我)
- 不会解决我的问题,因为semantic predicates(不是语义动作!)必须在规则的开头(如果我错了,请纠正我),此时yylval无法访问 TOK_IDENTIFIER 令牌(如果我错了,请纠正我),所以我无法查看符号表来查找变量的类型。
有没有人遇到过这种情况?我对手册的理解有误吗?你会如何处理这个问题?对我来说,这个问题似乎很自然,我会假设人们经常遇到它,以至于野牛会有一个内置的解决方案......
【问题讨论】: