JavaScript 编写的迷你 Lisp 解释器

388 查看

【感谢@李欲纯 的热心翻译。如果其他朋友也有不错的原创或译文,可以尝试推荐给伯乐在线。】

Little Lisp是一个解释器,支持函数调用、lambda表达式、 变量绑定(let)、数字、字符串、几个库函数和列表(list)。我写这个是为了在Hacker School(一所位于纽约的程序员培训学校)的一个闪电秀中展示写一个解释器不是很难。一共只有116行的JavaScript代码,下文我会解释它是如何运行的。

 

首先,让我们学习一些Lisp。

Lisp基础

这是一个原子,最简单的Lisp形式:

这是另一个原子,一个字符串:

这是一个空列表:

()

这是一个包含了一个原子的列表:

这是一个包含了两个原子的列表:

这是一个包含了一个原子和另一个列表的列表:

这是一个函数调用。函数调用由一个列表组成,列表的第一个元素是要调用的函数,其余的元素是函数的参数。函数first接受一个参数(1 2),返回1

这是一个lambda表达式,即一个函数定义。这个函数接受一个参数x,然后原样返回它。

这是一个lambda调用。lambda调用由一个列表组成,列表的第一个元素是一个lambda表达式,其余的元素是由lambda表达式所定义的函数的参数。这个lambda表达式接受一个参数"lisp"并返回它。

 

Little Lisp是如何运行的

写一个Lisp解释器真的很容易。

Little Lisp的代码包括两部分:分析器和解释器

分析器

分析分两个阶段:分词(tokenizing)和加括号(parenthesizing)。

tokenize()接受一个Lisp字符串,在每个括号周围加上空格,然后用空格作为分隔符拆分整个字符串。举个例子,它接受((lambda (x) x) "Lisp"),将它变换为( ( lambda ( x ) x ) "Lisp" ),然后进一步变换为['(', '(', 'lambda', '(', 'x', ')', 'x', ')', '"Lisp"', ')']

parenthesize()接受由tokenize()产生的词元列表,生成一个嵌套的数组来模拟出Lisp代码的结构。在这个嵌套的数组中的每个原子会被标记为标识符或文字表达式。例如,['(', '(', 'lambda', '(', 'x', ')', 'x', ')', '"Lisp"', ')']被变换为: