用 Lisp 解释 Lisp (2010-12-09)
最近在读《The Little Schemer》,一到七章都还好,到第八章就开始云里雾里了。书读不透,那么看代码吧。好在已经有了把示例都整理好了。那么开始吧。
我们期望能写出一个能对任意表达式求值的函数,一般叫它eval
。不过,这里为了强调是我们自己的求值函数,叫它 value 。
我们先来看两个求值过程:
(value 6)
(value '(add1 6)) ; 7
我们不禁要问,这么多函数是必要的吗?它们起什么作用?
显然,value
是个入口函数,因为它只用到了一次。
meaning
需要一个表达式和一张记录变量名和其值的对照表(上下文环境)。这就引出一个问题,什么时候改写对照表?(*application)
接着,由expression-to-action
判断表达式是否为原子( atom ),是的话交给atom-to-action
(如果是数字、真假、cons
, car
, cdr
, null?
, eq?
, atom?
, zero?
, add1
, sub1
, number?
,就交给*const
;其他交给*identifier
);否则交给list-to-action
。
函数分为内置函数( primitive )和自定义函数( non-primitive )两类。语言规定了内置函数的行为,但对于自定义函数,我们只能在运行时确定,所以需要在定义时把它们的相关信息(如:参数、函数体)写进对照表。
它们分别是这么存储的:
(primitive primitive-name)
(non-primitive (table formals body)) (上下文 形参 函数体)
如,
(lambda (x) (cons x y))
对照表的内容是
((( y z)
((8) 9)))
那么,解释器内部是这么存储的:
其中,list (table formals body)
叫做 closure record .
list-to-action
将quote
, lambda
, cond
之外函数交由*application
处理。*application
对函数和参数列表分别进行处理,按函数类型将primitive
交给apply-primitive
,而将non-primitive
交给apply-closure
处理。
evlis
对 list 中的各个元素依次求值,把结果合成一个 list 返回。
evcon
用于对cond
求值。
剩下的各位还是看书吧😊