1.1 背景
说明:文中很多地方夹杂着英文(包括标题),绝对不是作者故弄玄虚,而是始终觉得有时候中文不能真正表达原意,仅此说明,望读者谅解。
背景其实很简单,自己对编译器和操作系统感兴趣,但想实践却发现是一个难题。编译器和操作系统都是 giants,自己在搜寻相关的资料时,发现很少有为入门读者准备的资料,即完整的带领读者设计实现一门编程语言。闲话无须多说,总之,本系列文章是我怀着对解释器的好奇写下的,我相信还有很多人对这方面也感兴趣,想亲自实践编程却没有相应的参考,若你觉得本系列文章对你有用,我就很高兴了。最后实现的语言暂取名为 Fish,以便在文中引用。我也是学习,所以错误之处,请大家尽管指出,非常欢迎。
1.2 基础
其实说是基础,我觉得即使不懂也无所谓,正因为不懂你才能带着疑问学到更多东西,尤其是在用中学。凡是学过编译原理都会知道下面这几个概念(不知道也没关系,学习总会遇到柳暗花明的那一刻):
- BNF
- 词法分析
- 语法分析
- 语义分析
- 虚拟机
- literate programming
整个系列文章背后都使用noweb排版,放到网页上后显示可能有些差别,但不会有本质影响。由于使用noweb,故代码都是嵌入在文档中的,注释就不太需要了。最后的可编译代码使用notangle从文档中抽取出来。进入正题之前,说下我们的代码目录,假设当前目录为 fish,子目录 src 存放我们的源代码.c 和.h文件,test 目录存放我们的测试文件。
1.3 Where to start?
目前据作者所知道的,可以用如下方式实现解释器:- 将源程序翻译为一台抽象计算机的指令,然后执行这些翻译后的指令,这需要定义一套指令集,如《On Pascal Compilers》,hoc计算器;
- 采用即时编译执行的方式 (JIT),比如 Java 或 C#,实际上现在它们多是编译执行的,以提高效率,解释执行只是可选的,只不过这些对程序员隐藏;
- 直接读取源程序,边读边执行,如 Fish。
正如上面所说,我们采用第三种方式实现 Fish,因为它比较简单,不过可扩展性不是很好,但足以说明解释器原理了,我也不想弄的太复杂,给自己压力 :)。
根据典型的编译过程,源程序首先经过词法分析被转换为 token 流,再经过语法分析器 (parser) 进行类型检查、语义检查等,然后再由代码生成器生成目标代码。当然,中间可能有若干优化过程,这正是现代编译器的复杂之处。我们的Fish 没这么复杂,但也需要先进行词法分析,将输入流符号化 (tokenize),再由语法分析器进行处理,如图1-1所示。