JCL*_*CLL 84 c compiler-construction parsing compilation
似乎GCC和LLVM-Clang使用手写递归下降解析器,而不是机器生成,基于Bison-Flex,自下而上解析.
请问有人请确认是这种情况吗?如果是这样,为什么主流编译器框架使用手写解析器?
更新:这里有关此主题的有趣博客
Ira*_*ter 98
有一个民间定理说C很难解析,而C++基本上是不可能的.
事实并非如此.
真实的是,使用LALR(1)解析器很难解析C和C++,而不会破解解析机制并纠缠符号表数据.GCC实际上用来解析它们,使用YACC和这样的额外hackery,是的它很难看. 现在GCC使用手写解析器,但仍然使用符号表hackery.Clang人从未尝试使用自动解析器生成器; AFAIK的Clang解析器一直是手工编码的递归下降.
真实的是,C和C++使用更强大的自动生成的解析器(例如GLR解析器)进行解析相对容易,并且您不需要任何黑客攻击.所述埃尔莎 C++解析器是这样的一个例子.我们的C++前端是另一个(就像我们所有的"编译器"前端一样,GLR是非常精彩的解析技术).
我们的C++前端没有GCC那么快,而且肯定比Elsa慢; 我们很少精力调整它,因为我们还有其他更紧迫的问题(尽管如此,它已被用于数百万行C++代码).Elsa可能比GCC慢,因为它更通用.鉴于目前的处理器速度,这些差异在实践中可能并不重要.
但是今天广泛分布的"真正的编译器"源于10或20年前或更长时间的编译器.效率低下然后更重要,没有人听说过GLR解析器,所以人们做了他们知道怎么做的事情.Clang肯定是最近的,但民间定理长期保持其"说服力".
你不必再以那种方式去做了.您可以非常合理地使用GLR和其他此类解析器作为前端,并提高编译器的可维护性.
什么是真实的,是让符合您的睦邻友好编译器的行为语法是很难的.虽然几乎所有C++编译器都实现了(大多数)原始标准,但它们也往往有很多暗角扩展,例如MS编译器中的DLL规范等.如果你有一个强大的解析引擎,你可以花时间试图获得与现实相匹配的最终语法,而不是试图弯曲语法以匹配解析器生成器的限制.
编辑2012年11月:自写这个答案以来,我们改进了C++前端以处理完整的C++ 11,包括ANSI,GNU和MS变体方言.虽然有很多额外的东西,但我们不必改变我们的解析引擎; 我们刚刚修改了语法规则.我们确实需要改变语义分析; C++ 11在语义上非常复杂,这项工作淹没了使解析器运行的努力.
编辑2015年2月:...现在处理完整的C++ 14.(请参阅c ++代码中的人类可读AST,以获取一小段代码的GLR解析,以及C++臭名昭着的"最令人烦恼的解析").
编辑2017年4月:现在处理(草稿)C++ 17.
Mat*_*ery 73
是:
GCC仙界使用YACC(野牛)解析一次,但它是在3.x系列某一位置被手写的递归下降解析器代替:看http://gcc.gnu.org/wiki/New_C_Parser为指向相关修补程序提交的链接.
Clang还使用手写的递归下降解析器:请参阅http://clang.llvm.org/features.html末尾附近的"C,Objective C,C++和Objective C++的单一统一解析器"部分.
小智 30
Clang的解析器是一个手写的递归下降解析器,以及其他几个开源和商业C和C++前端.
Clang使用递归下降解析器有以下几个原因:
总的来说,对于C++编译器来说,它并不重要:C++的解析部分非常重要,但它仍然是更容易的部分之一,因此保持简单是值得的.语义分析 - 特别是名称查找,初始化,重载解析和模板实例化 - 比解析更复杂.如果你想要证明,请查看代码的分布并提交Clang的"Sema"组件(用于语义分析)与其"Parse"组件(用于解析).
gcc的解析器是手写的..对于铿锵,我怀疑是一样的.这可能有以下几个原因:
这可能不是"不是在这里发明"综合症的情况,而是更多的是"没有任何针对我们需要的东西进行优化,所以我们写了自己的".
那里有奇怪的答案!
C/C++语法不是上下文无关的.由于Foo*bar,它们对上下文敏感; 歧义.我们必须构建一个typedef列表来知道Foo是否是一个类型.
艾拉巴克斯特:我没有看到你的GLR的重点.为什么要构建一个包含歧义的解析树.解析意味着解决歧义,构建语法树.你可以在第二遍中解决这些歧义,所以这并不是那么难看.对我来说它更难看......
Yacc是LR(1)解析器生成器(或LALR(1)),但它可以很容易地修改为上下文敏感.并没有什么难看的.创建Yacc/Bison是为了帮助解析C语言,所以可能它不是生成C语法分析器最丑陋的工具......
在GCC 3.x之前,C语言分析器由yacc/bison生成,在解析期间构建了typedefs表.使用"in parse"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 noob的"易于阅读"代码.
他们为什么从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解析器开发的下一步?
| 归档时间: |
|
| 查看次数: |
28184 次 |
| 最近记录: |