You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

14 KiB

课前热身|开始学习之前我们要准备什么?

你好!我是宫文学,欢迎来到《手把手带你写一门编程语言》的课程。

其实,你从课程题目就可以看出,我们这个课强调动手实践。所以在这一节课,我要给你介绍一下我们这个课程示例代码所采用的计算机语言,以及相关编程环境的搭建。这样,会方便你阅读、运行和修改课程的示例代码。

对于课程里用到的汇编语言、编译原理知识,如果你之前没有相关的经验,也不要担心。我会介绍一下我们这方面的设计思路,保证你通过这个课程会更快、更扎实地掌握它们。

通过这篇导读,你会对课程里用到的语言、工具、技术心里有数,以便更好地开启你的学习之旅。

好,我们先从使用的计算机语言和环境说起。

怎么快速上手TypeScript语言

我们这个课程的目标呢是要实现TypeScript的编译器和各种运行时。既然如此那么我就尽可能地用TypeScript来实现这个目标。

虽然我们这个课程主体的代码都是用TypeScript写的但我正式使用TypeScript其实是从2021年5月份开始也就是我开始准备这个课的时间。

我知道你肯定会问用几个月的时间既要了解TypeScript又要用TypeScript写自己的编译器是不是太不靠谱了当然你可能也是因为要学习这门课程第一次使用TypeScript所以我就分享一下自己的一些经验。

第一,使用它!

我一直觉得真正的语言学习开始于你使用它的那一刻。否则你就是一直看这门语言的资料也只能留下个大概印象而且很快就会忘掉只有动手使用才会形成肌肉记忆。比如现在我一写for循环手指不自觉地打出“for (let i = …)”或"for (let x of …)"开头,这就是形成肌肉记忆了。

第二,看资料!

说实在的,现在学习计算机语言实在是太方便了,各种资料应有尽有,又有很多热心同学在网上的分享,遇到什么需求一查就有,用过才会记住。

如果你让我推荐一本学习资料,我比较推荐流浪小猫写的开源电子书《TypeScript入门教程》。这个作者可能比较懂我想看什么,提供的内容会到点上。比如,我们做面向对象编程的时候,都关心该语言是否具备运行时的类型判断能力,因为这个功能几乎百分之百会被用到,而这个教程里就专门有类型断言的章节。

第三,靠经验直觉!

其实,很多同学都学过多门语言,那么学一门新语言的速度就会很快。有经验的程序员会建立一些直觉,能够猜到一门语言可能具备什么特性。

在准备课程代码的时候我有一次要编写一个类代表汇编指令中的寄存器操作数。而在x86的汇编指令中不同位数的寄存器的名称是不一样的所以我需要用一个成员变量bits来表示这个寄存器的位数。这个时候我想当然地写出了下面的代码

class Register extends Oprand{
    bits:32|64 = 32;  //寄存器的位数
    ...
}

也就是位数只能取两个值要么是32要么是64赋其他值给它都是错误的。

在使用这个写法的时候,我其实不是很确定是否可以用两个值的集合来描述变量的类型,因为在教程里提到联合类型Union Types的时候只是说了可以用两个类型的联合。我直觉上觉得也应该支持两个值的联合因为如果我是TypeScript的作者我可能不会忽视这种使用场景。我根据自己的猜测试了一下然后就成功了。

如果上升到类型理论的高度,那么我们可以说,类型本来就是可以取的值的集合。但如果我们不上升到理论高度,仅凭直觉,其实也能去正确地使用类型。

说到老程序员的直觉,其实这门课程的一个重要目标,就是想帮你建立起更多的直觉,建立仅仅通过高级语言的语法表象就能看透其内部实现机制的能力,让计算机语言在你面前成为一个白盒子,从而让你能够更加自如地去支配不同的语言来为自己服务。

好了了解了怎么上手以后我们再来看看在这门课程中的TypeScript的环境配置问题。

TypeScript的环境配置

我们这个课程关于TypeScript的环境配置主要包括这些

1.编译和运行环境Node.js。

首先我使用Node.js来编译和运行TypeScript所以你要先在自己的电脑上安装Node.js配置好相应的环境。这方面的资料很多我就不提供链接了。

2.安装和配置TypeScript。

使用下面的命令可以安装TypeScript

npm install typescript -g

之后你可以用git命令下载示例代码

git clone https://gitee.com/richard-gong/craft-a-language.git

在用git下载了示例代码以后需要你在示例代码的目录中运行下面这个命令安装示例程序依赖的node.js中的一些包。安装完毕以后会在craft-a-language目录中建立一个node_modules子目录

npm i --save-dev @types/node

3.IDEVisual Studio Code简称VS Code

我在课程里使用了VS Code作为IDEVS Code缺省就支持TypeScript语言毕竟这个IDE本身就是用TypeScript编写的。

而且,我们每一节课的代码,都会被放在我们代码库里的一个单独的目录下比如01、02……在每个目录下都会有几个json文件是TypeScript的工程配置文件。你只要在目录下输入tsc就会编译该工程的所有.ts文件。

打开tscongfig.json你会看到我提供的一些配置项

  • target项是编译目标这里我用的是es6因为es6具备了很多高级特性。
  • module项是模块管理工具我们选用的是CommonJS。
  • 另外有“exclude”选项是排除了一些.ts文件不纳入编译范围。这些.ts文件都是以example开头是我们自己课程的一些例子给我们自己的编译器使用的就不用tsc编译了。

4.运行我们自己的语言:

我们自己的语言的入口是play.ts你可以通过下面这个命令来运行example.ts程序

node play example.ts

C语言相关的功能和配置

我们这个课程的部分内容还使用了C语言主要用来实现一些运行时的功能包括

  • 实现了一个运行字节码的虚拟机:

我们首先用TypeScript实现了一个虚拟机然后用C语言又实现了一版。因为现实世界的很多虚拟机都是用C/C++实现的,所以我们要体会一下系统级的语言来实现虚拟机有什么不同,特别是性能上会有什么提升。同时,也能体会到字节码带来的跨平台特性:针对相同的字节码,不同的虚拟机的运行结果是完全一样的。

  • 实现了一些内置函数:

每门语言都有一些内置的函数由于其功能比较底层所以要用系统级的语言来实现。比如我们在语言里提供了一个println()的内置函数用来打印信息方便程序调试。这个函数我是用C语言的标准库实现的然后在生成的汇编代码中调用这个函数就行。

还有一些内置函数叫做Intrinsics它们不是给开发者使用的而是被编译器所调用。比如为了实现TypeScript对字符串数据类型的支持我用C语言写了几个函数分别用于创建字符串对象、返回字符串长度还有进行字符串连接等功能。很多时候语言的使用者意识不到这些内核函数的存在但他们使用TypeScript本身的语法来处理字符串的时候在运行时里就会调用这些内置函数。

  • 实现了内存管理功能:

TypeScript是面向对象的语言在使用对象的过程中需要内存管理功能来生成内存中的对象。在必要的时候又会运行垃圾收集功能回收不再被使用的对象这些功能也是用C语言实现的。

我想大部分同学对C语言应该还是比较熟悉的。虽然有很多同学在工作以后可能不太使用C语言但很多在大学的时候都学习过做过课程练习所以阅读和编写课程中的示例代码应该难度不大。C语言的特点就是很简洁所以也受到很多极客的欢迎。如果我用C++来写这些代码,可能就会给你带来一些阅读障碍了。

我们这门课C语言的配置环境是这样的

1.编译器等工具链。

我平常工作采用的是MacOS系统所以系统有自带的clang工具链。同时你也可以使用gcc我在课程里使用的clang命令其实换成gcc都能运行。所以如果你用Linux/Windows系统做课程练习就可以用gcc开发环境Windows系统可以参考这篇文档

2.IDEVisual Studio Code。

如果你要编写和调试这些C语言的代码仍然用VS Code就可以了。而且我用起来已经比较习惯了觉得没必要采用一个商用的IDE。

这门课的C语言的代码都被我放在了代码库里的vm和rt两个子目录下一个是虚拟机的代码一个是运行时的代码。在这两个目录下都有一个.vscode目录里面有一些配置文件告诉VS Code如何做编译和代码调试。

最后我想说明一下我在课程里使用C语言其实还有另一个重要的用途就是观察同样的功能用C语言生成的汇编代码是什么样子的从而加以学习和模仿。根据我的经验这是最快熟悉器汇编语言的途径。

那我们接下来就说说汇编语言。

你是否需要懂汇编语言?

在这个课程里我们会实现编译器的后端也就是把TypeScript编译成汇编代码从而生成二进制的可执行文件。所以从这个意义上说我们需要了解汇编语言

但从现实角度,除了一些嵌入式开发、芯片开发、驱动开发、操作系统,还有编译器等领域的工程师,很少有人日常也使用汇编语言。就算使用,也很少人能够熟悉各种不同芯片的指令集。

所以,这个课程关于汇编语言的部分,不需要你提前学过相关课程。我会用一节课给你介绍一下物理计算机的运行时原理这是使用汇编语言的背景知识然后再用一节课介绍汇编语言的一些共性的知识包括不同芯片的指令集都会有的一些共性的指令、指令的构成以及我们课程所针对的x86架构的CPU的一些信息比如寄存器的名称和用途等。

有这些知识,基本就能够你开始使用汇编语言了。而要真正掌握汇编语言,把它用出感觉来,只有一条途径:在实践中使用它们

在这个课里你可以用我们自己实现的编译器生成汇编代码或者把C/C++程序生成汇编代码,这样反复比较对比,你就会去除对汇编语言的陌生感。到最后,你会发现汇编语言其实真的挺简单的。

除了汇编语言这个问题外,还有一点我想跟你说一下。其实,我们要实现一门计算机语言,是绕不开编译技术的,你可能也会担心这一点,会不会让你学了编译原理再来,我现在跟你解释一下。

你是否需要懂编译原理?

我们这门课程主要是一门实践课,所以我尽量不对一些理论性的知识做过多的要求,不需要你懂编译原理。

反之,其实很多编译方面的知识点,仅仅从理论方面学习,很难学得会,反倒是动手实现,并没有想象的那么难。比如语法分析的算法,是编译原理课的难点,但在这个课程中,我会介绍当前主流计算机语言实现的方法。你自己实现的时候,那些抽象的知识点一下子变得很具体,会产生“原来就是这么回事呀”的感觉,学习门槛一下子就降低了。

另有一些知识内容,属于工程性的知识,本来在教科书上就不多见,比如如何实现一个虚拟机等,就更需要通过实践来学习了。还有很多内容超出了编译原理的课程范围,有些国外的学校,会提供一些关于运行时的课程、计算机语言设计的课程,但都比较小众。

所以说我们这门课程围绕实现一门语言用到什么技术就介绍什么技术横跨的知识面很大很综合。比如在运行时方面涉及了AST解释器、运行字节码的虚拟机和编译成本地代码等多个机制、多个版本的实现我很少能见到能把这些所有内容都贯穿起来的课程或书籍。

总结起来,我们这门课,更多的是“行万里路”,而不是“读万卷书”,强调在动手实践的过程中学习。而在这个过程中,一旦获得了各个知识点的直觉理解,再反过去学那些纯理论性的内容,会更加容易。

利用好课程中的示例代码

在这个课程中我提供了用TypeScript和C语言的示例代码。在课程的文稿中我主要写的是设计思路但要真正理解这些设计思路和相关知识点不是仅仅看看文稿就够的还是要看代码、运行代码、修改代码加上这些实践环节后你的学习效果会更加理想。

并且我也鼓励你用你顺手的语言重新实现。在我的其他课程里有很多同学就用自己习惯的语言来重写课程的示例代码用什么语言的都有比如Go语言、Swift、C++、TypeScript等。这个优秀的传统我希望可以在这门课里继续发扬

当然我也会尽量把示例代码的结构理得清晰一些多加一些注释让你更容易看懂。如果对代码有什么疑问或建议我自己就经常发现旧代码里的bug可以在评论区给我留言或者在码云上提issue我会想办法去解决。

好了,上课前的准备工作就是这些。你赶紧把环境配置好,我们就开始上手学习和实践啦!相信在这门课里,你会在很多地方产生“噢,原来是这样!”的感受,帮助你打通计算机技术的奇经八脉,真的很爽!

来吧,我在课程里等你!

欢迎点击链接加入交流群