使用flex / yacc编写C ++编译器需要多长时间?

时间:2009-12-25 17:56:44

标签: c++ compiler-construction bison yacc flex-lexer

使用lex / yacc编写C ++编译器需要多长时间?

我可以从哪里开始使用它?

13 个答案:

答案 0 :(得分:21)

bison / yacc解析器无法解析许多解析规则(例如,在某些情况下区分声明和函数调用)。另外,有时令牌的解释需要来自解析器的输入,特别是在C ++ 0x中。例如,字符序列>>的处理关键取决于解析上下文。

这两个工具是解析C ++的非常糟糕的选择,你必须加入许多特殊情况,这些特殊情况逃脱了这些工具依赖的基本框架,以便正确解析C ++。这需要很长时间,即使这样你的解析器也可能会有奇怪的错误。

yacc和bison是LALR(1)解析器生成器,它们不够复杂,不能有效地处理C ++。正如其他人所指出的那样,大多数C ++编译器现在使用recursive descent解析器,其他几个答案指出了编写自己的解决方案。

C ++模板不适合处理字符串,甚至是常量字符串(尽管这可能在C ++ 0x中修复,我没有仔细研究过),但是如果它们是,你可以很容易地写一个递归下降解析器C ++模板语言。我发现这很有趣。

答案 1 :(得分:10)

可能需要,您可能会在此过程中切换到其他一些解析器生成器。

解析C ++非常容易出错。语法不完全是LR可解析的,因为许多部分都是上下文敏感的。你将无法在flex / yacc中正常工作,或者至少它实现起来真的很尴尬。我知道只有两个前端正确。您最好的选择是使用其中之一并专注于编写后端。无论如何,这就是有趣的东西: - )。

现有C ++前端:

  1. EDG front-end 在其编译器中被大多数商业供应商(IntelPortland Group等)使用。它costs money,但它非常彻底。人们为此付出了巨大的代价,因为他们不想处理编写自己的C ++解析器的痛苦。

  2. GCC的C ++前端对于生产代码足够透彻,但您必须弄清楚如何将其集成到您的项目中。我认为将它与GCC分开是相当复杂的。这也是GPL,但我不确定这对你来说是否有问题。您可以通过gcc_xml在项目中使用GCC前端,但这只会为您提供类,函数,命名空间和typedef的XML。它不会为您提供代码的语法树。

  3. 另一种可能性是使用 clang ,但他们的C ++支持目前还不稳定。很高兴看到他们得到了所有的错误,但如果你看看他们的C++ status page,你会注意到有不止一些测试用例仍然存在。注意 - 铿锵是一个很大的项目。如果要花费这些时间来实现C ++前端,那么你需要更长的时间。

  4. 其他人提到 ANTLR ,并且有可用的C ++语法,但我持怀疑态度。我没有听说过任何主要编译器中使用的ANTLR前端,但我确实认为它在NetBeans IDE中使用过。它可能适合IDE,但我怀疑你能否在生产代码上使用它。

答案 2 :(得分:10)

听起来你是解析/编译器创建的新手。如果是这种情况,我强烈建议从C ++开始。它是一种语言的怪物。

要么发明自己的琐碎玩具语言,要么做一些更小更简单的事情。我看到了一个lua解析器,其中语法定义大约有一页长。作为一个起点,这将更加合理。

答案 3 :(得分:6)

很长一段时间,而lex和yacc将无济于事

如果您具备为这么大的语言编写编译器的技能,那么您将不需要lex和yacc为您提供的少量帮助。事实上,虽然lex是可以的,但是使用yacc可能需要更长的时间,因为它对C或C ++来说并不是非常强大,并且你最终可以花费更多的时间来使它正常工作而不是只需编写一个递归血统解析器。

我认为lex和yacc最适合用于简单的语法,或者当需要额外的努力来获得一个可读的语法文件时,可能是因为语法是实验性的并且可能会发生变化。

就此而言,整个解析器可能不是您工作的主要部分,具体取决于您对代码生成器的具体目标。

答案 4 :(得分:3)

首先,SO上的“flex”标签是关于Adobe的产品,而不是词法生成器。其次,Bjarne Stroustrup表示他希望他使用递归下降而不是表驱动工具实现Cfront(第一个C ++编译器)。第三,直接回答你的问题 - 很多。如果您觉得需要编写一个,请查看ANTLR - 不是我最喜欢的工具,但已有C ++解析器。

答案 5 :(得分:3)

这是一个非常重要的问题,而且要花很多时间才能正确完成。首先,C ++的语法不能被LALR parser完全解析,例如yacc。您可以执行该语言的子集,但是使整个语言规范正确是很棘手的。

你不是第一个认为这很有趣的人。这是关于该主题的一篇很好的博客文章: Parsing C++

以下是文章的重要引用:

  

“经过大量调查,我   决定写一个   用于C ++的解析器/分析工具是   这是非常困难的   超出我想做的业余爱好。“

该文章的问题在于它有点陈旧,而且有些链接被破坏了。以下是一些关于编写C ++解析器主题的其他资源的链接:

答案 6 :(得分:3)

正如其他人已经说过的那样,yacc是实现C ++ 解析器的不良选择。一个人可以做到;在海湾合作委员会团队对维持和扩展的难度感到反感之前,原始海湾合作委员会这样做了。 (作为词法分析器,Flex可能没问题。)

有人说递归下降解析器是最好的,因为Bjarne Stroustrop这样说。我们的经验是GLR解析是正确的答案,我们的GLR-based C++ front end是一个很好的证明,就像Elsa前端一样。我们的前端已被用于数百万行C ++(包括Microsoft和GCC方言)的愤怒,以执行程序分析和大规模源代码转换。

但是没有强调的是解析只是构建编译器所需要的一小部分,特别是对于C ++。您还需要构建符号表(“此标识符在此上下文中的含义是什么?”),为此,您需要对C ++标准的几百页内的大部分内容进行编码。我们相信,我们构建类似编译器的工具DMS的基础,非常适合这样做,而且我们花了一年多的时间来完成这一部分。

但是你要考虑其余的编译器:

  • 预处理
  • AST构建
  • 语义分析和类型检查
  • 控制,数据流和指针分析
  • 基本代码生成
  • 优化
  • 注册分配
  • 最终代码生成
  • 调试支持

我一直这样说:为一种语言建立一个解析器(BNF部分)就像爬上喜马拉雅山的山麓。构建完整的编译器就像攀登珠穆朗玛峰。几乎任何土块都可以做前者(尽管C ++正处于边缘)。只有真正认真对待后者,并且只有做好充分准备。

期望构建一个C ++编译器来带你多年。

(SD C ++前端处理lexing,解析,AST生成,符号表,某些类型检查,以及从AST重新生成可编译的源文本,包括主要C ++方言的原始注释。它已经开发完成大约6年的时间。)

编辑:2015年5月。原始答案写于2010年;我们现在有11年的投资,通过C ++ 14带我们。重点是,构建其中之一是一项无穷无尽的大努力。

答案 7 :(得分:2)

Lex,yacc是不够的。你需要一个链接器,汇编程序..,c预处理器。 这取决于你是如何做到的。 您打算使用多少预制组件? 您需要从某处获得语法及其令牌的描述。

例如,如果您使用LLVM,则可以更快地进行。它已经提供了很多工具,汇编程序,链接器,优化器.... 你可以从boost项目中获得一个c预处理器。 您需要创建一个测试套件来自动测试编译器。

如果你每天工作可能需要一年的时间,或者你有更多的才能和动力。

答案 8 :(得分:2)

除非您已经编写了其他几个编译器; C ++不是一种你甚至想从头开始编写编译器的语言,这个语言有很多地方的意思需要大量的上下文才能消除歧义。

即使你有很多编写编译器的经验,你也会在几年内为一组开发人员寻找。这只是将代码正确解析为中间格式。编写后端以生成代码是另一项专门任务(尽管你可以窃取gcc后端)。

如果您使用谷歌搜索“C ++语法”,那么可以帮助您入门。

C++ LEX  Tokens:   http://www.computing.surrey.ac.uk/research/dsrg/fog/CxxLexer.l
C++ YACC Grammer:  http://www.computing.surrey.ac.uk/research/dsrg/fog/CxxGrammar.y
                   http://www.computing.surrey.ac.uk/research/dsrg/fog/CxxTester.y

答案 9 :(得分:1)

C ++编译器非常复杂。要实现足够的C ++以与大多数C ++代码兼容,需要几个开发人员几年全职。 clang是一个由Apple资助的编译器项目,用于为C,C ++和Objective-C开发一个新的编译器,有几个全职开发人员,C++ support仍远未完成经过几年的发展。

答案 10 :(得分:1)

几年 - 如果你能获得研究经费重新编写新的lex / yacc: - )

人们一直在追逐他们的尾巴 - 从Stroustrup开始,他一直被认为是一个语言“设计师”,而不是真正的编译器作者(记住他的C ++只是一个代码生成器,如果它不是'对于gcc和其他人来说。)

核心问题是,自从CPU变得足够快以处理函数式语言和强力递归下降以来,对解析器生成器的真正研究几乎不复存在。当你不知道该做什么时,递归下降是最后的手段 - 它会进行详尽的搜索直到它触发一个“规则”。一旦你对此感到满意,你就会对研究如何有效地研究它感兴趣。

你真正需要的是一个合理的中间地带 - 比如LALR(2)具有固定的,有限的回溯(加上静态检查器,如果“desiogner”挥霍进入一个非确定性树,就会大喊)以及有限和分区的符号表反馈(现代解析器需要兼容并发)。

听起来像研究补助金提案,不是吗:-)现在,如果我们找到某人实际资助它,那将是: - ))

答案 11 :(得分:0)

Recursive decent是解析C ++的不错选择。 GCC和clang使用它。

Elsa解析器(和我的ellcc编译器)使用Elkhound GLR编译器生成器。

在任何一种情况下,编写C ++编译器都是一项艰巨的任务。

答案 12 :(得分:0)

那么,编写编译器是什么意思?

我怀疑任何一个人都制作了一个真正的C ++编译器,它一直把它归结为汇编代码,但是我使用lex和yacc来制作一个C编译器而且我已经完成了它。

使用它们可以使编译器在几天内省去语义,但是弄清楚如何使用它们可能需要数周或数月的时间。弄清楚如何制作编译器将花费几周或几个月,无论如何,但我记得的数字是一旦你知道它是如何工作的,花了几天lex和yacc和几周没有,但第二个有更好的结果并且更少的错误,所以它们是否值得使用是否值得怀疑。

'语义'是实际的代码生成。这可能是非常简单的代码,只需要工作,可能不会花费很长时间,或者你可以花一辈子去做优化。

使用C ++,最大的问题是模板,但是有很多小问题和规则我无法想象有人想要这样做。即使你完成了,问题是你不一定有二进制兼容性,即能够被链接器或操作系统识别为可运行的程序,因为它不仅仅是C ++,而且很难确定标准,但是还有更多的标准要担心哪些标准的可用性更低。