从一个简单的(最简单的)C编译器开始?
我碰到这个: 使用Turbo Pascal编写一个编译器
我很好奇,如果有任何教程或参考解释如何创build一个简单的C编译器。 我的意思是,只要让我理解算术运算就足够了。 肯·汤普森 ( Ken Thompson)阅读了这篇文章后,我变得非常好奇。 写一些理解自己的想法似乎令人兴奋。
为什么我提出这个问题,而不是问Google? 我尝试了Google,而Pascal则是第一个链接。 其余的似乎没有相关性,并补充说…我不是一个CS专业(所以我仍然需要了解像yacc所有这些工具),我想通过这样做,我希望有更多经验的人是在这些方面总比Google好。 我想阅读一些与我上面列出的文章相同的文章,但至less强调了构build一个简单的C编译器的引导阶段。
另外,我不知道最好的学习方法。 我是否开始用C语言或其他语言构buildC编译器? 我是否编写C编译器或其他语言? 一旦我有了一些方向去探索,我觉得像这样的问题会得到更好的回答。 有什么build议么?
有什么build议么?
一个编译器由三部分组成:
- parsing器
- 抽象语法树(AST)
- 代码生成器
有很多很好的parsing器生成器,从语言语法开始。 也许ANTLR会是你开始的好地方。 如果你想坚持C根,尝试lex / yacc或野牛。
C有语法,但我认为C的整体是复杂的。 你最好从语言的一个子集开始,然后继续前进。
一旦你有一个AST,你用它来生成你将运行的机器码。
这是可行的,但不是微不足道的。
我还会检查Amazon关于编写编译器的书籍。 龙书是经典,但有更多的现代可用。
更新:有像堆栈溢出类似的问题,就像这个 。 检查出这些资源。
我build议你这个教程:
- LLVM教程
这是如何实现“小语言”编译器的一个小例子。 源代码非常小,并逐步解释。
LLVM(代表程序内部结构的低级虚拟机)库也有C前端库:
- 铛
对于它的价值, Tiny C编译器是一个function非常全面的C编译器,在一个相对较小的源代码包中。 例如,你可能从研究这个源代码中受益,因为比起试图理解GCC的所有源代码,它可能更容易理解。
这是我的观点(和猜想),如果不理解大学本科生(计算机科学)课程中通常涵盖的数据结构,就很难编写一个编译器。 这并不意味着你不能,但你需要知道关键的数据结构,如链表和树。
(至less在开始阶段),我build议将自己限制在一个基本的语言子集,比如常用的操作符,仅用于整数的支持,以及基本的函数和指针。 其中一个典型的例子就是Ron Cain的Small-C ,在Dobbs杂志上撰写的一系列文章中受到欢迎,我相信上世纪80年代。 他们用James Hendrix的绝版书“Small-C编译器”出版了一张CD 。
我build议遵循的是Crenshaw的教程,但是将其编写为C语言编译器,以及您希望定位的任何CPU目标(Crenshaw以Motorola 68000 CPU为目标)。 为了做到这一点,你将需要知道你想要运行编译程序的目标的基本汇编。 这可能包括一个68000的仿真器,或者MIPS,这些可以说是比Intel x86(16/32位)的老式CISC指令集更好的汇编指令集。
有许多潜在的书籍可以作为学习编译器/翻译理论(和实践)的起点。 阅读comp.compilers常见问题解答 ,并在各种网上书籍卖家审查。 大部分入门级书籍都是作为高二至高年级本科计算机科目的教科书编写的,所以在没有CS背景的情况下,他们的阅读速度会很慢。 一本比“ 龙书 ”更容易阅读的较旧的书,是由Thomas Parsons 编译的 “ 编译器介绍 ” 。 这是较旧的,所以你应该能够以合理的价格从您select的网上书店中find一份旧版本。
所以我会说,试着从Jack Crenshaw的“ 让我们编译一个编译器”教程开始,按照他的例子作为指导编写自己的代码,并构build一个简单编译器的基础知识。 一旦你有了这个工作,你可以更好地决定从那一点开始。
添加:
关于自举过程。 由于现有的C编译器可以免费使用,所以不需要担心引导。 使用单独的现有工具(GCC,Visual C ++ Express,Mingw / djgpp,tcc)编写您的编译器,您可以担心在晚些时候自行编译您的项目。 我对这个问题的这个部分感到惊讶,直到我意识到通过阅读肯·托马斯的ACM图灵奖演讲“ 反思信任信任 ”(编译器引导过程),才得到了编写自己的编译器的想法。 这是一个适度的高级话题,也是一个很大的麻烦。 我甚至发现,在旧的Unix系统(64位Alpha上的数字OSF / 1)下引导GCC C编译器,其中包括一个C编译器一个缓慢而费时的容易出错的过程。
另一类问题是像Yacc这样的编译器工具。 Yacc(另一个编译器编译器或GNU的Bison)是一个用来编写编译器(或翻译器)parsing器的工具。 根据您input到yacc的目标语言的forms语法 ,它会生成一个parsing器 ,它是编译器整体devise的一部分。 接下来是用于生成词法分析器或扫描器的Lex(或GNU Flex),它经常与yacc生成的parsing器结合使用,形成编译器前端的骨架。 这些工具使得编写器的前端比自己编写一个词法分析器和parsing器要容易得多。 Crenshaw的教程不使用这些工具,而且也不需要,许多编译器编写者并不总是使用它们。 当然Crenshaw承认教程的parsing器是相当基本的。
Crenshaw的教程也跳过了生成一个AST(抽象语法树),这简化了但也限制了教程编译器。 它缺less大部分(如果不是全部)优化,并且与编译器的“后端”发出的特定编程语言和特定汇编语言非常相关。 通常情况下,AST是一个中间件,可以进行一些优化,并在devise中用于解耦编译器的前端和后端。 对于一个没有计算机科学背景的初学者,我build议不要担心没有为你的第一个编译器(或者至less是第一个版本)提供一个AST。 我认为保持小而简单的方式可以帮助您在第一个版本中编写编译器,然后您可以从中决定如何继续。
我如何[开始写]一个简单的C编译器?
关于编译C没有什么简单的 。 最好的简单C编译器是Chris Fraser和David Hanson的lcc 。 他们花了10年时间在devise上尽可能简单地完成devise,同时还生成了相当不错的代码。 如果你有机会进入大学图书馆,你应该可以得到他们的书。
我是否开始用C语言或其他语言构buildC编译器?
其他一些语言。 有一次,我问Hanson他和Fraser在lcc项目上花了10年时间学到了什么。 汉森说的主要事情是
C编写一个编译器是一种糟糕的语言。
你最好使用Haskell或ML的一些方言。 两种语言都提供了代数数据types的function,这与编译器所面临的问题完美匹配。 如果你仍然想追求C,你可以从George Necula的CIL开始, CIL是用ML写的一个C编译器的一大块。
我想阅读一些与我上面列出的文章相同的文章,但至less强调了引导阶段…
你不会find像肯的另一篇文章。 但是,Andrew Appel写了一篇名为Axiomatic Bootstrapping的精彩文章:编译黑客指南我找不到免费版本,但很多人都可以访问ACM数字图书馆。
有什么build议么?
如果你想编写一个编译器,
-
使用Haskell或ML作为你的实现语言。
-
对于你的第一个编译器,select一个非常简单的语言,比如Oberon或者像Niklaus Wirth的书“ Algorithms + Data Structures = Programs”中的 P0。 Wirth因devise易于编译的语言而闻名。
你可以为你的第二个编译器编写一个C编译器。
您可能对本书/课程“计算系统的元素:从第一原则构build现代计算机”感兴趣。
请注意,这不是从你从newegg买来的东西build立一个“电脑”。 它从布尔逻辑基础的描述开始,从最低层次的抽象层次到越来越高层次的抽象层次构build了一个虚拟计算机。 课程资料全部在线,亚马逊书本身相当便宜。
在这个过程中,除了“构build硬件”之外,还将逐步实现汇编器,虚拟机,编译器和基本操作系统。 我认为这会给你足够的背景,可以深入研究其他答案中列出的一些更为常用的资源。
在Unix编程环境中 ,Kernighan和Pike经历了5次迭代,使计算器能够从简单的基于C语言的词法分析和立即执行到抽象机器的yacc / lexparsing和代码生成。 因为他们写得如此精彩,我不能build议更stream畅的介绍。 它肯定比C小,但这可能是你的优势。
编译器是一个复杂的主题,涵盖的方面
- input处理涉及乐清,parsing
- 构build所使用的每个variables的符号存储区,例如抽象语法树(AST)
- 从AST树转换并基于语法构build一个机器代码二进制文件
这绝不是详尽无遗,因为它是从山顶抽象的鸟瞰图,归结为语法符号是正确的,并确保格式错误的input不会丢掉,实际上,一个好的input处理决不应该掉落跪在地上,不pipe它有多么畸形,可怕,被滥用的情况。 而且,在决定和知道输出将会是什么的时候,它是否在机器代码中,这意味着你可能必须亲密地了解处理器指令(包括对于variables的存储器寻址等)。
以下是您开始使用的一些链接:
- 有一个杰克Crenshaw的C代码端口 …(我记得几个月前下载…)
- 这里有一个类似SO的链接。
- 另外,下面是Basic to x86汇编器编译器的另一个小编译器教程 。
- 微小的C编译器
- Hendrix的Small C编译器在这里find。
学习函数式编程也许是值得的。 函数式语言非常适合编写in和for编译器。 我学校的介绍编译器类包含了函数式语言的介绍,作业全部在OCaml中。
有趣的是你今天应该问这个问题,因为就在两天前,我写了一个lambda微积分解释器。 Lambda微积分是所有function语言的祖父。 它只有200行(在C ++中,包括错误报告,一些漂亮的打印,一些unicode),并且具有两阶段结构,中间格式可以用来生成代码。
不仅要从小做起,而且要build立最实用的编译器方法,也鼓励良好的,模块化的组织实践。
编译器是一个非常大的项目,尽pipe我认为这不会让人受伤。
我知道至less有一个用Pascal编写的C编译器,所以这不是你能做的最疯狂的事情。 我个人会select一个更现代的语言来实现我的C编译器项目,为简单起见(Python,Ruby,C,C ++或Java的d / l包很容易),因为它会在你的简历上看起来更好。
为了做一个编译器作为一个初学者项目,虽然,你将需要喝所有的敏捷kool-aid 。
总是有东西在运行,即使它没有做太多的事情。 只需要一小步就可以将东西添加到编译器中。 (“频繁发布”)。select语言的一小部分,并首先实现它。 (首先支持i = 0;
然后从那里扩展)
如果你想要一个令人兴奋的经验,教你如何编写自己编译的编译器,你需要阅读从1964年起的这篇论文。
META II是由Val Schorre 编写的面向语法的编译器语言 。
在10页中,它告诉你如何编写编译器,如何编写元编译器,提供一个虚拟的元编译器指令集,以及用元编译器构build的样例编译器。
在60年代后期,我学习了如何编写本文的编译器,并利用这些思想为几个小型机和微处理器构build了类C语言。
如果论文本身太多(不是!),那么就有一个在线教程 ,它会引导你完成整个事情。
如果从原始链接中获取纸张很尴尬,因为您不是ACM会员,那么您会发现本教程包含了所有的细节。 (恕我直言,价格,纸本身waaaaay值得)。
10页!
我不build议从C开始作为语言来实现,也不build议使用任何编译器生成器或parsing器生成器工具。 C是一个非常棘手的语言,可能是一个更好的主意,只是你自己的语言。 它可以是一个类似于C的(例如,如果要指示函数体,使用相同的types名称,则不需要记住所谓的所有内容就使用curl后缀)。
用于编译器和parsing器的工具非常棒,但是有一个真正的简化符号的问题。 如果你不知道如何创build一个编译器,速记会显得模糊不清,不必要的限制等。所以编写你自己的简单的编译器,然后从那里继续。 我也build议你不要开始生成实际的机器码,除非你吃和呼吸汇编程序。 用虚拟机创build你自己的字节码解释器。
至于你应该用什么语言来创build你的第一个编译器:只要语言相当完整就没关系。 您将阅读input文本,从中构build数据结构并写出二进制数据。 所以,如果一种语言以任何方式使这些事情变得更容易,那就是有利于它的一点。 select一种你熟悉的语言,这样你就可以专注于创build编译器,而不是学习语言。 我通常使用面向对象的语言,这使得语法树更容易编写,如果熟悉这个语言,一个函数式语言也可能工作。
我已经写了很多关于编程语言的博客,所以你可能会在这里find一些有用的贴子: http : //orangejuiceliberationfront.com/category/language-design/
哦,关于一个编译器的引导:你可能不会从一开始就这样做。 创build编译器涉及相当多的工作。 因此,不仅编写自引导编译器涉及到编写编译器(用其他语言),一旦拥有了编译器,就必须使用自身编写第二个版本的编译器。 这是工作的两倍,加上现有的和自举的新编译器所需的debugging,直到全部工作。 也就是说,一旦你有一个工作的编译器,这是一个很好的方法来testing它的完整性。 好吧,也许不是工作的两倍,而是更多的工作。 我会先取得轻松的成功,然后从那里继续前进。
无论如何,玩得开心!