介绍的的第一句话应该是:解释器很神奇。当然,最早的某位不愿透露姓名的评论者说:看起来很愚蠢。好吧,Christian,我不这样认为。我仍然认为解释器很神奇!让我来告诉你为什么。
从表面上看,它们很简单:输入文本,输出内容。它们是将其他程序作为输入并产生某些东西的程序。很简单吧?但你越想它,它就越迷人。看似随机的字母,数字和特殊字符,被输入解释器就突然变得有意义。解释器赋予了它们意义!看起来胡说八道是有道理的。而计算机是一个基于理解0和1的机器,现在却可以理解并且对我们输入奇奇怪怪的语音进行响应——这要归功于在阅读时翻译这种语言的解释器。
我一直在反问自己:这究竟是怎样运行的?第一次这个问题在脑海中形成时,我就已经知道,只有通过编写自己的解释器才能解决这个问题。所以我就这样去做了。
有许多关于解释器的书,文章,博客文章和教程。不过,在大多数情况下,它们都无外乎这两种情况:它们要么巨长,要么就理论很深,并且是针对于对解释器有所了解的人。要么就很简短,仅仅只提供对解释器简短的介绍,使用外部工具作为黑盒子并且仅仅只关心“玩具解释器”。
后一类资源更是令人难过,因为它们的解释器知识用非常简单的语法解释语言。我不想走捷径,我真的想理解解释器是怎样工作的,其中包括了解语法分析器和解析器的工作原理。尤其是对于类似C语言机器花括号和分号,我都不知道如何开始解析它们。学术教科书当然有我寻找的答案,但在它们冗长的理论解释和数学解释背后,我很难理解。
我想要的东西是900页关于编译器的书和解释如何用50行Ruby代码编写Lisp解释器的博客与文章之间的内容。
所以我写了这本书,给你和我。这是一本我希望有的书。这是一本适合喜欢深入原理的人的书,对于喜欢通过了解事物真正运作方式来学习的人。
在这本书中我们将写下我们自己的解释器对于我们自己的编程语言——从头开始。我们将不会使用第三方工具和库。结果不会是准备生产的,它不会具有成熟解释器的性能,当然,基于语言建立的解释器将会缺少功能,但是我们会学到很多东西。
很难对解释器进行描述,因为它的种类很多而且各不相同。可以说的是它们都有的基本属性是获取源代码进行评估,而不会产生一些可以执行可见的中间结果。这与编译器形成对比,编译器可以获取源代码并以底层系统可以理解的另一种语言进行输出。
一些解释器是真的微小,甚至不需要解析。它们知识立即解释输入,我的意思是看看众多的Brainfun之一。
另一方面是更复杂的解释器类型。其中一些不只是评估它们的输入,而是将其编译成字节码在内部表示,然后对其进行评估。更先进的是JIT解释器,它编译输入的即时语调机器代码然后执行。
但是,在两个类别之间,有一些解释器可以解析源代码,从中构建一个抽象语法树(AST),然后评估语法树。这种类型的解释器有时被称为“行走的树”解释器,因为它运行在AST上并且解释它。
我们将在本书中构建的是这样一个树形解释器。
我们将构建我们自己的词法分析器、我们自己的解析器、我们自己的表达树和我们自己的评估器。我们将看一看什么是“令牌”,什么是抽象语法树,如何构建这样的树,如何评估它以及如何使用新的数据结构和内置函数拓展我们的语言。
每个解释器是构建特定的编程语言而构建的。这就是你“实现”编程语言方式。没有编译器或解释器,编程语言只不过是一种想法或规范。
我们将解析和评估我们的自己的Monkey编程语言。这是一个为本书特别设计的语言。它唯一的实现是我们将在本书中构建的——我们的解释器。
列出一系列功能,Monkey具有以下:
- C-like语法
- 变量绑定
- 整数和布尔值
- 算术表达式
- 内置函数
- 一等和高阶函数
- 闭包
- 字符串数据结构
- 数组数据结构
- 哈希数据结构
我们将在本书的其余部分详细了解并实现这些功能中的每一个。但是现在,让我们看看Monkey是什么样子。
这里是我们如何变量绑定在Monkey中:
let age = 1;
let name = "Monkey";
let resulet = 10 * (20/2);
除了整形,布尔值和字符串,Monkey解释器我们也将绑定支持数组和哈希,这是将整数数组绑定到名称的样子:
let myArray=[1,2,3,4,5];
并且这里是哈希,这儿值和键是联系的:
let thorsten = {"name":"Thorsten","age":20};
访问数组中的元素和哈希完成索引表达式:
myArray[0] // => 1
thorsten["name"] // =>"Toristen"
let
声明也可以用来绑定函数的名称,这是一个添加两个数的小函数:
let add = fn(a,b) { return a + b; };
但是Monkey不仅仅支持return
声明。隐式返回值也是可能的,这意味着我们可以省略返回值,如果我们想要:
let add = fn(a,b) { a + b; };
并且调用函数和你期待的一样简单:
add(1,2)
一个更加复杂的函数,例如一个斐波那契函数返回结果,可能像这样:
let fibonacci = fn(x){
if(x==0) { 0
} else {
if (x==1) {
1
} else
{fibonacci(x-1) + fibonacci(x-2);
}
}
};
注意递归调用fibonacci
它自己!
Monkey也支持特殊的函数类型,称为高阶函数。这些是将其他函数作为参数的函数。 下面是一个例子:
let twice = fn(f,x){
return f(f(x));
};
let addTwo = fn(x){
return x+2;
};
twice(addTwo,2);// >= 6
这里twice
接受两个参数,另一个叫做addTwo
函数和整数2。它使用 第一个2 作为参数调用 addTwo 两次,然后使用第一次调用的返回值调用。 最后一行产生6。
是的,我们能使用函数作为另一个函数调用的参数。在Monkey中函数只是一个值,像整形和字符串。该功能称为“一流功能”。
我们将在本书构建的解释器将实现这些所有的功能。它将在REPL中标记和解析Monkey源码,构建称为抽象语法树的代码的内部表示,然后评估该树。它将有几个主要部分:
- 此法分析器
- 解析器
- 抽象语法树(AST)
- 内部对象系统
- 评估器
我们将完全按照这个顺序从下往上构建这些部分。或者更好地说:从源码开始,以输出结束。这种方法的缺点是它不会在第一章结束后产生一个简单的"Hello Word"。优点是更容易理解所有部分如何组合在一起以及数据如何在程序中流动。
但是为什么这个名字?为什么叫Monkey?好吧,因为monkey是华丽,优雅,迷人和有趣动物。就像我们的解释器。
并且为什么是这个书名呢?
如果你读到这里都没有注意到标题和其中的"in Go"字样,首先:恭喜,这很了不起。第二:我们将用Go实现我们的解释器。为什么用Go呢?
我喜欢用Go写代码,我享受使用Go语言、它的标准库和它提供的工具所带来的乐趣。除此之外,我认为Go语言有一些很适合本书的属性。
Go是真正地易于阅读和快速理解。你将不需要破译我在本书中给你展示的Go代码。即使你不是一个有经验的Go程序员。我敢打赌,即使你从未写过一行Go语言,你也可以跟这本书学习。
另一个原因就是Go语言提供的强大工具。本书集中于我们所写的解释器——它背后的思想和概念及其实现。借助Go的通用格式风格,感谢gofmt
和内置的测试框架,我们可以专注于我们的解释器而不必担心第三方库、工具和依赖性。除了Go语言提供的工具之外,我们不会再本书中使用任何其他工具。
但我认为更重要的是,本书中呈现的 Go 代码与其他可能更底层的语言(如 C、C++ 和 Rust)密切相关。 可能这是 Go 本身的原因,它专注于简单性、精简的魅力以及缺乏其他语言中没有且难以翻译的编程语言结构。或者也许是因为我选择为这本书编写 Go 的方式。无论哪种方式,都不会有任何元编程技巧来学习两周后没人能理解的捷径,也不会出现需要笔、纸和“实际上,这很容易”这句话来解释的宏伟的面向对象设计和模式。
所有这些原因使得在此处实现的代码易于去理解(在概念和技术层面上),并且可重复使用。如果你在阅读本书之后,选择用另一种编程语言编写自己的解释器,本书也会派上用场。我想通过这本书为你理解和构建解释器提供一个起点,我认为代码反映了这一点。
本书既不是一个参考书,也不是表述附录中代码解释器实现概念充满理论的论文。本书旨在从头到尾阅读,我建议您按照阅读、输入和修改呈现代码进行阅读。
每一章都在其之前的代码和文章中构建。在每个章节中,我们融合了我们的解释器的一部分,每个部分都紧密相连。为了使代码更容易衔接,这本书是一个被叫code
的文件夹,它包含,好吧,code。 如果您的书的副本没有文件夹,您可以在此处下载
https://interpreterbook.com/waiig_code_1.3.zip
code
文件夹分为许多子文件夹,每个章节一个,其中包含响应章节的最后结果。
有时我只会暗示在代码中的东西,而不是代码本身(因为它要么像测试文件的一样占用太多的空间,要么就是有很多细节)-你可以在对应的章节中找到这些代码。
你需要哪些准备哪些工具?不多:一个文本剪辑器和Go语言。任何Go1.0以上的版本都可以工作,但作为一个免责声明和以防未来的变化:在撰写本文时,我正在使用Go 1.7。
我还建议使用direnv
,那可以改变你终端的环境根据.envrc
文件。本书附带的code
文件夹中的每个子文件夹都包含这样一个.envrc
文件,该文件为该子文件夹正确设置了GOPATH。这让我们可以轻松处理不同章节的代码。
让我们开始吧!
随着那个方式,让我们开始!
< Acknowledgments | > 1.1词法分析 |
---|