学习写一个编译器
首选语言 :C / C ++,Java和Ruby。
我正在寻找一些有用的书籍/教程,如何编写自己的编译器只是为了教育目的。 我对C / C ++,Java和Ruby最为熟悉,所以我更喜欢涉及这三者之一的资源,但是任何好的资源都是可以接受的。
资源大名单:
- 一个编译器教育的Nanopass框架
- 高级编译器devise和实现 $
- 编译器构build的增量方法 ¶
- ANTLR 3.xvideo教程
- 编译器devise基础
- build立一个鹦鹉编译器
- 编译器基础
- 编译器构build $
- 编译器devise和build造 $
- 用C $ 编写一个编译器
- C编译器devise
- 编译器:原理,技术和工具 $ – 又名“龙书” ; 被广泛认为是编译器编写的“书”。
- devise编译器 $
- 编程语言要点
- Flipcode文章存档 (查找“由Jan Niestadt实现脚本引擎”)
- 游戏脚本掌握 $
- 如何在C#中从头开始构build虚拟机
- 实现function语言
- 实现编程语言(与BNFC)
- 使用C#4.0实现编程语言
- 解释器模式 (描述于devise模式 $)指定一种评估语言中的句子的方法
- 语言实现模式:创build您自己的特定领域和一般编程语言
- 由Jack Crenshaw编写一个编译器 – PDF版本(例子在Pascal中,但是这些信息通常是可用的)
- 链接器和加载器 $(Google Books)
- Lisp in Small Pieces(LiSP) $
- LLVM教程
- 现代编译器在ML $中的实现 – 还有一个Java $和C $版本 – 被广泛认为是一本很好的书
- 面向对象的编译器构造 $
- parsing技巧 – 实用指南
- Oberon项目 ¶ – 请看第13章
- 编程一台个人电脑 $
- 编程语言:应用和解释
- 兔子:计划的编译器
- 对信任信任的思考 – 快速指南
- 为.NET框架推出自己的编译器 – 来自MSDN的快速教程
- 计算机程序的结构和解释
- types和编程语言
- 想编写一个编译器? – 一个快速指南
- 在Ruby底层编写一个编译器
传说:
- ¶链接到PDF文件
- $链接到一本印刷书籍
我想这是一个相当模糊的问题。 只是因为涉及到这个话题的深度。 编译器可以分解成两个独立的部分,但是, 上半部分和下半部分。 上半部分通常采用源语言并将其转换为中间表示,下半部分负责平台特定的代码生成。
尽pipe如此,一个简单的方法来解决这个问题(我们在编译器类中至less使用过这个方法)的一个想法是在上面描述的两部分中构build编译器。 具体来说,您只需构build上半部分即可了解整个stream程。
只要完成上半部分,就可以获得编写词法分析器和parsing器的经验,并生成一些“代码”(我提到的中间表示)。 所以它会把你的源程序,并将其转换为另一种表示,并做一些优化(如果你想),这是编译器的核心。 然后,下半部分将采用该中间表示forms,并生成在特定体系结构上运行程序所需的字节。 例如,下半部分将采取您的中间表示forms并生成一个PE可执行文件。
一些关于这个话题的书籍,我发现特别有用的是编译原则和技术 (或龙书,由于可爱的龙在封面上)。 它有一些伟大的理论,并且确实涵盖了一个非常容易理解的上下文语境。 另外,为了构build词法分析器和parsing器,您可能会使用* nix工具lex和yacc。 有趣的是,这本名为“ lex and yacc ”的书收录了Dragon Book离开这部分的地方。
我认为ML中的现代编译器实现是编写文本的最佳入门编译器。 还有一个Java版本和一个C版本 ,根据您的语言背景,其中任何一个都可以更容易访问。 本书包含了许多有用的基础资料(扫描和parsing,语义分析,激活logging,指令select,RISC和x86本地代码生成)以及各种“高级”主题(编写OO和函数式语言,多态,垃圾收集,优化和单一静态分配forms)分配到相对较小的空间(〜500页)。
我更喜欢现代编译器实现龙的书,因为现代编译器实现调查较less的领域,而是真正覆盖所有的主题,你将需要编写一个严肃的,体面的编译器。 在你完成本书之后,如果你需要的话,你将会准备好更深入地处理研究论文。
我必须承认,我对Niklaus Wirth的编译器构build有着严重的困惑。 它可以在网上以PDF格式提供。 我发现Wirth的编程美学简单美观,但是有些人觉得他的风格太less了(例如Wirth喜欢recursion下降parsing器,但是大多数CS课程都关注parsing器生成器工具; Wirth的语言devise相当保守)编译器构造是一个非常简洁的蒸馏Wirth的基本思想,所以不pipe你喜欢他的风格与否,我强烈推荐阅读这本书。
我同意龙书的参考资料; 海事组织,这是编译器build设的权威指南。 尽pipe准备好一些硬核理论。
如果你想要一本理论上较轻的书, 游戏脚本掌握可能是一本更好的书。 如果你是一个编译器理论的新手,它提供了一个温和的介绍。 它并没有涵盖更实用的parsing方法(select非预测性recursion下降而不讨论LL或LRparsing),我记得它甚至没有讨论任何种类的优化理论。 另外,不是编译成机器代码,而是编译为一个字节码,该字节应该在你也写的虚拟机上运行。
这仍然是一个体面的阅读,特别是如果你可以在亚马逊便宜。 如果您只想简单地介绍编译器,那么游戏脚本掌握并不是一个好的方法。 如果你想在前面硬派,那么你应该定下一本龙书。
“让我们来编译一个编译器”真棒,但有点过时了。 (我不是说这使得它有点不那么有效。)
或者退房SLANG 。 这与“Let's Build a Compiler”类似,但是对于初学者来说,这是一个更好的资源。 这与一个PDF教程,它采取了7步的方法教你一个编译器。 添加quora链接,因为它具有指向SLANG的所有不同端口(C ++,Java和JS)的链接,还包含最初使用C#和.NET平台编写的python和java中的解释器。
如果您正在寻找使用function强大,层次更高的工具而不是自己构build所有内容 ,那么通过本课程的项目和阅读材料是一个不错的select。 这是Javaparsing器引擎ANTLR的作者的一门语言课程。 您可以从实用程序员那里获得该课程的PDF文件。
本课程将介绍标准的编译器编译器,您可以在其他地方看到:parsing,types和types检查,多态性,符号表和代码生成。 几乎没有涉及的唯一的事情就是优化。 最终的项目是一个编译C子集的程序。 因为你使用了ANTLR和LLVM这样的工具,所以在一天之内编写整个编译器是可行的(我有一个存在的证据,尽pipe我的意思是〜24小时)。 使用现代工具在实际工程上是沉重的,理论上有点轻。
顺便说一句,LLVM简直太棒了。 很多情况下,你通常可以编译成汇编语言,而不是编译成LLVM的中间表示语言 。 它是更高层次的,跨平台的,LLVM很好地从它产生优化的组装。
如果你没有时间,我推荐Niklaus Wirth编写的“编译器构造”(Addison-Wesley,1996) ,一本你可以在一天中阅读的小小册子,但它解释了基本知识(包括如何实现词法分析器,recursion下降parsing器,和您自己的基于堆栈的虚拟机)。 在那之后,如果你想深入潜水,其他评论者build议的龙书没有办法。
你可能想看一下Lex / Yacc(或者Flex / Bison,无论你想怎么称呼它们)。 Flex是一个词法分析器,它将parsing和识别语言的语义组件(“令牌”),Bison将用于定义每个令牌被parsing时会发生什么。 这可能是,但绝对不限于打印C代码,编译为C的编译器或dynamic运行指令。
这个FAQ可以帮助你,而且这个教程看起来非常有用。
一般来说,编译器没有五分钟的教程,因为这是一个复杂的主题,编写一个编译器可能需要几个月的时间。 你将不得不做自己的search。
Python和Ruby通常被解释。 也许你也想从口译开始。 这通常更容易。
第一步是编写一个正式的语言描述,你的编程语言的语法。 然后,您必须将要编译或根据语法解释的源代码转换为抽象语法树,这是计算机可以理解并可以操作的源代码的内部forms。 这一步通常称为parsing,parsing源代码的软件称为parsing器。 parsing器通常由parsing器生成器生成,该parsing器生成器将forms语法转换为源代码或机器代码。 对于一个好的,非math的parsing解释,我推荐parsing技术 – 实用指南。 维基百科有一个parsing器生成器的比较,你可以select一个适合你的parsing器生成器。 根据您select的parsing器生成器,您可以在Internet上find教程,并且可以find非常stream行的parsing器生成器(如GNU bison),还有书籍。
为您的语言编写parsing器可能非常困难,但这取决于您的语法。 所以我build议保持简单的语法(与C ++不同); LISP就是一个很好的例子。
在第二步中,抽象语法树从树结构转换成线性中间表示。 作为一个很好的例子,这个Lua的字节码经常被引用。 但是中间表示真的取决于你的语言。
如果你正在build立一个口译员,你只需要解释中间表示。 你也可以及时编译它。 我推荐LLVM和libjit进行即时编译。 为了使语言可用,您还必须包含一些input和输出function,也许还需要一个小型标准库。
如果你要编译这个语言,它会更复杂。 您将不得不为不同的计算机体系结构编写后端,并从后端的中间表示生成机器代码。 我推荐LLVM完成这个任务。
关于这个话题有几本书,但是我可以不推荐一些用于一般用途的书。 他们中的大多数人太学术或太实际。 没有“在21天内编写自己的编译器”,因此,你将不得不买几本书才能很好地理解整个主题。 如果你search互联网,你会遇到一些在线书籍和讲义。 也许你附近有一个大学图书馆,你可以借阅编辑器的书。
如果你打算让你的项目变得严肃,我还build议你在理论计算机科学和图论方面有很好的背景知识。 计算机科学学位也是有帮助的。
看看下面的书。 作者是ANTLR的创造者。
语言实现模式:创build您自己的特定领域和一般编程语言 。
有一本书尚未提出,但非常重要的是John Levine的“Linkers and Loaders” 。 如果您不使用外部汇编程序,则需要一种方法来输出可链接到最终程序中的对象文件。 即使你使用的是外部汇编器,你可能也需要了解重定位,以及整个程序加载过程是如何工作的。 本书收集了许多关于这个过程的各种系统的随机知识,包括Win32和Linux。
龙书绝对是“build筑编译器”的书,但是如果你的语言不像现在这样的语言那么复杂,你可能想看看Design Patterns的Interpreter 模式 。
书中的例子devise了一种正则expression式语言,并且经过深思熟虑,但是正如他们在书中所说的那样,这对于思考整个过程是有好处的,但是只对小语言有效。 然而,用这种模式为小语言编写解释器要快得多,而不必学习所有不同types的parsing器,yacc和lex等等。
如果您愿意使用LLVM,请查看: http : //llvm.org/docs/tutorial/ 。 它教你如何使用LLVM的框架从头开始编写编译器,并不假定你对这个主题有任何的了解。
本教程build议您编写自己的parsing器和词法分析器等,但是我build议您一旦得到这个想法,就去查看一下bison和flex。 他们让生活变得如此简单。
我正在研究相同的概念,并find了Joel Pobar这篇有前途的文章,
为.NET Framework创build一个语言编译器
他讨论了一个编译器的高级概念,并着手为.Net框架创build自己的语言。 尽pipe其目标是.Net框架,但许多概念应该能够被复制。 该条款涵盖:
- Langauge定义
- 扫描器
- parsing器(这个位主要是感兴趣的)
- 面向.Net框架
- 代码生成器
还有其他的话题,但是你明白了。
它的目标是开始用C#编写(不太相当Java)
HTH
骨头
“…让我们build立一个编译器…”
我想通过@sasb来访问 http://compilers.iecc.com/crenshaw/ 。 暂时忘记购买更多的书籍。
为什么? 工具和语言。
所需的语言是帕斯卡,如果我没有记错是基于Turbo-Pascal。 如果你去http://www.freepascal.org/并下载Pascal编译器,那么所有的例子都是从页面直接运行的。http ://www.freepascal.org/download.var帕斯卡是你可以使用它几乎任何处理器或操作系统,你可以照顾。
一旦你掌握了课程,然后尝试更先进的“ 龙书 ” 〜http://en.wikipedia.org/wiki/Dragon_book
我发现龙书太难以阅读,过于关注语言理论,在实践中并不需要编写一个编译器。
我将添加Oberon书,其中包含一个惊人的快速和简单的Oberon编译器项目Oberon的全部源。
创build编译器的一个简单方法是使用bison和flex(或类似),构build一个树(AST)并用C生成代码。生成C代码是最重要的一步。 通过生成C代码,您的语言将自动在具有C编译器的所有平台上工作。
生成C代码就像生成HTML一样简单(只是使用print或者等价的),而这又比写一个C语言分析器或者HTML分析器容易得多。
从comp.compilers常见问题解答 :
“编程个人电脑”Per Brinch Hansen Prentice-Hall 1982 ISBN 0-13-730283-5
这本不幸的书籍解释了微软单用户编程环境的devise和创build,使用了一种名为Edison的Pascal语言。 作者介绍了Edison编译器和简单支持操作系统的逐步实现的所有源代码和解释,全部都是用Edison自己编写的(除了用PDP 11/23的符号汇编程序编写的小型支持内核之外,完整的源代码也可以为IBM PC订购)。
本书最有意思的是:1)能够演示如何创build一个完整的,自包含的,自我维护的,有用的编译器和操作系统; 2)有趣的语言devise和规范问题的讨论,在第二章中。
“Brinch Hansen on Pascal Compilers”by Per Brinch Hansen Prentice-Hall 1985 ISBN 0-13-083098-4
另一个理论上的重载语言在这里是如何编写的。 作者提供了一个编译器和P代码解释器的devise,实现和完整的源代码,Pascal(Pascal“minus”),带有布尔和整数types(但不包含字符,实数,子范围或枚举types) ,常量和variables定义以及数组和loggingtypes(但不包括打包,变体,集合,指针,无名称,重命名或文件types),expression式,赋值语句,带有值和variables参数的嵌套过程定义,if语句,while语句,和开始结束块(但没有函数定义,过程参数,goto语句和标签,case语句,repeat语句,语句和语句)。
编译器和解释器是用Pascal *(Pascal“star”)编写的,Pascal子集是用于创build软件开发系统的一些Edison风格特征的扩展。 作者出售了IBM PC的Pascal *编译器,但很容易将本书的Pascal编译器移植到任何便利的Pascal平台上。
这本书使编译器的devise和实现看起来很简单。 我特别喜欢作者关心质量,可靠性和testing的方式。 编译器和解释器可以很容易地用作更多涉及的语言或编译器项目的基础,特别是如果你被迫迅速启动并运行的话。
您应该查看Darius Bacon的“ ichbins ”,它是一个针对C语言的小Lisp方言的编译器,只需要超过6页的代码。 它比大多数玩具编译器的优点是语言足够完整,编译器编写。 (tarball还包括一个翻译引导的东西。)
关于在我的Ur-Scheme网页上学习编译器时发现有用的东西,还有更多的东西。
我记得大约在七年前,当我刚刚接触编程时,提出这个问题。 当我问起时,我非常小心,而我却没有像到这里那样得到太多的批评。 然而,他们却把我指向了“ 龙书 ”,在我看来,这是一本非常棒的书,它解释了编写一个编译器所需要知道的一切(当然,你必须掌握一门或两门语言)你知道的语言,更好)。
是的,许多人说读这本书是疯了,你不会从中学到什么,但我完全不同意。
许多人也说编写编译器是愚蠢的和毫无意义的。 那么编译器开发有用的原因有很多: – 因为它很有趣。 – 这是教育性的,当学习如何编写编译器时,您将学习到很多关于编写其他应用程序时有用的计算机科学和其他技术。 – 如果没有人编写编译器,现有的语言不会好起来的。
我没有马上编写自己的编译器,但是在问我知道从哪里开始。 而现在,在学习了很多不同的语言并阅读龙书之后,写作不是什么大问题。 (我也在学习计算机工程atm,但是我所了解的关于编程的大部分都是自学的。)
总结:龙书是一个伟大的“教程”。 但在尝试编写编译器之前,花一些时间掌握一门或两门语言。 不要指望在未来十年左右成为编译器大师。
如果你想学习如何编写parsing器/解释器,这本书也是很好的。
Python与用Python编写的python编译器捆绑在一起。 你可以看到源代码,它包括从parsing,抽象语法树,发射代码等所有阶段。
Fraser和Hanson的LCC编译器( 维基百科 )( 项目主页 )在其“可重新编译的C编译器:devise和实现”一书中有介绍。 它非常易读,解释了整个编译器,直到代码生成。
对不起,这是西class牙文,但是这是阿根廷“Compiladores eIntérpretes”(编译器和口译员)课程的参考书目。
课程是从forms语言理论到编译器构build的,至less需要一个简单的编译器来构build这些课题:
编译器deviseC.
艾伦一霍勒布普伦蒂斯霍尔。 1990年。
Compiladores。 TeoríayConstrucción。
SanchísLlorca,FJ,GalánPascual,C编辑Paraninfo。 1988年。编译器构造。
尼克劳斯·沃斯Addison-Wesley出版社。 1996年。
冷语,语法和语法。 联合国práctico。
Pedro IsasiViñuela,PalomaMartínezFernández,Daniel BorrajoMillán。 Addison-Wesley Iberoamericana(España)。 1997年。编译器devise的艺术。 理论与实践。
托马斯·皮特曼,詹姆斯·彼得斯。普伦蒂斯霍尔。 1992年。
面向对象的编译器构造。
吉姆·福尔摩斯
Prentice Hall,Englewood Cliffs,NJ 1995Compiladores。 Conceptos Fundamentales。
B. Teufel,S.Schmidt,T.Teufel。Addison-Wesley Iberoamericana。 1995年。
自动机理论,语言和计算导论。
约翰·E·霍普克罗夫特 Jeffref D. Ullman。
Addison-Wesley出版社。 1979年。正式语言介绍
GyörgyE.Révész。Mc Graw小山。 1983年。
parsing技巧。 实用指南。
Dick Grune,Ceriel Jacobs。
Impreso por los autores。 1995年
http://www.cs.vu.nl/~dick/PTAPG.htmlYacc:另一个编译器 – 编译器。
斯蒂芬C.约翰逊
计算机科学技术报告第32号,1975年。贝尔实验室。 美利山,新
新泽西州。Lex:一个词法分析器生成器。
ME Lesk,E. Schmidt。 计算机科学技术报告第39号,1975年。贝尔实验室。 美国新泽西州Murray Hill。lex&yacc。
约翰·莱文,托尼·梅森,道格·布朗。
O'Reilly&Associates。 1995年。计算理论的要素。
Harry R. Lewis,Christos H. Papadimitriou。 SegundaEdición。 学徒霍尔。 1998年。Un Algoritmo Eficiente para laConstruccióndel Grafo de Dependencia de Control。
萨尔瓦多V.卡瓦帝尼。
Trabajo Final de Grado para obtener elTítulode Ingeniero enComputación。
Facultad deMatemáticaAplicada。 UCSE 2001。
这里有很多很好的答案,所以我想我只是再添加一个:
我在十多年前得到了一本名为“Oberon Project”的书,在编译器上有一些写得很好的文字。 这本书真正脱颖而出,其源和解释是非常实用和可读的。 完整的文本(2005版)已经以pdf格式提供,所以你可以立即下载。 编译器在第12章中讨论:
http://www-old.oberon.ethz.ch/WirthPubl/ProjectOberon.pdf
Niklaus Wirth,JürgGutknecht
(这个处理并不像他编写的那样广泛)
我已经阅读了几本关于编译器的书籍,我可以把龙书,秒花在这本书上的时间是非常值得的。
如果你有兴趣为function语言(而不是程序语言)编写编译器,Simon Peyton-Jones和David Lester的“ 实现function语言:一个教程 ”是一个很好的指导。
function评估如何工作的概念基础是以一个简单而强大的function语言“核心”的例子为指导。 此外,Core语言编译器的每个部分都使用Miranda中的代码示例(与Haskell非常类似的纯函数语言)进行了解释。
描述了几种不同types的编译器,但即使您只遵循Core的所谓模板编译器,您也将很好地理解函数式编程的内容。
- 这是一个很大的课题。 不要低估这一点。 不要低估我的观点,不要低估它。
- 我听说龙书是一个(?)开始的地方,随着search。 :)更好的search,最终这将是你的生活。
- build立你自己的编程语言绝对是一个很好的练习! 但是要知道,它永远不会被用于任何实际目的。 这个例外是less之又less。
如果你想了解更多关于编译器(和元编译器)的知识,那么这本书不是一本书,而是一本技术文章和一个非常有趣的学习体验。本网站引导你构build一个完全独立的编译器系统,可以编译自己和其他语言:
教程:Metacompilers第1部分
这是基于一个惊人的10页的小技术文件:
Val Schorre META II:一种面向语法的编译器编写语言
从1964年的诚实到神灵。我从1970年开始就学会了如何编译编译器。当你最终了解编译器如何重新生成自己时,有一个令人兴奋的时刻….
我知道我大学时代的网站作者,但是我与网站无关。
我也喜欢Crenshaw教程 ,因为它清楚地表明编译器只是读取一些input并写出一些输出的另一个程序。
阅读。
如果你愿意的话,就去做吧,但是再看看另外一个关于如何编写更大更完整的编译器的参考。
And read On Trusting Trust , to get a clue about the unobvious things that can be done in this domain.
You can use BCEL by the Apache Software Foundation. With this tool you can generate assembler-like code, but it's Java with the BCEL API. You can learn how you can generate intermediate language code (in this case byte code).
Simple example
-
Create a Java class with this function:
public String maxAsString(int a, int b) { if (a > b) { return Integer.valueOf(a).toString(); } else if (a < b) { return Integer.valueOf(b).toString(); } else { return "equals"; } }
Now run BCELifier with this class
BCELifier bcelifier = new BCELifier("MyClass", System.out); bcelifier.start();
You can see the result on the console for the whole class (how to build byte code MyClass.java). The code for the function is this:
private void createMethod_1() { InstructionList il = new InstructionList(); MethodGen method = new MethodGen(ACC_PUBLIC, Type.STRING, new Type[] { Type.INT, Type.INT }, new String[] { "arg0", "arg1" }, "maxAsString", "MyClass", il, _cp); il.append(InstructionFactory.createLoad(Type.INT, 1)); // Load first parameter to address 1 il.append(InstructionFactory.createLoad(Type.INT, 2)); // Load second parameter to adress 2 BranchInstruction if_icmple_2 = InstructionFactory.createBranchInstruction(Constants.IF_ICMPLE, null); // Do if condition (compare a > b) il.append(if_icmple_2); il.append(InstructionFactory.createLoad(Type.INT, 1)); // Load value from address 1 into the stack il.append(_factory.createInvoke("java.lang.Integer", "valueOf", new ObjectType("java.lang.Integer"), new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); il.append(InstructionFactory.createReturn(Type.OBJECT)); InstructionHandle ih_13 = il.append(InstructionFactory.createLoad(Type.INT, 1)); il.append(InstructionFactory.createLoad(Type.INT, 2)); BranchInstruction if_icmpge_15 = InstructionFactory.createBranchInstruction(Constants.IF_ICMPGE, null); // Do if condition (compare a < b) il.append(if_icmpge_15); il.append(InstructionFactory.createLoad(Type.INT, 2)); il.append(_factory.createInvoke("java.lang.Integer", "valueOf", new ObjectType("java.lang.Integer"), new Type[] { Type.INT }, Constants.INVOKESTATIC)); il.append(_factory.createInvoke("java.lang.Integer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); il.append(InstructionFactory.createReturn(Type.OBJECT)); InstructionHandle ih_26 = il.append(new PUSH(_cp, "equals")); // Return "equals" string il.append(InstructionFactory.createReturn(Type.OBJECT)); if_icmple_2.setTarget(ih_13); if_icmpge_15.setTarget(ih_26); method.setMaxStack(); method.setMaxLocals(); _cg.addMethod(method.getMethod()); il.dispose(); }
The Dragon Book is too complicated. So ignore it as a starting point. It is good and makes you think a lot once you already have a starting point, but for starters, perhaps you should simply try to write an math/logical expression evaluator using RD, LL or LR parsing techniques with everything (lexing/parsing) written by hand in perhaps C/Java. This is interesting in itself and gives you an idea of the problems involved in a compiler. Then you can jump in to your own DSL using some scripting language (since processing text is usually easier in these) and like someone said, generate code in either the scripting language itself or C. You should probably use flex/bison/antlr etc to do the lexing/parsing if you are going to do it in c/java.