【问题标题】:How much time would it take to write a C++ compiler using flex/yacc?使用 flex/yacc 编写 C++ 编译器需要多少时间?
【发布时间】:2010-12-30 00:23:08
【问题描述】:

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

我可以从哪里开始?

【问题讨论】:

  • 祝你好运。 (还有@Neil,新标签是(错误地)gnu-flex,或者,lex
  • 您可以从 Code Complete 名声的 Steve McConnell 的 Software Estimation 开始阅读。
  • 一些帮助:让我们构建一个编译器,作者 Jack Crenshaw,compilers.iecc.com/crenshaw

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


【解决方案1】:

bison/yacc 解析器无法解析许多解析规则(例如,在某些情况下区分声明和函数调用)。此外,有时对标记的解释需要来自解析器的输入,尤其是在 C++0x 中。例如,字符序列>> 的处理非常依赖于解析上下文。

这两个工具对于解析 C++ 来说是非常糟糕的选择,为了正确解析 C++,您必须放入许多超出这些工具所依赖的基本框架的特殊情况。这将花费您很长时间,即使如此,您的解析器也可能会出现奇怪的错误。

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

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

【讨论】:

  • 如果我没记错的话,gcc 在 3.x 系列后期的某个时候切换到使用递归下降解析器。
  • gcc 将 lex 和 yacc 用于 C++ 前端。对于您提到的所有歧义,有明确的命令可以解决冲突。我个人怀疑是否有更好的框架来解析 C++。 但是在没有大量编译器经验的情况下编写 C++ 词法分析器/解析器对于使用 lex/yacc 的单个开发人员来说是行不通的(它只是庞大而复杂)。
  • @Martin York,事实上,bison/yacc 解析器在 gcc-3.4 中被递归下降解析器取代 - gcc.gnu.org/gcc-3.4/changes.html
  • 递归下降解析器也更容易理解。事实上,如果您对语言开发感兴趣,我可能会建议您从手动生成一个相对简单的语法的递归下降解析器开始。
  • @kyoryu:递归下降解析器并不比纯语法更容易理解,尤其是对于 C++ 规模的工件。您确实需要根据 BNF 规则从语言定义驱动的解析器生成器。说 C++ 难以解析的人是那些使用 YACC(和变体)的人,以及那些将名称/类型解析与解析纠缠不清的人。 GLR 解析器生成器允许您使用 BNF 规则构建一个非常好的解析器,并隔离名称/类型解析;这种分离使每项任务变得容易得多(尽管并不容易)。在这里查看我的答案。
【解决方案2】:

听起来您对解析/编译器创建很陌生。如果是这种情况,我强烈建议不要从 C++ 开始。这是一种语言的怪物。

要么自己发明一种微不足道的玩具语言,要么以更小更简单的东西为模型做一些事情。我看到一个 lua 解析器,其中语法定义大约有一页长。作为起点,这样会更合理。

【讨论】:

    【解决方案3】:

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

    解析 C++ 是出了名的容易出错。该语法不是完全可 LR 解析的,因为许多部分是上下文相关的。你将无法让它在 flex/yacc 中正常工作,或者至少实现起来会很尴尬。据我所知,只有两个前端可以做到这一点。您最好的选择是使用其中之一并专注于编写后端。无论如何,这就是有趣的地方:-)。

    现有的 C++ 前端:

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

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

    3. 另一种可能是使用 clang,但目前他们的 C++ 支持参差不齐。很高兴看到他们解决了所有错误,但如果您查看他们的C++ status page,您会发现仍有不少测试用例无法解决。注意——clang 是一个大项目。如果这些人要花几年时间来实现一个 C++ 前端,那你会花更长的时间。

    4. 其他人提到了ANTLR,并且有可用的 C++ 语法,但我持怀疑态度。我还没有听说过任何主要编译器中使用了 ANTLR 前端,但我相信它已在 NetBeans IDE 中使用。它可能适用于 IDE,但我怀疑您能否在生产代码中使用它。

    【讨论】:

    • gcc_xml 解析除代码之外的所有内容,因此它对编译器没有用处。你只能得到函数和类型声明。
    【解决方案4】:

    时间长了,lex 和 yacc 也无济于事

    如果您有能力为如此庞大的语言编写编译器,您将不需要 lex 和 yacc 为您提供的少量帮助。事实上,虽然 lex 没问题,但使用 yacc 可能需要更长的时间,因为它对于 C 或 C++ 来说还不够强大,而且你最终可能会花费更多的时间来让它正常工作,而不是仅仅编写一个递归下降解析器。

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

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

    【讨论】:

    • 我完全不同意。 gcc 团队也是如此。其中 C++ 前端是 lex 和 yacc。
    • 马丁:不是。 "bison/yacc 解析器在 gcc-3.4 中被递归下降解析器替换"
    • Martin:使用yacc当然是可能的,gcc在有和没有的情况下都做到了。 Ruby 有一个复杂的语法,主要实现确实使用了 yacc。我已经用两种方式编写了解析器,当然没有简单的“总是这样做”的答案,我只是认为重要的是要意识到解析器无论哪种方式都会付出大致相同的努力。 yacc 的真正问题在于,虽然简单的东西真的很容易,但您也可能会遇到难以理解的错误。使用 RD,您只需修复代码。
    • 我认为重点不是 flex/yacc 是否是好工具,而是指出它们只是整个问题的一小部分。太好了,您已经将文件解析为某种中间表示(AST/whatever) - 现在是什么?
    【解决方案5】:

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

    有人说递归下降解析器是最好的,因为 Bjarne Stroustrop 是这么说的。我们的经验是 GLR 解析是解决这个问题的正确答案,我们的 GLR-based C++ front end 就是一个很好的证明,Elsa 前端也是如此。我们的前端已经在数百万行C++(包括微软和GCC方言)上怒用,进行程序分析和海量源代码转换。

    但没有得到足够强调的是,解析只是构建编译器所需的一小部分,尤其是对于 C++。您还需要构建符号表(“此标识符在此上下文中的含义是什么?”),为此您需要对数百页 C++ 标准的大部分内容进行编码。我们相信,我们构建类似编译器的工具的基础 DMS 非常适合这样做,我们花了一个多月的时间才把这部分做好。

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

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

    我一直在说:为一种语言构建解析器(BNF 部分)就像攀登喜马拉雅山的山麓。构建一个完整的编译器就像攀登珠穆朗玛峰。几乎任何 clod 都可以做到前者(尽管 C++ 就在边缘)。只有真正认真的人才会做后者,而且只有在准备充分的情况下。

    预计构建 C++ 编译器会花费您数年时间。

    (SD C++ 前端处理主要 C++ 方言的词法分析、解析、AST 生成、符号表、一些类型检查和从 AST 重新生成可编译源文本,包括原始 cmets。它已经开发超过大约 6 年)。

    编辑:2015 年 5 月。原始答案写于 2010 年;我们现在已经投入了 11 年,从 C++14 开始。关键是,要构建其中的一个是无尽的、巨大的努力。

    【讨论】:

    • 如果你能负担得起,那就太好了,Ira,你是否与语义设计的这群人有联系? stackoverflow.com/questions/526797/…, stackoverflow.com/questions/792454/…
    • 语义设计的人群。在这里查看我的简历,其中明确说明了这一点。同意,如果你能负担得起就好了。如果您也负担得起,另一种选择(自己构建整个东西)也不错,但是您不能;您和您的雇主都无法负担您花费大量时间来构建此类工具。如果你打算把它作为一种爱好去做,那就更没有意义了,除非它是一项终生的任务。 “如何实现一个简单的编译器”这种形式的问题不会引起这种反应。
    【解决方案6】:

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

    【讨论】:

    • 恕我直言,Adobe 的问题是他们为自己的产品选择了一个已经被广泛使用的名称。
    • 好吧,这就是我们的问题。我怀疑使用 Adob​​e Flex 的人数(不是我,我赶紧补充一下)大大超过了 flex 工具的用户——据我所知,他们的名字没有版权或商标。
    • @Nils - 我同意,但关于 Meta 的讨论表明,共识是针对将在 5 年内消失的新技术,而不是几乎永远存在的久经考验的小众计划.围绕它的元讨论(由我开始。我很有名!):meta.stackexchange.com/questions/23959/…
    【解决方案7】:

    这是一个不平凡的问题,并且需要相当多的时间才能正确完成。一方面,C++ 的语法不能完全被 LALR parser 解析,例如 yacc。你可以做语言的子集,但是让整个语言规范正确是很棘手的。

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

    这是文章的重要引述:

    “经过大量调查,我 决定写一个 C++ 的解析器/分析工具是 足够困难 超出了我想做的爱好。”

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

    【讨论】:

      【解决方案8】:

      Lex,yacc 是不够的。你需要一个链接器,也需要一个汇编器......,c预处理器。 这取决于你怎么做。 您打算使用多少预制组件? 您需要从某个地方获取语法及其标记的描述。

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

      如果你每天都在努力,可能需要一年的时间,或者更少你有更多的天赋和动力。

      【讨论】:

      • +1 用于提及 LLVM。我将它用于我的后端。很棒的东西。
      • 编译器不一定需要链接器、汇编器或预处理器。我曾经写过一个小型的 C 编译器,也不需要。
      【解决方案9】:

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

      即使您有很多编写编译器的经验,您也需要为一个开发团队工作几年。这只是为了将代码正确解析为中间格式。编写后端以生成代码是另一项专门任务(尽管您可以窃取 gcc 后端)。

      如果您在 google 上搜索“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
      

      【讨论】:

        【解决方案10】:

        几年 - 如果你能获得研究资助来重写新的 lex/yacc :-)

        人们在这方面一直在追赶他们的尾巴——从 Stroustrup 开始,他总是幻想成为一名语言“设计师”而不是真正的编译器编写者(请记住,他的 C++ 多年来只是一个代码生成器,如果不是的话,他仍然会在那里。 t 用于 gcc 和其他人)。

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

        您本质上需要的是一个合理的中间立场——例如具有固定、有限回溯的 LALR(2)(加上静态检查器,如果“设计者”挥霍到不确定的树中,则大喊大叫)以及有限和分区的符号表反馈(现代解析器需要对并发友好)。

        听起来像是一项研究资助计划,不是吗 :-) 现在,如果我们能找到真正资助它的人,那就太好了 :-))

        【讨论】:

          【解决方案11】:

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

          【讨论】:

            【解决方案12】:

            递归体面是解析 C++ 的好选择。 GCC 和 clang 使用它。

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

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

            【讨论】:

              【解决方案13】:

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

              我怀疑是否有人制作了真正的 C++ 编译器,将其一直分解为汇编代码,但我使用 lex 和 yacc 制作了 C 编译器,但我没有这样做。

              同时使用这两种方法,您可以在几天内制作出一个忽略语义的编译器,但弄清楚如何使用它们可能需要数周或数月的时间。无论如何,弄清楚如何制作一个编译器将花费数周或数月的时间,但我记得的数字是,一旦你知道它是如何工作的,使用 lex 和 yacc 需要几天,而没有使用则需要几周,但第二个效果更好并且错误更少,因此真的值得怀疑它们是否值得使用。

              “语义”是实际的代码生成。这可以是非常简单的代码,足以运行并且可能根本不需要很长时间,或者您可以花费一生的时间来优化它。

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

              【讨论】:

                猜你喜欢
                • 2011-01-15
                • 1970-01-01
                • 2011-03-09
                • 2016-12-16
                • 2017-04-17
                • 1970-01-01
                • 1970-01-01
                • 2012-08-27
                • 1970-01-01
                相关资源
                最近更新 更多