This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
### Env クラス | |
class Env(dict): | |
"環境: ペア{'var':val} の dict で、外部環境(outer)を持つ。" | |
def __init__(self, parms = (), args = (), outer = None): | |
self.update(zip(parms, args)) | |
self.outer = outer | |
def find(self, var): | |
"var が現れる一番内側の Env を見つける" | |
return self if var in self else self.outer.find(var) | |
### グローバル環境 | |
def add_globals(env): | |
"環境に Scheme 標準の手続きのいくつか追加する" | |
import math, operator as op | |
env.update(vars(math)) # sin, sqrt, ... | |
env.update( | |
{'+':op.add, '-':op.sub, '*':op.mul, '/':op.div, 'not':op.not_, | |
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, | |
'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':lambda x, y: [x] + y, | |
'car':lambda x: x[0], 'cdr':lambda x:x[1:], 'append':op.add, | |
'list':lambda *x:list(x), 'list?':lambda x:isa(x, list), | |
'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol)}) | |
return env | |
global_env = add_globals(Env()) | |
### 実行: eval | |
def interpret(x, env = global_env): | |
"環境の中で式を評価する。" | |
if isa(x, Symbol): # 変数参照 | |
return env.find(x)[x] | |
elif not isa(x, list): # 定数リテラル | |
return x | |
elif x[0] == 'quote': # (quote exp) | |
(_, exp) = x | |
return exp | |
elif x[0] == 'if': # (if test conseq alt) | |
(_, test, conseq, alt) = x | |
return interpret((conseq if interpret(test, env) else alt), env) | |
elif x[0] == 'define': # (define var exp) | |
(_, var, exp) = x | |
env[var] = interpret(exp, env) | |
elif x[0] == 'lambda': # (lambda (var*) exp) | |
(_, vars, exp) = x | |
return lambda *args: interpret(exp, Env(vars, args, env)) | |
elif x[0] == 'begin': # (begin exp*) | |
for exp in x[1:]: | |
val = interpret(exp, env) | |
return val | |
else: # (proc exp*) | |
exps = [interpret(exp, env) for exp in x] | |
proc = exps.pop(0) | |
return proc(*exps) | |
isa = isinstance | |
Symbol = str | |
### 構文解析: read と parse | |
def read(s): | |
"文字列から Scheme 式を読み込む。" | |
return read_from(tokenize(s)) | |
parse = read | |
def tokenize(s): | |
"文字列をトークンのリストに変換する。" | |
return s.replace('(', ' ( ').replace(')', ' ) ').split() | |
def read_from(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.pop(0) # pop off ')' | |
return L | |
elif ')' == token: | |
raise SyntaxError('unexpected )') | |
else: | |
return atom(token) | |
def atom(token): | |
"数は数にし、それ以外のトークンはシンボルにする。" | |
try: return int (token) | |
except ValueError: | |
try: return float(token) | |
except ValueError: | |
return Symbol(token) | |
def to_string(exp): | |
"Python オブジェクトを Lisp の読める文字列に戻す。" | |
return '('+' '.join(map(to_string, exp))+')' if isa(exp, list) else str(exp) | |
def repl(prompt='lis.py> '): | |
"read-eval-print-loop のプロンプト" | |
while True: | |
val = interpret(parse(raw_input(prompt))) | |
if val is not None: print to_string(val) | |
repl() |
実際は、Schemeとは呼べないんですけどね(笑)。仕様満たしてませんし。かつ単なる写経です(爆)。
作者はPAIP
「Lisp以外の言語で」Lispを実装してみる、ってのは初めての経験で、結構面白いとは思いました。
ただ、Pythonの構文の細かいトコには明るくないんで、「読解」自体は上手く行ってるとは思いませんが(笑)。んで、やっぱOOPは嫌いです(笑)。
ちなみに、Python2.6.xだとだいぶ改良されてはいますが、やっぱエンコーディングはめんどっちいと思っています。