0

我正在为某种查询语言编写一个 bison+flex 解析器,我需要向它添加一个 C 风格的强制转换运算符。这是代码的相关部分:

%token <characterToken>  Identifier
%token <commandToken>    LRPAR RRPAR

%type <characterToken>   typename
%type <operationValue>   generalExp castExp variable

%%
generalExp: variable
      | LRPAR generalExp RRPAR
       { /* some code here */ }
      | castExp
      ;

castExp: LRPAR typename RRPAR generalExp
   { /* some code here */ }
   ;

variable: Identifier 
    { /* some code here*/ };
typename: Identifier;
%%

这里的问题是(typename)in与incastExp发生冲突并产生 reduce/reduce 冲突:(variable)generalExp

test.yy: conflicts: 1 reduce/reduce
test.yy:23.11-20: warning: rule useless in parser due to conflicts: typename: Identifier

可能的解决方案是在相应的 lex 文件中列出所有有效的类型名(如 long、int、char),但是我也需要支持使用定义的类型。

UPD:另一种解决方案是使用野牛 GLR 解析器,由于性能下降,我不想要它。

bison -v输出在这里

4

3 回答 3

2

是的,C强制转换语法选择得非常糟糕。如果不知道强制转换中的内容是类型还是值,就不可能正确解析。以下是 C 中的一些情况:

(f)*a    // cast the value pointed to by a to type f, or multiply f by a?
(f)(a+b) // cast a+b to type f or call the function f with argument a+b?

我发现处理这个问题的最简单方法是让词法分析器查询符号表,以便它可以为类型名和标识符返回不同的标记类型。(C 语法的工作方式非常好。)在词法分析器和解析器之间共享符号表有点难看,特别是如果您允许范围类型定义,但可以这样做。为了正确进行词法分析,词法分析器唯一需要知道的是符号是否是类型的名称;您可能可以假设符号不是类型,除非它已明确声明为类型,因此不必严格要求在使用之前声明所有符号。(例如,在 C++ 中,非类型类成员不必在使用前声明,这仍然可以工作。)

说了这么多,我强烈建议你认真考虑使用不同的语法进行强制转换,如果可能的话。除了对解析器来说是一种痛苦之外,它们对于人类读者来说也可能是令人讨厌的模棱两可(参见上面的示例),并且强制转换不是(或不应该)如此普遍以至于它需要缩写的语法。

C++ 风格的强制转换 -- cast<TYPE>(value)-- 遭受将比较运算符作为括号回收而引入的歧义,但如果使用尖括号的操作是一组固定的关键字,则可以容忍。(否则,您将创建另一个上下文,其中词法分析器需要区分名称类型。)

就个人而言,特别是对于查询语言,我会使用类似的东西

value as type

如在

(3 + 4) as double

这是(IMO)更具可读性和更容易解析而不会更难输入。

于 2014-05-09T20:01:11.293 回答
0

最好的猜测——你没有显示的语法的其他部分允许两个连续generalExp的,它们之间没有标记。因此,当您有类似 的输入时( Identifier ) expression,它可能是一个强制转换或两个连续generalExp的 s。

继续的方法与所有冲突相同——运行 bison 并-v选择获取一个文件,该.output文件显示所有状态以及哪些状态在哪些符号上有哪些冲突。一旦你知道了这一点,你就有机会弄清楚可以做什么。

于 2014-05-09T20:00:53.587 回答
0

我有类似的问题 - 以下规则的冲突:

expr: expr "+" expr
expr: "(" type ")" expr

我通过设置运算符的优先级来解决它:

%precedence "+"
%precedence ")"

这为 设置了更高的优先级")",因此将首选第二条规则。

于 2015-12-02T13:13:19.610 回答