我对编程很了解,并且遇到过各种语言,包括BASIC,FORTRAN,COBOL,LISP,LOGO,Java,C ++,C,MATLAB,Mathematica,Python,Ruby,Perl,JavaScript,Assembly等。我无法理解人们如何创建编程语言并为其设计编译器。我也无法理解人们如何创建Windows,Mac,UNIX,DOS等操作系统。对我来说,另一件事是神秘的,人们如何创建诸如OpenGL,Op​​enCL,OpenCV,Cocoa,MFC等的库。我无法弄清的最后一件事是科学家如何设计微处理器的汇编语言和汇编器。我真的很想学习所有这些知识,我今年15岁。我一直想成为计算机科学家,例如Babbage,Turing,Shannon或Dennis Ritchie。


我已经读过Aho的Compiler Design和Tanenbaum的OS概念书,他们都只讨论概念。和高层次的代码。它们不涉及细节和细微差别,也不涉及如何设计编译器或操作系统。我想要一种具体的理解,以便我自己创建一个,而不仅仅是对线程,信号量,进程或解析的理解。我问我哥哥这一切。他是麻省理工学院EECS的SB学生,对如何在现实世界中实际创建所有这些东西一无所知。他所知道的只是对编译器设计和OS概念的理解,就像你们提到的(即线程,同步,并发,内存管理,词法分析,中间代码生成等)一样。 >

评论

如果您使用的是Unix / Linux,则可以获得有关专用工具的信息:lex,yacc和bison。

我的第一个建议是读Aho的《龙书》。 amazon.com/Compilers-Principles-Techniques-Alfred-Aho/dp / ...

也许不太有用,但我建议您浏览sites.google.com/site/steveyegge2/blog-rants(史蒂夫·耶格的博客)和steve-yegge.blogspot.com/(史蒂夫·耶格的另一个博客)。
尽可能多地学习编程语言。这样,您将从他们的概念以及他们的错误中学习。当您可以站在巨人的肩膀上时,为什么对小矮人感到满足?

提示:解释器比编译器容易;它只是一个类,它基于逐行读取的输入文本来“执行某些操作”。另一个提示:将其绑定到反射,您可以使用脚本控制任意对象。

#1 楼

基本上,您的问题是“如何设计和实现计算机芯片,指令集,操作系统,语言,库和应用程序?”这是一个价值数十亿美元的全球性行业,拥有数百万名员工,其中许多人是专家。您可能想更多地关注您的问题。

那,我可以破解:


我不明白人们是如何创建编程的语言并为此设计出编译器。


我感到惊讶,但是很多人确实认为编程语言是神奇的。当我遇到参加聚会或其他场合的人时,如果他们问我要做什么,我告诉他们我设计编程语言并实现编译器和工具,令人惊讶的是,人们(专业程序员,介意您)说的次数“哇,我没想过,但是,是的,有人必须设计那些东西。”就像他们认为语言已经由周围的工具基础结构完全形成一样。

它们并没有出现。语言的设计与其他任何产品一样:通过在竞争可能性之间进行一系列权衡来进行。编译器和工具的构建方式与其他任何专业软件产品一样:通过分解问题,一次编写一行代码,然后测试所得程序的技巧来实现。

语言设计是一个巨大的话题。如果您对设计语言感兴趣,那么一个不错的起点就是考虑一下您已经知道的某种语言的不足。设计决策通常是由考虑其他产品的设计缺陷引起的。

或者,考虑您感兴趣的域,然后设计一种特定于域的语言(DSL),该语言指定该域中问题的解决方案。您提到了LOGO;这是“线条图”域的DSL的一个很好的例子。正则表达式是用于“在字符串中查找模式”域的DSL。 C#/ VB中的LINQ是用于“过滤,联接,排序和项目数据”域的DSL。 HTML是用于“描述页面上文本布局”域的DSL,依此类推。有许多领域适合基于语言的解决方案。我最喜欢的一个是Inform7,它是用于“基于文本的冒险游戏”域的DSL。它可能是我见过的最高级别的严肃编程语言。选择一个您了解的领域,并考虑如何使用语言来描述该领域中的问题和解决方案。确定什么是合法程序和非法程序的规则是什么?通常,您需要在三个级别上执行此操作:


词法:该语言中单词的规则是什么,哪些字符是合法的,数字是什么样的等等。

语法:语言的单词如何组合成更大的单位?在C#中,较大的单元是诸如表达式,语句,方法,类之类的东西。

语义:给定句法上合法的程序,您如何确定该程序的作用? br />尽可能精确地写下这些规则。如果您做得很好,则可以将其用作编写编译器或解释器的基础。看一下C#规范或ECMAScript规范,了解我的意思;它们充满了非常精确的规则,这些规则描述了什么构成法律程序以及如何确定法律程序的作用。

开始编写编译器的最佳方法之一是编写从高级语言到高级语言的编译器。编写一个使用您的语言的字符串并使用C#或JavaScript或您碰巧知道的任何语言吐出字符串的编译器;让该语言的编译器负责将其转换为可运行代码的繁重工作。

我写了一篇有关C#,VB,VBScript,JavaScript和其他语言和工具的设计的博客;如果您对此主题感兴趣,请查看。 http://blogs.msdn.com/ericlippert(历史记录)和http://ericlippert.com(当前记录)

在这里,我列出了C#编译器在语义分析过程中为您执行的大多数任务。如您所见,有很多步骤。我们将大的分析问题分解为一系列可以单独解决的问题。

http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-最后,如果您正在寻找一份工作,当您年纪大一点的时候就从事这种工作,那么可以考虑以微软的大学实习生身份进入开发人员部门。那就是我今天结束工作的方式!

评论


您是否已经写过关于CLR可以自动完成编译器优化的程度?

–user1249
2011年6月16日在8:48

@Thorbjørn:让我们弄清楚术语。 “编译器”是将一种编程语言转换为另一种编程语言的任何设备。关于拥有将C#转换为IL的C#编译器和将IL转换为机器代码的IL编译器(“抖动”)的好处之一是,您可以将C#编译器编写为IL(很简单!)将处理器特定的优化置于抖动中。这并不是说编译器优化没有“完成”,而是jit编译器团队为我们做了优化。参见blogs.msdn.com/b/ericlippert/archive/2009/06/11/…

–埃里克·利珀特
2011年6月16日13:19



@ Cyclotis04:Inform6编译为Z代码,这是基于字节码的虚拟机的一个非常著名的早期示例。这就是1980年代所有Infocom游戏都可以既大于内存又可以移植到多种架构的方式。游戏被编译为Z代码,然后为多台计算机实现了带有代码存储分页的Z代码解释器。如今,当然,如果需要,您可以在手表上运行zcode解释器,但那是高科技时代。有关详细信息,请参见en.wikipedia.org/wiki/Z-machine。

–埃里克·利珀特
2011年7月9日在3:36



@EricLippert编译器不是设备,设备是包含硬件的东西。我们可以说是一个预定义的程序,该程序具有一组将输入数据转换为机器代码的规则

–达兰
2014年5月23日6:47



@dhams:设备是为特定目的而制造的任何东西。我曾经编写的每个编译器都在专门用于允许编译器存在的硬件上运行。

–埃里克·利珀特
2014年5月23日6:50



#2 楼

您可能会发现Jack Crenshaw撰写的Lets Build a Compiler有趣地介绍了编写编译器和汇编语言。

评论


Crenshaw简介的有趣之处在于它就结束了(破坏者:它是不完整的),大约是在您碰到会让您意识到的问题的时候,嘿,我真的应该在开始实施它之前就已经完全设计了我的语言。然后您说,嘿,如果我必须编写完整的语言规范,为什么不以正式的符号来表示它,然后我就可以将其输入工具以生成解析器了?然后您像其他所有人一样进行操作。

– Kindall
2011年6月16日在22:28

@kindall,您需要手工完成,以便意识到有理由使用这些工具。

–user1249
2011年6月17日下午6:57

#3 楼

“我真的很想学习这些东西”。如果您长期认真,请:


上大学,专攻软件工程。取得所有可以获得的编译器类。那些上课的人比你受过更好的教育和更有经验;最好利用他们的专家观点以从未阅读代码的方式向您提供信息。
坚持从高中开始上数学课,并继续四年大学学习。专注于非标准数学:逻辑,群论,元数学。这将迫使您进行抽象思考。它使您能够阅读有关编译的高级理论论文,并理解为什么这些理论有趣而有用。如果您永远想落后于最新技术,则可以忽略这些高级理论。
收集/阅读标准编译器文本:Aho / Ullman等。它们包含社区普遍认为的基本知识。您可能不会使用这些书中的所有内容,但您应该知道它的存在,并且应该知道为什么不使用它。我以为Muchnick很棒,但这是针对高级主题的。
构建编译器。现在开始建立一个烂的。这将教您一些问题。建立第二个。重复。这种经验与您的书籍学习建立了巨大的协同作用。
一个真正好的起点是学习BNF(Backus Naur Form),解析器和解析器生成器。 BNF在编译器领域得到了有效的普遍使用,如果您不了解BNF,您将无法与之交谈。

如果您想对编译有一个很好的入门介绍,并且BNF的直接价值不仅仅是作为文档,而是作为一种工具可处理的语言,请参阅本教程(不是我的文章),该教程基于“ 1964年的论文(是的,您没看错)[Val Schorre撰写的“ META II,一种面向语法的编译器编写语言”。 (http://doi.acm.org/10.1145/800257.808896)]
此恕我直言是有史以来撰写过的唯一最好的comp-sci论文之一:它教您用10页构建编译器。我最初是从本文中学到的。

我上面写的内容来自个人经验,我认为它对我很有帮助。 YMMV,但是恕我直言,不是很多。

评论


-1以上都不是必需的。

–尼尔·巴特沃思(Neil Butterworth)
2011年6月15日在20:38

@nbt以上都不是必需的。但是以上所有这些都有帮助。真的很多

–康拉德·鲁道夫(Konrad Rudolph)
11年6月16日在8:56

我特别不同意“学习数学以进行抽象思考!”建议。即使您认为“学习抽象思维”对于创建自己的编程语言和编译器特别有帮助(我不知道,我发现通过实践来学习比通过绕行,难以置信的间接路线要有用得多) ,数学不是唯一具有抽象思想的领域! (我是一个数学家,所以我一般不否认使用数学,只是数学在这种特殊情况下的适用性...)

– grautur
11年6月16日在21:14

如果您想阅读有关编译器理论的高级技术论文,则最好在数学上胜任。您可以决定忽略该文献,因此您的理论以及相应的编译器将因此变得更糟。这里的反对者都指出,您可以在无需大量正规教育的情况下构建编译器,我同意。他们似乎暗示您可以在没有它的情况下构建非常好的编译器。我不想打赌。

–伊拉克·巴克斯特
2011年6月16日在21:20



CS是一门真正对语言设计和实现有用的学科。当然不是强制性的,但是已经有数十年的研究可以并且应该被利用,而且根本没有理由重复其他错误。

–研究员
2011年6月17日21:36

#4 楼

这是您可以遵循的在线书籍/课程,名为《计算系统的要素:从第一原理构建现代计算机》。

实际上,使用模拟器可以从头开始构建完整的计算机系统。尽管许多评论者都说您的问题过于笼统,但实际上这本书在保持可管理性的同时回答了它。完成后,您将使用高级语言(您设计的)编写游戏,该游戏使用自己的操作系统功能,并由编译器编译为您设计的VM语言。由VM转换器翻译为汇编语言(由您设计),由汇编程序汇编为机器代码(由您设计),该汇编程序在计算机系统上运行,并通过使用布尔逻辑和一种简单的硬件描述语言。

各章:


课程概述组合芯片
顺序芯片
机器语言
计算机体系结构
汇编程序
虚拟机I:算术
虚拟机II:控制
编程语言
编译器I:语法分析
编译器II:代码生成
操作系统
列表项

更多有趣的事情

评论


感谢您的修改,不知名的人。我尝试了几次,但无法将自己的思想集中在描述上……但是不想不想提到这本书。该书现已在学习计划链接上在线发布:www1.idc.ac.il/tecs/plan.html。它在网上的价格也很合理。享受大家。

– Joe Internet
2011年6月16日15:26

我打算自己提出这个建议...对于那些懒惰的人,请查看10分钟的介绍:从NAND到俄罗斯方块的12个步骤@ youtube.com/watch?v=JtXvUoPx4Qs

–理查德·安东尼·海因(Richard Anthony Hein)
2011年6月16日19:25

#5 楼

退后一步。编译器只是一个将一种语言的文档翻译成另一种语言的文档的程序。两种语言都应该定义明确且特定。

这些语言不必是编程语言。它们可以是任何可以写下规则的语言。您可能已经看过Google Translate;那是编译器,因为它可以将一种语言(例如德语)翻译成另一种语言(也许是日语)。

编译器的另一个示例是HTML呈现引擎。它的输入是一个HTML文件,输出是一系列在屏幕上绘制像素的指令。

当大多数人谈论编译器时,他们通常是指翻译高级别的编程语言(例如Java,C,Prolog)转换为低级的一种语言(汇编或机器代码)。这可能令人生畏。但是,如果您以通才的观点认为编译器是将一种语言翻译成另一种语言的程序,那还不错。

您可以编写一个使字符串中的每个单词都反向的程序吗?例如:

When the cat's away, the mice will play.


成为

nehW eht s'tac yawa, eht ecim lliw yalp.


写这个程序并不难,但是您需要想想一些事情:


什么是“单词”?您可以定义由哪个字符组成一个单词吗?
单词在哪里开始和结束?
单词是否仅由一个空格分隔,或者可以多于或少于一个空格?
标点符号是否需要?也可以颠倒吗?
一个单词中的标点符号怎么办?
大写字母怎么办?

这些问题的答案有助于明确定义该语言。现在继续编写程序。恭喜,您已经编写了一个编译器。

如何?您可以编写一个程序,该程序需要一系列绘图指令并输出PNG(或JPEG)文件吗?也许是这样的:

image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99


再次,您需要做一些思考来定义语言:


什么是原始指令?
“ line”一词后面会出现什么? “颜色”之后会怎样?同样对于“背景”,“框”等。
什么是数字?
是否允许输入空文件?
可以大写单词吗?
是否为负数?允许使用数字吗?
如果不给出“图像”指令怎么办?
不指定颜色可以吗?回答,但是如果您可以确定它们的名称,那么您已经定义了一种语言。猜猜您编写的用于翻译的程序是编译器。

您知道,编写编译器并不难。您在Java或C语言中使用的编译器只是这两个示例的较大版本。所以去吧!定义一种简单的语言并编写程序以使该语言有所作为。迟早您将要扩展语言。例如,您可能要添加变量或算术表达式。您的编译器将变得更加复杂,但是您将了解它的每一个细节,因为您自己编写了它。语言和编译器就是这样产生的。

评论


myFirstCompiler =(str)->(“” +(str ||“”))。split('')。reverse()。join(''); jsfiddle.net/L7qSr

–拉里·巴特
2012年5月18日在22:29



#6 楼

如果您对编译器设计感兴趣,请查阅《 Dragon Book》(官方标题:Compilers:Principles,Techniques和Tools)。它被广泛认为是关于该主题的经典书籍。

评论


请注意,您可能需要更多实际经验才能充分利用本书。很好的参考。

–user1249
2011年6月15日在20:09

-1只有没有看过书的人才能认为龙书有什么用。特别是它没有解决这个问题。

–尼尔·巴特沃思(Neil Butterworth)
2011年6月15日20:26

龙书?热情的15岁?我希望他再保持一段时间的热情。

– David Thornley
2011年6月15日20:45

一个更易于访问的替代方法:“编程语言实用程序” 3e。

–willjcroz
2011年6月16日15:15

@DavidThornley不要完全把他排除在外(是的,我知道这是一个非常古老的职位)。我从15岁开始研究语言的工作方式,并专门研究虚拟机。现在我16岁,经过几个月的研究,编写和重写,我对自己满意的工作解释器和编译器感到满意。

–大卫
16年5月28日18:00



#7 楼

已经建议“让我们构建一个编译器”。有一个使用Haskell而不是Turbo Pascal的“现代化”版本:http://alephnullplex.appspot.com/blog/view/2010/01/12/lbach-1-introduction

与Haskell保持联系,其中有一个非常有启发性的Scheme解释器,可以提供进一步的想法:48小时内编写自己的方案

#8 楼

不要相信编译器或操作系统有什么魔力:没有。还记得您编写的用于计算字符串中所有元音或将数组中的数字相加的程序吗?编译器在概念上没有什么不同。

每个程序都有三个阶段:


读取一些东西
处理这些东西:将输入数据转换为输出数据
编写一些其他内容–输出数据

考虑一下:编译器的输入是什么?源文件中的字符串。

编译器输出什么?代表目标计算机的机器指令的字节字符串。

那么,编译器的“处理”阶段是什么?该阶段做什么?

如果您认为编译器(如任何其他程序)必须包括这三个阶段,那么您将对编译器的构造有一个很好的了解。

评论


正如尼尔所说,是真实的但没有用。基本的编译器方面(例如递归语法和符号表)在直观上并不明显。

–梅森·惠勒
2011年6月15日在21:06

@Mason Wheeler:我认为任何渴望写出编译器(并设计目标语言?)的人都最有可能认为递归语法和符号表是非常基本的概念。

–FumbleFingers
2011年6月16日下午2:50

#9 楼

我不是专家,但是这是我的目的:

您似乎并没有在询问编写编译器,而只是在询问汇编器。这真的不是魔术。

从SO窃取别人的答案(https://stackoverflow.com/questions/3826692/how-do-i-translate-assembly-to-binary),汇编看起来像这样:

label:  LDA #
$A9 
$A9 q4312078q C  q4312078q
C q4312078q
JMP label


然后通过汇编器运行它,并变成这样的东西:

q4312078q

这样就把所有东西都压扁了:

您不能在记事本中写该字,因为记事本使用ASCII(不是十六进制)。您将使用十六进制编辑器,或仅以编程方式将字节写出。您将十六进制写出到文件中,将其命名为“ a.exe”或“ a.out”,然后告诉操作系统运行它。

当然,现代CPU和操作系统确实非常实用。复杂,但这是基本思想。

如果要编写新的编译器,请按以下步骤操作:

1)在pyparsing中使用类似计算器示例的方式编写解释性的语言(或其他好方法)解析框架)。这样您就可以快速掌握解析的基本知识。

2)编写翻译器。将您的语言翻译成Javascript。现在您的语言将在浏览器中运行。

3)写一个翻译器到较低级别的东西,例如LLVM,C或Assembly。

您可以在这里停止,这是编译器。它不是一个优化的编译器,但这不是问题。您可能还需要考虑编写链接程序和汇编程序,但是您真的要这样做吗?

4)(疯狂)编写优化器。大型团队为此工作了数十年。

4)(Sane)参与现有社区。 GCC,LLVM,PyPy,负责任何口译员的核心团队。

#10 楼

其他几个人给出了很好的答案。我只会添加一些建议。首先,关于您要尝试做的一本好书是Appel的Modern Compiler Implementation文本(选择C,Java或Standard ML)。本书为您提供了一种简单的编译器的完整实现,该编译器使用了简单的语言Tiger到可以在仿真器中运行的MIPS程序集,以及最少的运行时支持库。对于使编译语言正常工作所需的所有步骤而言,这是一本非常不错的书。

Appel将带您了解如何编译预先设计但不会花费太多钱的语言。讨论各种语言功能的含义或如何根据它们在设计自己的功能时的相对优势来考虑它们。在这方面,编程语言:概念和构造是不错的。 《计算机编程的概念,技术和模型》也是一本深入思考语言设计的好书,尽管它是在单一语言(Oz)的背景下进行的。

最后,我提到Appel用C,Java和Standard ML编写了他的文章-如果您对编译器的构造和编程语言很认真,我建议学习ML并使用该版本的Appel。 ML系列语言的强类型系统主要是功能性的-与许多其他语言不同的功能,因此,如果您还不知道功能性语言,则学习它们会磨练您的语言水平。而且,它们的模式匹配和功能思维方式非常适合您在编译器中经常需要进行的操作,因此,以ML为基础的语言编写的编译器通常比用C编写的编译器更短,更容易理解, Java或类似语言。 Harper关于Standard ML的书是一个很好的入门指南。通过这一工作,您应该可以准备学习Appel的Standard ML编译器实施手册。如果您学习标准ML,那么接起OCaml进行以后的工作也将非常容易。 IMO,它为正在工作的程序员提供了更好的工具(与周围的OS环境更干净地集成,易于生成可执行程序,并且具有一些出色的编译器构建工具,例如ulex和Menhir)。

> 1作为长期参考,我更喜欢《龙书》,因为它有更多关于我可能要引用的内容的详细信息,例如解析器算法的内部工作原理,并且涵盖了各种方法,但是Appel的书非常好第一遍。基本上,Appel会教您一种通过编译器进行整体处理的方法,并指导您完成编译器。 《龙书》更详细地介绍了不同的设计替代方案,但提供的工作指导却少得多。


编辑:用Sethi替换不正确的Aho参考,并提及CTMCP。 >

评论


gh,我的大学口译班上有程序语言基础。那是AWFUL。我个人甚至喜欢该方案,并且不介意语法,这是作者对概念的不良解释对我造成了破坏。

– Greg Guida
2011年7月9日在21:09

我喜欢Appel的续篇进行编译,但我确实发现他的书具有很多先验知识。

– J D
2012年10月10日19:06

#11 楼

我必须为大学的课堂创建一个编译器。

执行此操作的基础并不像您想象的那么复杂。第一步是创建语法。想一想英语的语法。以相同的方式,您可以分析具有主语和谓语的句子。有关阅读上下文无关文法的更多信息。

一旦掌握了语法(语言的规则),编写编译器就像遵循这些规则一样简单。编译器通常会翻译成机器代码,但除非您想学习x86,否则建议您看一下MIPS或制作自己的虚拟机。

编译器通常由两部分组成,一个扫描器和一个解析器。基本上,扫描程序会读取代码并将其分离为令牌。解析器查看这些令牌的结构。然后,编译器将按照一些相当简单的规则进行操作,以将其转换为所需的任何代码(汇编,字节码等中间代码等)。如果将其分解成越来越小的碎片,那么最终一点都不令人畏惧。

祝你好运!

评论


概念上简单吗?是。其实很简单?没有。

–尼尔·巴特沃思(Neil Butterworth)
2011年6月15日20:44



嗯扫描/解析后,编译器需要进行类型检查/推断,优化,寄存器分配等。这些步骤非常简单。 (使用解释的代码时,只需将这些部分推迟到运行时阶段即可。)

– Macke
11年6月16日在7:31

我不投赞成票:编译器有两个基本部分,其中一个是构建程序的抽象描述(通常分为扫描和解析),另一个是在某些情况下再次编写该抽象描述的版本。其他形式(例如,机器代码)。 (附带说明:优化编译器通常会在写出抽象描述之前尝试对其进行改进,但这只是一种改进。)

–研究员
2011年6月17日在21:41

#12 楼

Petzold的《代码》一书很好地介绍了非技术人员和技术人员,从最初的原理开始。它具有很高的可读性,并且范围广,不会陷入太多的麻烦。

#13 楼

您可能想在StackOverflow:学习编写编译器上检查这个出色的问题(和答案)。它包含大量资源。

#14 楼

这个线程有很好的答案,但是我只是想添加我的,因为我也曾经遇到过同样的问题。 (另外,我想指出的是,Joe-Internet推荐的这本书是一本很好的资源。)

首先是计算机如何工作的问题?输入->计算->输出。

首先考虑“计算”部分。
稍​​后我们将研究输入和输出的工作方式。

计算机基本上是由一个处理器(或CPU)和一些内存(或RAM)组成。处理器是一个小工具,可以从内存中获取数据,根据该数据执行某些操作,然后将一些数据写回到内存中。处理器如何从内存中读取数据后找出要读取的内容和要做什么?
以下是一个非常简单的视图。
处理器本质上由两部分组成。一个是一组内置于处理器中的内存位置,用作其工作内存。这些被称为“寄存器”。第二个是一堆用于使用寄存器中的数据执行某些操作的电子机器。有两个特殊的寄存器,分别称为“程序计数器”或“ pc”和“指令寄存器”或“ ir”。
处理器将内存分为三部分。第一部分是“程序存储器”,用于存储正在执行的计算机程序。第二个是“数据存储器”。第三个用于某些特殊目的,我们将在后面讨论。
程序计数器包含要从程序存储器中读取的下一条指令的位置。指令计数器包含一个数字,表示正在执行的当前操作。处理器可以执行的每个操作均由称为该操作的操作码的数字来引用。计算机的本质工作原理是将程序计数器引用的存储器位置读入指令寄存器(并递增程序计数器,使其指向下一条指令的存储器位置)。接下来,它读取指令寄存器并执行所需的操作。例如,指令可能是将特定的存储器位置读取到寄存器中,或者使用两个寄存器的值写入某个寄存器或执行某些操作,然后将输出写入第三个寄存器。

现在计算机如何执行输入/输出?我将提供一个非常简化的答案。
请参见http://en.wikipedia.org/wiki/Input/output和http://en.wikipedia.org/wiki/Interrupt。更多。
它使用两件事,即内存的第三部分和称为中断的东西。连接到计算机的每个设备都必须能够与处理器交换数据。它使用前面提到的内存的第三部分来实现。处理器为每个设备分配一个内存片,并且设备和处理器通过该内存片进行通信。但是,处理器如何知道哪个位置指的是什么设备,以及设备何时需要交换数据?中断就是从这里来的。中断本质上是向处理器发出的信号,它暂停当前的内容并将其所有寄存器保存到已知位置,然后开始执行其他操作。中断很多,每个中断都有一个唯一的编号。对于每个中断,都有一个与之关联的特殊程序。当发生中断时,处理器执行与该中断相对应的程序。现在,取决于BIOS以及硬件设备如何连接至计算机主板,每个设备都会获得唯一的中断和一片内存。在BIOS的帮助下启动操作系统时,将确定每个设备的中断和内存位置,并为中断设置特殊程序以正确处理设备。因此,当设备需要一些数据或想要发送一些数据时,它将发出中断信号。处理器暂停正在执行的操作,处理中断,然后返回到正在执行的操作。中断有很多种,例如硬盘驱动器,键盘等。一个重要的中断是系统计时器,它会定期调用中断。还有一些可以触发中断的操作码,称为软件中断。

现在我们几乎可以了解操作系统的工作原理。当它启动时,操作系统会设置一个定时器中断,以便它以固定的时间间隔控制操作系统。它还会设置其他中断来处理其他设备等。现在,当计算机运行一堆程序时,发生定时器中断,操作系统将获得控制权并执行重要任务,例如进程管理,内存管理等。操作系统通常还提供程序访问硬件设备的一种抽象方式,而不是让它们直接访问设备。当程序要访问设备时,它会调用os提供的一些代码,然后与该设备通信。这些涉及很多问题,涉及并发,线程,锁,内存管理等。

现在,理论上可以使用操作码直接编写程序。这就是所谓的机器代码。这显然是非常痛苦的。现在,处理器的汇编语言不过是这些操作码的助记符,这使得编写程序变得更加容易。简单的汇编程序是一个程序,该程序采用汇编语言编写的程序,并用适当的操作码替换助记符。

如何设计处理器和汇编语言。要知道您必须阅读一些有关计算机体系结构的书。 (请参阅joe-internet引用的书的第1-7章)。这涉及到学习布尔代数,如何构建简单的组合电路以进行加法,乘法等,如何构建存储器和顺序电路,如何构建微处理器等。

现在,如何编写计算机语言。首先可以用机器代码编写一个简单的汇编器。然后使用该汇编器为C的简单子集编写编译器。然后使用C的子集编写更完整的C版本。最后使用C编写更复杂的语言,例如python或C ++。当然,要编写一种语言,您必须首先设计一种语言(与设计处理器一样)。再次看一些有关这方面的教科书。

以及如何编写操作系统。首先,您以x86之类的平台为目标。然后,您可以弄清楚它是如何启动的以及何时调用您的操作系统。典型的PC会以此方式启动。它启动,BIOS执行一些测试。然后BIOS读取HDD的第一个扇区并将内容加载到内存中的特定位置。然后,它设置cpu以开始执行此加载的数据。这就是您被调用的点。此时,典型的操作系统会加载其自身的剩余内存。然后,它初始化设备并设置其他内容,最后用登录屏幕向您致意。

因此,要编写OS,必须编写“ boot-loader”。然后,您必须编写代码来处理中断和设备。然后,您必须编写用于过程管理,设备管理等的所有代码。然后,您必须编写一个api,该api允许在您的操作系统中运行的程序访问设备和其他资源。最后,您必须编写代码,从磁盘读取程序,将其设置为进程并开始执行。为了辩护,我现在是理论上的研究生,所以我忘记了很多这些东西。但是您可以在Google上搜索很多这些东西,以了解更多信息。

#15 楼

我记得自己在编程生涯中处于与您相似的困惑状态的那一点:我已经读了很多有关该理论的文章,​​例如《龙》,《老虎》(红色),但还没有太多

将它们组合在一起的线索是找到一个具体的项目要做(然后发现我只需要所有理论的一小部分即可)。

Java VM为我提供了一个很好的起点:从概念上讲它是一个“处理器”,但是它是从实际CPU的混乱细节中高度抽象出来的。它还提供了学习过程中一个重要且经常被忽视的部分:将它们拆散,然后再重新组合在一起(就像以前的孩子们以前用收音机做的那样)。以及Java中的Hello,World类。阅读JVM规范并尝试了解发生了什么。这将使您对编译器的工作有深入的了解。

然后尝试创建创建Hello,World类的代码。 (实际上,您正在针对一种高度专业化的语言创建特定于应用程序的编译器,在该语言中您只能说Hello,World。)

尝试编写能够在Hello,World中读取的代码用其他某种语言编写,并输出相同的类。做到这一点,以便您可以将字符串从“ Hello,World”更改为其他名称。 ”。将此类分开,编写一个可以重新组合在一起的“玩具编译器”。

#16 楼

1)华盛顿大学的精彩视频讲座:

CSE P 501编译器构造-2009年秋季
www.cs.washington.edu/education/courses/csep501/09au/lectures/video。 html *

2)SICP http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/
和这本书同名。

3)另外,关于函数式编程,Haskell,lambda演算,语义(包括名词性)和函数式语言的编译器实现。
您如果您已经知道Haskell,则可以从2005-SS-FP.V10.2005-05-24.HDV开始。
Uxx视频就是答案。请先关注Vxx视频。

http://video.s-inf.de/#FP.2005-SS-Giesl.(COt).HD_Videoaufzeichnung

(视频用英语,其他课程用德语。)


新用户最多只能发布两个超链接。


#17 楼

ANTLR是一个很好的起点。这是一个语言生成框架,类似于Lex和Yacc。有一个名为ANTLRWorks的GUI可以简化过程。我已经编写了一种称为Zentrum的表达语言,该语言使用DLR生成代码。它将向您展示如何解析和执行静态和动态类型的表达式。

#18 楼

为了简单地介绍编译器的工作方式以及如何创建自己的编程语言,我推荐一本新书http://createyourproglang.com,该书更多地侧重于语言设计理论,而无需了解OS / CPU内部结构,即词法分析器,解析器。 ,解释器等。

它使用与创建最近流行的Coffee Script和Fancy编程语言相同的工具。

#19 楼

如果您说的都是真的,那么您就拥有一个有前途的研究人员的身份,并且只有一种方式就可以获得具体的理解:学习。我的意思不是说“读这个天才写的所有这些高级计算机科学书籍(特别是这些)!”;我的意思是:您必须与高水平的人在一起,才能成为像Charles Babbage,Alan Turing,Claude Shannon或Dennis Ritchie这样的计算机科学家。我并没有轻视自学成才的人(我是其中之一),但是没有多少人喜欢你。我认真推荐斯坦福大学的符号系统程序(SSP)。正如他们的网站所说:
斯坦福大学的符号系统程序(SSP)专注于
计算机和心灵:人工和自然系统,使用符号
表示信息。 SSP将
学生和教职员工聚集在一起
对人机关系的不同方面感兴趣的人,
包括...


< br认知科学:研究人类智能,自然语言,
和大脑作为计算过程


人工智能:使计算机具有类似于人的行为
和理解;和


人机交互:设计与人
用户良好配合的计算机软件和
界面。



#20 楼

我将建议一些超出范围的内容:学习Python(或者也许是Ruby,但是我在Python方面有很多经验,因此我将在这里进行讨论)。不仅要涉足其中,而且还要深入了解它。

我建议这样做的原因有很多:


Python是一个例外设计精良的语言。虽然它有一些疣,但它的恕我直言比许多其他语言要少。如果您是一位崭露头角的语言设计师,那么最好将自己暴露于尽可能多的良好语言。
Python的标准实现(CPython)是开源的,并且有据可查,因此可以更轻松地理解该语言如何在
Python被编译成一个简单的字节码,比汇编更容易理解,并且在所有运行Python的平台上都一样。因此,您将了解编译(因为Python确实将源代码编译为字节代码)和解释(因为此字节代码在Python虚拟机中进行解释)。
Python有很多建议的新功能,以编号记录PEP(Python增强建议)。阅读PEP有趣,可以了解语言设计师在选择功能实现方式之前如何考虑实现功能。 (在这方面,仍在考虑中的PEP尤其有趣。)
Python具有来自各种编程范例的多种功能,因此您将学习解决问题的各种方法,并考虑到更多的工具包括您自己的语言。
Python使得通过装饰器,元类,导入钩子等以各种方式扩展语言变得非常容易,因此您可以在不实际离开语言的情况下使用新的语言功能。 (顺便说一句:代码块是Ruby中的一流对象,因此您实际上可以编写新的控制结构,例如循环!我给Ruby程序员不必考虑扩展语言的印象,这就是您可以使用Ruby编程。但这非常酷。)
在Python中,您实际上可以反汇编由编译器生成的字节码,甚至可以从头开始编写自己的字节码,然后让解释器执行它(我自己做了,这是令人费解的,但很有趣。)
Python有很好的解析库。您可以将Python代码解析为抽象语法树,然后使用AST模块对其进行操作。 PyParsing模块可用于解析任意语言,例如您设计的语言。从理论上讲,您可以根据需要使用Python编写第一个语言编译器(并且它可以生成C,汇编语言甚至Python输出)。您将开始认识以您使用的语言学习过的概念,反之亦然。

玩得开心!

评论


不是要深入研究python,而是要害。这个孩子已经有N种语言,可以表示N种语言; N的增加不会有太大的区别。以C为例。这是标准的。它有很多图书馆。它是跨平台的(当您遵循标准时)。您可以反汇编输出。您可以编写CFront。等等。

–伊恩
2012年12月31日在3:44

#21 楼

好吧,我想您的问题可以改写为“计算机科学学位的核心实践概念是什么”,而总的答案当然是要获得自己的计算机科学学士学位。

从根本上说,您可以通过以下操作来创建自己的编程语言编译器:读取文本文件,从文本文件中提取信息,然后根据从文本文件中读取的信息对文本进行转换,直到将其转换为可以由加载程序读取(请参阅Levine的链接程序和加载程序)。琐碎的编译器在初次完成时是一个相当严格的项目。

操作系统的心脏是内核,它管理资源(例如,内存分配/重新分配),并在任务/​​进程/之间切换程序。

汇编器是文本->字节转换。

如果您对此内容感兴趣,我建议在Linux中编写一个X86汇编器,该汇编器支持某些功能。标准X86程序集的子集。这将是一个非常简单的切入点,并向您介绍这些问题。这不是一个小项目,并且会教给您很多东西。

我建议用C语言编写; C是该级别工作的通用语言。

评论


另一方面,这是非常高级的语言的好地方。只要可以指定文件中的各个字节,就可以使用任何语言制作编译器/汇编器(这更容易)。说,perl。或VBA。天堂,可能性!

–伊恩
2012年12月31日下午3:48

#22 楼

请参阅肯尼思·劳登(Kenneth Louden)的书,“编译器构造”进行编译器开发。

人们边做边学。只有少数人可以看到符号在板上乱涂,并立即从理论跳到实践。不幸的是,这些人通常是教条主义,原教旨主义者,并且对此大声疾呼。

#23 楼

我很荣幸能以PDP-8作为我的第一门汇编语言。 PDP-8只有六个指令,这些指令是如此简单,以至于很难想象它们是由几个谨慎的组件实现的,实际上它们是。它确实消除了计算机中的“魔术”。

另一个启示的门户是Knuth在他的示例中使用的“混合”汇编语言。 “ Mix”在今天看来已经过时,但仍然具有DE神秘的效果。

#24 楼

编译器和编程语言(以及所有内容,包括在构建语言中的所有内容,例如定义有限的语法并转换为汇编语言)都是一项非常复杂的任务,需要对整个系统有大量的了解。这种类型的课程通常作为大学的3/4年级Comp Sci课程提供。

我强烈建议您首先全面了解操作系统以及如何编译/执行现有语言。 (例如,本机(C / C ++),在VM(Java)中或在解释器(Python / Javascript)中)。 。Galvin,Greg Gagne在我的操作系统课程(第二年)中。这是一本非常出色的书,它全面介绍了操作系统的每个组件-有点昂贵,但很值得,而且旧的/二手的副本应该随处可见。

评论


操作系统概念?构建编译器只需要很少的钱。需要的是对软件体系结构的理解:寻址空间,堆栈,线程(如果他想学习编译器,他会更好地了解并行性及其未来)。

–伊拉克·巴克斯特
2011年6月15日20:30

在说他想学习语言设计和编译器后,他立即说他想学习OS。

– David Thornley
2011年6月15日在20:47

@伊拉克-同意。我从来没有说过要理解OS是构建编译器/语言所必需的,只是简单地解释说这可能是一个更简单的起点。每个人都在关注问题的“编译器”方面,但他也提到他希望更好地了解OS和库。对于15岁仍在学习架构的人,了解内存管理,线程,锁定,I / O等将比学习如何使用yacc(IMHO)定义语法有用得多。

–不满意
2011年6月15日20:48



抱歉...错过了要学习(构建?)操作系统的要点。我的观点是:他不需要很多OS知识的编译器。实际上,这几乎是一个完全不同的主题,除了在其中编译器和OS交互以实现某些共同目的的地方。 (例如,Multics要求其PL / 1编译器以某些方式构建函数调用以启用全局VM。)

–伊拉克·巴克斯特
2011年6月15日在21:36

#25 楼

这是一个很大的话题,但是与其花大笔的“读一本书,孩子”来代替您,不如说,我会很高兴地为您提供指示,以帮助您绕开它。

大多数编译器和/或解释器的工作方式如下:

令牌化:扫描代码文本并将其分为令牌列表。

这一步可能很棘手,因为您不能仅将字符串拆分成空格,还必须认识到if (bar) foo += "a string";是8个令牌的列表:WORD,OPEN_PAREN,WORD,CLOSE_PAREN,WORD,ASIGNMENT_ADD, STRING_LITERAL,TERMINATOR。如您所见,简单地将源代码分割成空格是行不通的,您必须按顺序读取每个字符,因此,如果遇到字母数字字符,您将继续读取字符,直到遇到非字母数字字符为止刚刚读到的是一个WORD,稍后再进行分类。您可以自己决定令牌生成器的粒度:它是否将"a string"吞噬为一个称为STRING_LITERAL的令牌以供以后进一步解析,或者是否将"a string"视为OPEN_QUOTE,UNPARSED_TEXT,CLOSE_QUOTE,等等,这只是您众多选择之一必须在编码时自行决定。

Lex:现在您有了令牌列表。您可能用诸如WORD之类的歧义标记了一些标记,因为在第一遍过程中,您无需花费太多精力来尝试找出每个字符串的上下文。因此,现在再次阅读源标记列表,并根据您的语言中的关键字,使用更具体的标记类型对每个歧义标记进行重新分类。因此,您有一个诸如“ if”之类的单词,并且“ if”在特殊符号列表中称为“符号IF”,因此您将该令牌的符号类型从WORD更改为IF,而不在特殊关键字列表中的任何WORD (例如WORD foo)是IDENTIFIER。

解析:因此,现在您在if (bar) foo += "a string";上列出了以下词汇化标记的列表:如果OPEN_PAREN IDENTIFER CLOSE_PAREN IDENTIFIER ASIGN_ADD STRING_LITERAL TERMINATOR。该步骤是将标记序列识别为语句。这是解析。您可以使用以下语法来做到这一点:

STATEMENT:= ASIGN_EXPRESSION | IF_STATEMENT

IF_STATEMENT:= IF,PAREN_EXPRESSION,STATEMENT

ASIGN_EXPRESSION:= IDENTIFIER,ASIGN_OP,VALUE

PAREN_EXPRESSSION:= OPEN_PAREN,VALUE,CLOSE_PAREN

VALUE:=标识符| STRING_LITERAL | PAREN_EXPRESSION

ASIGN_OP:=等于| ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT

使用“ |”的作品术语之间的意思是“匹配任何这些术语”,如果术语之间有逗号,则表示“匹配此术语序列”

你如何使用这个?从第一个令牌开始,尝试将令牌序列与这些产品匹配。因此,首先您尝试将令牌列表与STATEMENT匹配,因此您阅读了STATEMENT的规则,并说“ STATEMENT是ASIGN_EXPRESSION或IF_STATEMENT”,因此您首先尝试匹配ASIGN_EXPRESSION,因此您查找了ASIGN_EXPRESSION的语法规则并显示“ ASIGN_EXPRESSION是IDENTIFIER,后跟一个ASIGN_OP,后跟一个VALUE,因此您查找IDENTIFIER的语法规则,就会发现IDENTIFIER没有语法恶作剧,这意味着IDENTIFIER是一个“终端”,这意味着不需要进行解析以匹配它,以便您可以尝试直接将其与您的令牌匹配。但是您的第一个源令牌是IF,并且IF与IDENTIFIER不同,因此匹配失败。匹配下一项:IF_STATEMENT。您查找IF_STATEMENT,它以IF开始,查找IF,如果IF是一个终端,将终端与您的第一个令牌进行比较,如果令牌匹配,那么继续,下一项是PAREN_EXPRESSION,查找PAREN_EXPRESSION,不是终端,这是第一个术语,PAREN_EXPRESSION以OPEN_PAREN开始,查找OPEN_PAREN,它是终端,将OPEN_PAREN与您的下一个令牌匹配,它匹配....依此类推。

实现此步骤的最简单方法是,您拥有一个名为parse()的函数,该函数将要与之匹配的源代码令牌以及与之匹配的语法术语传递给该函数。 。如果语法术语不是终结符,则可以递归:再次调用parse(),将相同的源标记和该语法规则的第一项传递给parse()。这就是为什么它被称为“递归下降解析器”的原因。parse()函数返回(或修改)您在读取源令牌中的当前位置,它实际上将返回匹配序列中的最后一个令牌,然后您继续对从那里解析()。

每当parse()与ASIGN_EXPRESSION之类的生产匹配时,您都会创建一个代表该代码段的结构。此结构包含对原始源令牌的引用。您开始构建这些结构的列表。我们将整个结构称为抽象语法树(AST)

编译和/或执行:对于语法中的某些生成,您已经创建了处理函数,如果给出AST结构,该处理函数将编译或执行AST的那一部分。

所以,让我们看一下您的AST中具有ASIGN_ADD类型的代码。因此,作为解释器,您具有ASIGN_ADD_execute()函数。该函数作为与foo += "a string"的解析树相对应的AST的一部分传递,因此此函数查看该结构,并且知道该结构中的第一项必须为IDENTIFIER,第二项为VALUE,因此ASIGN_ADD_execute( )将VALUE术语传递给VALUE_eval()函数,该函数返回一个表示内存中已评估值的对象,然后ASIGN_ADD_execute()在变量表中查找“ foo”,并将对eval_value( )函数。

这是一个解释器。相反,编译器将使处理程序函数将AST转换为字节代码或机器代码,而不是执行它。

使用Flex和Bison之类的工具可以使步骤1至3和某些步骤4更容易。 (又名Lex和Yacc),但从头开始编写解释器可能是任何程序员都可以完成的最有力量的工作。在提出这一挑战之后,所有其他编程挑战似乎都是微不足道的。

我的建议是从小开始:一门小语言,一门小语法,然后尝试解析并执行一些简单的语句,然后从那里开始。

阅读这些内容,祝你好运!

http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c

http://en.wikipedia .org / wiki / Recursive_descent_parser

评论


当人们考虑进行编译时,您犯了一个我认为是经典的错误:即认为问题在于解析。从技术上讲,解析很容易;有很多很棒的技术可以做到这一点。编译的难点是语义分析,在程序表示的高低级进行优化以及代码的生成,这些天来越来越强调PARALLEL代码。您可以在回答中完全忽略这一点:“编译器具有处理程序函数,可以将AST转换为字节码”。隐藏了50多年的编译器理论和工程知识。

–伊拉克·巴克斯特
2011年6月17日15:31

#26 楼

计算机领域之所以复杂,是因为它有时间在许多方面发展。 。
它使人们了解了计算机在基本级别上的工作方式。 ,如果不了解需要什么就很难理解。
祝你好运,不要只看东西。
做东西。

#27 楼

请访问http://mikeos.berlios.de/

x86汇编中有一个非常简单的操作系统。

他有一个很好的教程,介绍如何编写从头开始创建简单的操作系统。

#28 楼

另一本很好的入门书是N. Wirth于1986年撰写的“ Compilerbau”(编译器结构),该书长约100页,并解释了玩具语言PL / 0的简洁,精心设计的代码,包括解析器,代码生成器和虚拟机。它还显示了如何编写一个语法分析器,该语法分析器以EBNF表示法读取语法。这本书是德语的,但我写了一个摘要,并作为练习将代码翻译成Python,请参阅http://www.d12k.org/cmplr/w86/intro.html。

#29 楼

如果您有兴趣了解编程语言的本质,建议您阅读PLAI(http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/)一书,以了解概念和他们的实施。它还将帮助您设计自己的语言。

#30 楼

如果您真的对编译器感兴趣,并且以前从未有过兴趣,则可以从设计用于计算算术公式(如Eric提到的DSL的一种)的计算器开始。对于这种编译器,您需要考虑很多方面:


允许的数字
允许的运算符
运算符优先级
语法验证
/>可变的查找机制
循环检测
优化

例如,您具有以下公式,您的计算器应该能够计算x的值:

a = 1
b = 2
c = a + b
d = (3 + b) * c
x = a - d / b


编译器一开始并不困难,但可以使您更多地了解编译器的基本概念,并帮助您提高编程技巧和控制代码的质量(这实际上是测试驱动开发TDD可以用来改善软件质量的完美问题)。