-
Notifications
You must be signed in to change notification settings - Fork 21
/
eval_.py
94 lines (81 loc) · 2.61 KB
/
eval_.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import inspect
from pycell.env import Env
def _operation(expr, env):
arg1 = eval_expr(expr[2], env)
arg2 = eval_expr(expr[3], env)
if expr[1] == "+":
return ("number", arg1[1] + arg2[1])
elif expr[1] == "-":
return ("number", arg1[1] - arg2[1])
elif expr[1] == "*":
return ("number", arg1[1] * arg2[1])
elif expr[1] == "/":
return ("number", arg1[1] / arg2[1])
else:
raise Exception("Unknown operation: " + expr[1])
def fail_if_wrong_number_of_args(fn_name, params, args):
if len(params) != len(args):
raise Exception((
"%d arguments passed to function %s, but it "
+ "requires %d arguments."
) % (len(args), fn_name, len(params)))
def _function_call(expr, env):
fn = eval_expr(expr[1], env)
args = list((eval_expr(a, env) for a in expr[2]))
if fn[0] == "function":
params = fn[1]
fail_if_wrong_number_of_args(expr[1], params, args)
body = fn[2]
fn_env = fn[3]
new_env = Env(fn_env)
for p, a in zip(params, args):
new_env.set(p[1], a)
return eval_list(body, new_env)
elif fn[0] == "native":
py_fn = fn[1]
params = inspect.getargspec(py_fn).args
fail_if_wrong_number_of_args(expr[1], params[1:], args)
return fn[1](env, *args)
else:
raise Exception(
"Attempted to call something that is not a function: %s" %
str(fn)
)
def eval_expr(expr, env):
typ = expr[0]
if typ == "number":
return ("number", float(expr[1]))
elif typ == "string":
return ("string", expr[1])
elif typ == "none":
return ("none",)
elif typ == "operation":
return _operation(expr, env)
elif typ == "symbol":
name = expr[1]
ret = env.get(name)
if ret is None:
raise Exception("Unknown symbol '%s'." % name)
else:
return ret
elif typ == "assignment":
var_name = expr[1][1]
if var_name in env.items:
raise Exception("Not allowed to re-assign symbol '%s'." % var_name)
val = eval_expr(expr[2], env)
env.set(var_name, val)
return val
elif typ == "call":
return _function_call(expr, env)
elif typ == "function":
return ("function", expr[1], expr[2], Env(env))
else:
raise Exception("Unknown expression type: " + str(expr))
def eval_iter(exprs, env):
for expr in exprs:
yield eval_expr(expr, env)
def eval_list(exprs, env):
ret = ("none",)
for expr in eval_iter(exprs, env):
ret = expr
return ret