Created
October 11, 2014 16:58
-
-
Save ihodes/6ea33e4538dbeba4ee8a to your computer and use it in GitHub Desktop.
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
def tokenize(string): | |
return string.replace('(', ' ( ').replace(')', ' ) ').split() | |
def parse(tokens): | |
expression, estack = [], [] | |
for token in tokens: | |
if token is '(': | |
estack.append(expression) | |
expression = [] | |
elif token is ')': | |
expression = estack.pop() + [expression] | |
else: | |
expression.append(token) | |
return expression | |
def read(string): | |
return parse(tokenize(string)) | |
class Env(dict): | |
def __init__(self, vars=(), vals=(), env=None): | |
self.update(zip(vars, vals)) | |
self.env = env | |
def lookup(self, var): | |
if var in self: | |
return self[var] | |
elif self.env: | |
return self.env.lookup(var) | |
else: | |
raise ValueError('"' + var + '" not in scope.') | |
FUNCTIONS = {'atom': lambda x: not isinstance(x, list), 'eq': lambda x, y: x == y, | |
'cons': lambda x, y: [x] + y if y else [x], 'car': lambda x: x[0], | |
'cdr': lambda x: x[1:]} | |
def eval(expr, env): | |
if expr == 'nil': | |
return None | |
elif isinstance(expr, str): | |
return env.lookup(expr) | |
elif expr[0] == 'quote': | |
return expr[1] | |
elif expr[0] == 'cond': | |
_, test, texpr, eexpr = expr | |
return eval(texpr) if eval(test) else eval(eexpr) | |
elif expr[0] == 'lambda': | |
_, vars, expr = expr | |
return lambda *args: eval(expr, Env(vars=vars, vals=args, env=env)) | |
elif expr[0] == 'label': | |
_, var, expr = expr | |
env[var] = eval(expr, env) | |
return (var, eval(expr, env)) | |
else: | |
expr = [eval(e, env) for e in expr] | |
return expr[0](*expr[1:]) | |
def pr(expr): | |
if expr is None: | |
return 'nil' | |
elif isinstance(expr, list): | |
return '(' + ' '.join(pr(e) for e in expr) + ')' | |
else: | |
return expr | |
def loop(env, prompt): | |
while True: | |
try: | |
string = raw_input(prompt) | |
if string[0] == '%' and string[1:5] == 'load': | |
with open(string[6:] + '.scm') as f: | |
string = f.read() | |
else: | |
while string.count('(') != string.count(')'): | |
string = string + raw_input(' ') | |
for val in (eval(expr, env) for expr in read(string)): | |
print(pr(val)) | |
except EOFError: | |
print "\nExiting REPL..." | |
break | |
except Exception as e: | |
print "Error: ", e | |
GLOBAL = Env() | |
GLOBAL.update(FUNCTIONS) | |
if __name__ == '__main__': | |
loop(global_env, '> ') | |
assert (tokenize('(apply max (cons (+ 1 2) somelist))') == | |
['(', 'apply', 'max', '(', 'cons', '(', '+', '1', '2', ')', 'somelist', ')', ')']) | |
assert (parse(['(', 'apply', 'max', '(', 'cons', '(', '+', '1', '2', ')', 'somelist', ')', ')'])[0] == ['apply', 'max', ['cons', ['+', '1', '2'], 'somelist']]) | |
assert print_(['cons', '1', ['+', '1', '2']]) == '(cons 1 (+ 1 2))' | |
assert eval(['cons', ['quote', 'apple'], 'nil'], GLOBAL) == ['apple'] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment