Python写Lisp解释器(四、解释器1.0版本)

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

插曲:这么久没有更新,很大一部分原因因为数据丢失,有道云笔记略坑。

概述

我们之前做好了parse, eval,现在是时候将它们整合到一起,做一个交互式的最初版的解释器(实际上是个计算器233)

虽说是个计算器,但它的这种思维模式什么的都是非常有用的,记起我在做叙事曲项目的时候,写的游戏引擎脚本语言的解析器,就是和这个模式极其的类似

毕竟怎么说还是初步创造了一门语言嘛233、

repl的意思是,交互式解释环境

提供交互式环境

def repl(prompt = '>>>>' ):
	print("laji lisp V1.0. Go go go\n")
	while True:
		i = input(prompt)
		try:
			if i == "quit": break
			val = eval(parse(i))
			print(val)
		except Exception as e:
			print('%s: %s' % (type(e).__name__, e))
repl()

综合之前的代码

项目1.0版本的代码为:

import math
import operator as op

# parse

Symbol = str
symbolTable = {}
def Sym(s):
	if s not in symbolTable:
		symbolTable[s] = Symbol(s)

_quote = Sym('quote')
_if = Sym('if')
_set = Sym('set!')
_define = Sym('define')
_lambda = Sym('lambda')
_begin = Sym('begin')
_definemacro = Sym('define-marco')
_quasiquote = Sym('quasiquote')
_unquote = Sym('unquote')
_unquotesplicing = Sym('unquote-solicing')
_checkexpect = Sym('check-expect')
_checkwithin = Sym('check-within')
_member = Sym('member?')
_struct = Sym('struct')

def tokenize(code):
	code = code.replace('(', ' ( ').replace(')', ' ) ')
	code = code.replace('\"', ' \" ').replace(';', ' ;').split()
	return code
def read_from_tokens(tokens):
	if len(tokens) == 0:
		raise SyntaxError('*** Unexpected EOF while reading')

	token = tokens.pop(0)
	
	if '(' == token:
		L = []
		while tokens[0] != ')':
			L.append(read_from_tokens(tokens))
		tokens.pop(0)
		return L

	elif '"' == token:
		L = []
		while tokens[0] != '"':
			L.append(read_from_tokens(tokens))
		end_quote = tokens.pop(0)
		string = token
		string += " ".join(L)
		string += end_quote
		return ["quote", string]

	elif ';' == token:
		L = []
		L.append(token)
		while tokens[0] != '\n':
			L.append(read_from_tokens(tokens))
		new_line = tokens.pop(0)
		L.append(new_line)
		string = " ".join(L)
		return ["quote", string]

	elif ')' == token:
		raise SyntaxError('*** Unexpected ) appears')
	else:
		return atom(token)

def atom(token):
	try: return int(token)
	except ValueError:
		try: return float(token)
		except ValueError:
			return Symbol(token)

def parse(code):
	return read_from_tokens(tokenize(code))




# env

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()

# eval

def eval(x, env = global_env):
	if isinstance(x, Symbol):
		return env.find(x)[x]

	elif not isinstance(x, list):
		return x

	elif x[0] == 'quote':
		(_, exp) = x
		return exp
	
	elif x[0] == 'if':
		(_, test, conseq, alt) = x
		exp = (conseq if eval(test ,env) else alt)
		return eval(exp, env)

	elif x[0] == 'define':
		(_, var, exp) = x
		env[var] = eval(exp, env)

	else:
		proc = eval(x[0], env)
		args = [eval(arg, env) for arg in x[1:]]
		return proc(*args)

# repl
def repl(prompt = '>>>>' ):
	print("laji lisp V1.0. Go go go\n")
	while True:
		i = input(prompt)
		try:
			if i == "quit": break
			val = eval(parse(i))
			print(val)
		except Exception as e:
			print('%s: %s' % (type(e).__name__, e))
repl()

然后就可以愉快的享受第一个解释器成功的那一刹那了,不过,可别高兴的太早,要知道,我们这才刚刚走出第一步,相当于刚刚生下来了一只婴儿,会长成什么样子,会不会长残,可都要由我们继续做下去。

好了,下一节我们继续扩展和优化这个解释器。