原文地址:Let’s Build A Simple Interpreter. Part 2.
在《The 5 Elements of Effective Thinking》1这本令人惊叹的书中,作者 Burger 和 Starbird 分享了一个故事,讲述了他们如何观察国际知名小号演奏家 Tony Plog 为有 造诣的小号演奏家举办大师班。学生们首先演奏了复杂的音乐片段,他们演奏的很好。但随 后他们被要求演奏非常基础、简单的音符。当他们演奏这些音符时,和前面演奏的复杂片段 相比这些音符显得非常幼稚。当他们演奏完,大师也演奏了相同的音符,但他演奏时,这些 音符听上去并不幼稚。差别很惊人。Tony 解释说,掌握简单音符的演奏会使得对复杂作品 有更好的控制。这一课要传递的想法很清楚──要掌握真正的高超技艺,就必须专注于掌握简 单基础的概念。
故事中的教训显然不仅适用于音乐,也适用于软件开发。这个故事很好地提醒我们所有人, 不要忽视深入研究简单、基础想法的重要性,即使这有时感觉像是退步。不仅精通所使用的 工具或框架很重要,知道它们背后的原理同样非常重要。正如 Ralph Waldo Emerson 所说:
If you learn only methods, you’ll be tied to your methods. But if you learn principles, you can devise your own methods.
让我们以此为指导,再次深入到解释器和编译器当中。
今天我会向你展示第一部分中计算器的一个新版本,它可以做到:
- 处理输入字符串中任何位置的空白符
- 处理输入中的多位数
- 两个整数相减(当前只有加法)
下面是新版计算器的源代码,它可以做到上述几点:
1 | # Token types |
把以上代码保存到名为 calc2.py
中,或者直接从 GitHub 上下载。试一试。亲眼看一下
它可以按预期运行:它可以处理输入中的任何位置的空白符;它接受多位整数,除了整数相
加还可以处理整数相减。
下面是在我笔记本上的一次尝试:
$ python calc2.py calc> 27 + 3 30 calc> 27 - 7 20 calc>
与第一部分相比代码的主要变化有:
get_next_token
方法做了一点重构。增加指针pos
的逻辑被重构到了方法advance
中。- 增加了两个方法:
skip_whitespace
用来忽略空白符,integer
用来处理输入中 的多位整数。 expr
方法在修改后,除了可以识别 INTEGER -> PLUS -> INTEGER 这个组合(phrase) 之外,还可以识别INTEGER -> MINUS -> INTEGER。而且在成功识别相应的组合后,也可 以进行相应的加减操作。
在第一部分你尝到了两个重要的概念,即 token 和 词法分析器 。今天我想聊一聊 lexeme 、 parsing 和 parser 。
你已经知道 token 了。但为了叙述方便,我需要介绍一下 lexeme。什么是 lexeme? lexeme 是组成 token 的一个字符序列。在下面的图片中是一些 token 和 lexeme 的例子, 希望它能把两者之间的关系表达清楚:
Figure 1: Lexemes
现在还记得 expr
方法吗?我以前说过这是真正解释算术表达式的地方。但在解释一个表
达式之前,你需要知道它是哪种组合,比如相加或相减。这是 expr
方法本质上做的事:
它从 get_next_token
方法得到的 token 流中找到结构,然后解释它识别出的组合,产
生算术表达式的结果。
从 token 流中查找结构,或者说从 token 流中识别组合,的过程叫做 parsing. 解释器 或编译器中执行这部分任务的叫 parser.
现在你知道解释器的 parsing 和 解释 都在 expr
方法中了── expr
方法首先尝
试从 token 流中识别(即parse) INTEGER -> PLUS -> INTEGER 或 the INTEGER ->
MINUS -> INTEGER 组合,在成功识别到(即parsed)其中一个组合时,该方法就解释执行
它并返回给调用者两个整数相加或相减的结果。
又到了做练习的时间了。
Figure 2: exercises
- 扩展计算器以处理两个整数相乘
- 扩展计算器以处理两个整数相除
- 修改代码以使它可以解释包含任意个数字的加减操作,如“9 - 5 + 3 + 11”
检查你的理解。
- 什么是 lexeme?
- 在 token 流中找到结构的过程叫什么?或者这么问,在 token 流中识别出特定组合的 过程叫什么?
- 解释器(编译器)做 parsing 工作的部分叫什么?
我希望你能喜欢今天讲的内容。在本系列的下一篇文章中你会扩展你的计算器来处理更复杂 的算术表达式。敬请关注。
下面是我推荐的帮助你学习解释器和编译器的一些书:
- Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages (Pragmatic Programmers)
- Writing Compilers and Interpreters: A Software Engineering Approach
- Modern Compiler Implementation in Java
- Modern Compiler Design
- Compilers: Principles, Techniques, and Tools (2nd Edition)
顺便说一下,我还在写一本书“et’s Build A Web Server: First Steps”,解释了怎么从头 写一个基本的 Web 服务器。你可以从这里, 这里 和 这里 来感受一下这本书。
本系列的所有文章:
- Let’s Build A Simple Interpreter. Part 1.
- Let’s Build A Simple Interpreter. Part 2.
- Let’s Build A Simple Interpreter. Part 3.
- Let’s Build A Simple Interpreter. Part 4.
- Let’s Build A Simple Interpreter. Part 5.
- Let’s Build A Simple Interpreter. Part 6.
- Let’s Build A Simple Interpreter. Part 7.
- Let’s Build A Simple Interpreter. Part 8.
- Let’s Build A Simple Interpreter. Part 9.
- Let’s Build A Simple Interpreter. Part 10.
- Let’s Build A Simple Interpreter. Part 11.
- Let’s Build A Simple Interpreter. Part 12.
- Let’s Build A Simple Interpreter. Part 13.
- Let’s Build A Simple Interpreter. Part 14.