Python写Lisp解释器(二、标准环境)

May 16, 2017


这是一篇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~