我正在使用 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;
}
;
但野牛手册说
- 在确定性 GLR 操作期间,YYERROR 的效果与其在确定性解析器中的效果相同。
- 延迟动作的效果是类似的,但是错误的精确点是不确定的;相反,解析器恢复到确定性操作,选择一个未指定的堆栈以继续出现语法错误。
- 在非确定性解析期间的语义谓词(请参阅语义谓词)中,YYERROR 默默地修剪调用测试的解析。
我不确定我是否理解正确 - 我是这样理解的:
- 不适用于此处,因为 GLR 解析不是确定性的
- 是我在上面的代码中的方式,但不应该这样做,因为 YYERROR 杀死的路径是完全随机的(如果我错了,请纠正我)
- 不会解决我的问题,因为语义谓词(不是语义动作!)必须在规则的开头(如果我错了,请纠正我),此时来自 TOK_IDENTIFIER 令牌的 yylval 不可访问(纠正我如果我错了),所以我无法查看符号表来查找变量的类型。
有没有人遇到过这种情况?我对手册的理解有误吗?你会如何处理这个问题?对我来说,这个问题似乎很自然,我会假设人们经常遇到它,以至于野牛会有一个内置的解决方案......