制作C++解析器时的歧义解决



我为 C++17 编写了一个 LALR(1) 解析器。我发现了 156 个歧义,其中一些我可以按照标准解决,但其他的我不能。

例如:在解析"运算符+ <..."时,当遇到小于时,会发生 Shift-Reduce 冲突:

我们可以将其解析为:

(1)

template-id -> operator-function-id · <......>

或:

(二)

非限定 ID -> 运算符函数 ID ·其中(1)需要转移,但(2)需要减少。

但是,该标准具有:

在名称查找 (3.4) 发现名称是模板名称或运算符函数 ID 或文本运算符 ID 引用一组重载函数(其任何成员是函数模板)后,如果后跟<,则<始终被视为模板参数列表的分隔符,而不是小于运算符。解析模板参数列表时,第一个非嵌套的>137 将作为结束分隔符,而不是大于运算符。

所以我们选择转移。

不幸的是,有很多模棱两可的地方我找不到解决方案。在这里我列出了其中的一些(其中一些可以清楚地做出选择,但我只是找不到证据):

  1. 标准中是否有某些部分表明"移位"是发生歧义时的默认选择?

说明符

(1)当解析 noptr-declar 并遇到左 paren 时,我应该根据以下方式减少它:

PTR-Declarator -> noptr-declarator ·

或将左边的参数移位以满足:

声明符 -> noptr-声明符 · 参数和限定符

参数和限定符 -> · 左参数声明子句 右括号......

(2)当解析声明符ID并遇到左括号时,我应该根据以下方式减少它:

noptr-declarator -> declarator-id · noptr-declarator -> noptr-declarator · \left-bracket ?constant-expression \right-bracket ?attribute-specifier-seq

或向左移动方块以满足:

noptr-declarator -> declarator-id ·attribute-specifier-seq

(属性说明符序列是 [[.......]])

跟进 TonyD 的评论: 参见为什么不能用 LR(1) 解析器解析C++?

在某些地方,您基本上必须保留解析产生的歧义,并通过执行名称解析来解决它,或者等效地,您必须将名称解析纠结到解析过程中。 无论哪种情况,您都必须解释标准以确定应该如何解决歧义,是的,这是一项非常艰巨的任务。

然后你就会发现编译器到底做了什么;GCC和MS在语法和语义解释方面都有很多扩展和标准的变化(这些程序在不同的编译器下产生不同的结果)。 最后,您可以找到系统头文件中的可憎之物;这些是编译器人员为了方便他们的生活而添加的黑客,并且记录得非常糟糕,如果有的话。

C++是图灵完备的解析。

这里非常相关的帖子。

最新更新