看起来GCC和LLVM-Clang正在使用手写递归下降解析器,而不是基于Bison-Flex的机器生成自下而上分析。
请问这里是否有人能够确认这一点?如果是这样的话,为什么主流编译器框架使用手写解析器呢?
看起来GCC和LLVM-Clang正在使用手写递归下降解析器,而不是基于Bison-Flex的机器生成自下而上分析。
请问这里是否有人能够确认这一点?如果是这样的话,为什么主流编译器框架使用手写解析器呢?
编辑于2015年2月:...现在支持完整的C++14。(有关从C++代码获取人类可读的AST,请参见get human readable AST from c++ code,以及C++的臭名昭著的“最令人烦恼的解析”)。
编辑于2017年4月:现在支持(草案)C++17。
foo * bar;
的歧义? - Martin是的:
GCC曾经使用过yacc(bison)解析器,但在3.x系列的某个时候被手写的递归下降解析器替换了:请参见http://gcc.gnu.org/wiki/New_C_Parser以获取相关补丁提交的链接。
Clang也使用手写的递归下降解析器:请参见http://clang.llvm.org/features.html末尾的“一个统一的解析器用于C、Objective C、C++和Objective C++”部分。
Clang的解析器是手写的递归下降解析器,和其他几个开源和商业的C和C ++前端一样。
Clang使用递归下降解析器的原因有几个:
总体而言,对于C ++编译器而言,这并不太重要:C ++的解析部分是非常复杂的,但它仍然是比较容易的部分,因此保持简单性很重要。语义分析-特别是名称查找、初始化、重载决议和模板实例化-比解析复杂几个数量级。如果您想证明,请去查看Clang的“Sema”组件(用于语义分析)和其“Parse”组件(用于解析)的代码和提交的分布情况。
这里的答案很奇怪!
C/C++语法不是上下文无关的。它们是上下文敏感的,由于Foo * bar;的二义性。我们必须建立一个typedef列表来确定Foo是否为类型。
Ira Baxter:我不明白你的GLR的意义所在。为什么要构建包含歧义的解析树。解析意味着解决歧义,建立语法树。您可以在第二次通过中解决这些歧义,因此这并不比较丑陋。对我来说,它更加丑陋……
Yacc是一个LR(1)解析生成器(或LALR(1)),但可以轻松地修改为上下文敏感。而且这并不难看。 Yacc / Bison已被创建以帮助解析C语言,因此可能不是最丑陋的用于生成C解析器的工具...
直到GCC 3.x,C解析器是由yacc / bison生成的,在解析期间构建typedefs表。通过“解析”typedefs表构建,C语法变得在局部上下文无关,并且还可以“在局部上下文LR(1)”。
现在,在Gcc 4.x中,它是一个递归下降解析器。它与Gcc 3.x中完全相同的解析器,仍然是LR(1),并具有相同的语法规则。区别在于yacc解析器已经手动重新编写,shift / reduce现在隐藏在调用堆栈中,并且没有“state454:if(nextsym =='(')goto state398”,如在gcc 3.x yacc的解析器中,因此更容易进行修补,处理错误并打印更好的消息,并在解析期间执行一些下一个编译步骤。代价是对于gcc新手来说代码不那么“易读”。
为什么他们从yacc转换到递归下降?因为必须避免使用yacc来解析C ++,并且因为GCC梦想成为多语言编译器,即在它可以编译的不同语言之间共享最大的代码。这就是为什么C ++和C解析器以相同的方式编写的原因。
C ++比C更难解析,因为它不是"局部" LR(1)如C,甚至不是LR(k)。
看一下func<4 > 2>
, 这是一个使用4 > 2实例化的模板函数,即func<4 > 2>
必须读作func<1>
。这绝对不是LR(1)。现在考虑一下func<4 > 2 > 1 > 3 > 3 > 8 > 9 > 8 > 7 > 8>
。这就是递归下降可以轻松解决歧义的地方,代价是需要多调用一些函数(parse_template_parameter 是有歧义的解析函数。如果parse_template_parameter(17tokens)失败,则尝试再次运行parse_template_parameter(15tokens),parse_template_parameter(13tokens)... 直到成功为止)。我不知道为什么不能将递归子语法添加到yacc/bison中,也许这将成为gcc/GNU解析器开发的下一步?func<4 > 2>
不是 func<1>
。这段代码无法编译。第一个 >
关闭了模板。 - Phil1970这可能不是“非此即彼”综合征的情况,而更多地是“没有针对我们所需进行优化的东西,所以我们编写了自己的代码”。
特别是Bison,我认为它无法处理语法而不会有一些歧义,并在稍后进行第二次处理。
我知道Haskell的Happy允许使用单调(即状态相关)解析器来解决C语法的特定问题,但我不知道有哪些C解析器生成器允许用户提供状态单子。
理论上,手写解析器的错误恢复将是一个优点,但我对GCC / Clang的经验是错误消息并不特别好。
至于性能-一些声明似乎没有依据。使用解析器生成器生成大型状态机应该会产生O(n)
的结果,我怀疑解析不是很多工具中的瓶颈。