这是一篇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()
然后就可以愉快的享受第一个解释器成功的那一刹那了,不过,可别高兴的太早,要知道,我们这才刚刚走出第一步,相当于刚刚生下来了一只婴儿,会长成什么样子,会不会长残,可都要由我们继续做下去。
好了,下一节我们继续扩展和优化这个解释器。