这是一篇Lisp解释器的教程。参考国外某大神的Lisp(其实是其中的一种叫做Scheme的方言)的解释器的过程。
正好在学编译原理,准备搞一些事情,这个project纯当一个搞事情的前奏。因为一年多前简略的看过了SICP,所以对LISP语言有一定小小的理解,所以开一个LISP语言的解释器,对编译上的理解可能会非常的有帮助。
另外,国内能够自主写出编译器的人实在是太少,这一行业上可能更多的引用国外大神的博客内容,所以搬运和翻译加上自主原创的这种过程也是极有必要的。
作者:岐山凤鸣,引用请加上本站域名
内容:作者原创
参考:http://norvig.com/lispy.html
参考:http://blog.jobbole.com/47659/
参考:关于符号类型:http://blog.csdn.net/xiao_wanpeng/article/details/8441562
插曲:这么久没有更新,很大一部分原因因为数据丢失,有道云笔记略坑。
概述
整个项目的eval部分是最核心的部分,可以说讲完eval这个项目就结束了。
那么在eval部分之前,我们需要创立好一个解释环境,这个环境将贯穿整个eval部分,我们叫做标准环境(standard env)。
什么是环境呢,简而言之就是,如果你要eval(parse(‘(+ 5 6)’))的话,那么解析后一定是调用add(5, 6), 这个add(5, 6)的形式是解析得到的,而add函数就是环境提供的。
于是环境我们可以用一个字典来代替,键代表我们表面上要进行的操作,值代表我们实际调用的Python函数名:
class Env(dict):
def __init__(self, parms = (), args = (), outer = None):
self.update(zip(parms, args))
self.outer = outer
def find(self, var):
return self if (var in self) else self.outer.find(var)
这个Env环境类继承于字典dict, 传入初始化键值和一个外部字典(用于扩展)。
然后定义了一个查找函数,如果本身查找不到就试试外部字典的查找,返回一个字典,即包含有改进键的字典。
比如假设Env本身是{‘+’:add}的话,那么find(‘+’)则会返回{‘+’:add},所以要找到add就可以这样,find(‘+’)[’+’]
标准环境的建立
看懂了我上面说的之后,然后就是标准环境构建的函数了。
def standard_env():
env = Env()
env.update(vars(math))
env.update(
{
'+': op.add,
'-': op.sub,
'*': op.mul,
'/': op.truediv,
'>': op.gt,
'<': op.lt,
'>=': op.ge,
'<=': op.le,
'=': op.eq,
'abs': abs,
'append': op.add,
'begin': lambda *x: x[-1],
'car': lambda x: x[0],
'cdr': lambda x: x[1:],
'cons': lambda x, y: [x] + y,
'eq?': op.is,
'equal?': op.eq,
'length': len,
'list': lambda *x: list(x),
'list?': lambda x: isinstance(x, list),
'map': map,
'max': max,
'filter': filter,
'min': min,
'not': op.not,
'null?': lambda x: x == [],
'number?': lambda x: isinstance(x, Number),
'procedure?': callable,
'round': round,
'symbol?': lambda x: isinstance(x, Symbol)
})
return env
上面这个构建标准环境,如果Python基础好一些的话,应该可以很容易看懂,其实就是构建了一个Env()类的对象,然后往里面加东西(毕竟Env对象是继承字典嘛)。
那么加了些什么东西呢?首先把math里面的函数名都加进去了,这样就可以解析(sqrt (/ 7 8))这样的表达式,返回0.935…
然后加了各种自定义的函数和操作函数,比如加减乘除什么的,其他的后面再说,总之调用了standard_env()这个函数,就能得到一个字典,这个字典里面的键是我们在使用自己的Scheme语言时的操作类型,值是Python内建的各种函数名字。
有了这个环境,当我们解析(sqrt (/ 7 8))如果顺利的话,解析后的结果应该就是math.sqrt((7/8)), 返回0.935…
要注意,这里的环境还不全,并没有加入尾递归啊,复合类型struct等等,什么什么的功能,所以只是1.0版本,后期还需要改进。
下一节就进入正式的eval环节。
所有代码:
# env
import math
import operator as op
class Env(dict):
def __init__(self, parms = (), args = (), outer = None):
self.update(zip(parms, args))
self.outer = outer
def find(self, var):
return self if (var in self) else self.outer.find(var)
def standard_env():
env = Env()
env.update(vars(math))
env.update({
'+': op.add,
'-': op.sub,
'*': op.mul,
'/': op.truediv,
'>': op.gt,
'<': op.lt,
'>=': op.ge,
'<=': op.le,
'=': op.eq,
'abs': abs,
'append': op.add,
'begin': lambda *x: x[-1],
'car': lambda x: x[0],
'cdr': lambda x: x[1:],
'cons': lambda x, y: [x] + y,
'eq?': op.is_,
'equal?': op.eq,
'length': len,
'list': lambda *x: list(x),
'list?': lambda x: isinstance(x, list),
'map': map,
'max': max,
'filter': filter,
'min': min,
'not': op.not_,
'null?': lambda x: x == [],
'number?': lambda x: isinstance(x, Number),
'procedure?': callable,
'round': round,
'symbol?': lambda x: isinstance(x, Symbol)
})
return env
global_env = standard_env()
def eval(x, env = global_env):
pass
# wait for our next page~