制作C++解析器时的歧义解决
Ambiguity resolution when making a C++ parser
我为 C++17 编写了一个 LALR(1) 解析器。我发现了 156 个歧义,其中一些我可以按照标准解决,但其他的我不能。
例如:在解析"运算符+ <..."时,当遇到小于时,会发生 Shift-Reduce 冲突:
我们可以将其解析为:
(1)
template-id -> operator-function-id · <......>
或:
(二)
非限定 ID -> 运算符函数 ID ·其中(1)需要转移,但(2)需要减少。
但是,该标准具有:
在名称查找 (3.4) 发现名称是模板名称或运算符函数 ID 或文本运算符 ID 引用一组重载函数(其任何成员是函数模板)后,如果后跟<,则<始终被视为模板参数列表的分隔符,而不是小于运算符。解析模板参数列表时,第一个非嵌套的>137 将作为结束分隔符,而不是大于运算符。
所以我们选择转移。
不幸的是,有很多模棱两可的地方我找不到解决方案。在这里我列出了其中的一些(其中一些可以清楚地做出选择,但我只是找不到证据):
- 标准中是否有某些部分表明"移位"是发生歧义时的默认选择?
说明符
(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++是图灵完备的解析。
这里非常相关的帖子。
- 歧义语法是如何解决的
- 使用enable_if解决多重继承歧义
- 如何解决传递给boost线程的函数中的歧义
- 解决虚拟方法的歧义继承的两种方法
- 解决歧义的方法
- 如何解决以下代码中的函数重载歧义
- 使用声明解决歧义
- 解决 CRTP 函数重载歧义问题
- 如何解决钻石问题的这种歧义
- 在size_t的声明中解决歧义
- LR解析器如何解决歧义和冲突
- 概念没有按预期解决歧义。代码示例有什么问题?
- 引用如何解决多重继承中的歧义
- 解决C++构造函数和调用歧义
- 如何解决嵌入结构内嵌入的操作员中的歧义
- 为什么这种ADL解决方案歧义
- 在这里的派生类中,如何解决歧义
- 解决隐藏歧义的不明确用户定义转换
- 制作C++解析器时的歧义解决
- 理解歧义解决的示例代码