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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
from parser import Expr
def interpret(exprs):
ret = None
for expr in exprs:
ret = evaluate(expr)
return ret
def evaluate(expr):
if isinstance(expr, Expr.Literal):
return expr.value
elif expr.symbol.name == "not":
return interpretNot(expr)
elif expr.symbol.name in ("*", "/"):
return interpretFactor(expr)
elif expr.symbol.name in ("-", "+"):
return interpretTerm(expr)
elif expr.symbol.name in (">", ">=", "<", "<="):
return interpretComparison(expr)
elif expr.symbol.name == "eq?":
return interpretEq(expr)
elif expr.symbol.name == "and":
return interpretAnd(expr)
elif expr.symbol.name == "or":
return interpretOr(expr)
# flow control
elif expr.symbol.name == "if":
return interpretIf(expr)
# io
elif expr.symbol.name == "print":
return interpretPrint(expr)
else:
raise Exception(f"unable to evaluate: {expr}")
def interpretOr(expr):
# or returns true for the first expression that returns true
if len(expr.args) < 2:
raise Exception("'or' has at least two operands")
for arg in expr.args:
ev = evaluate(arg)
if ev not in (True, False):
raise Exception("'or' needs boolean arguments")
if ev == True:
return True
return False
def interpretAnd(expr):
# and returns false for the first expression that returns false
if len(expr.args) < 2:
raise Exception("'and' has at least two operands")
for arg in expr.args:
ev = evaluate(arg)
if ev not in (True, False):
raise Exception("'and' needs boolean arguments")
if ev == False:
return False
return True
def interpretEq(expr):
# equal
if len(expr.args) != 2:
raise Exception("'eq?' needs two arguments")
first = evaluate(expr.args[0])
second = evaluate(expr.args[1])
return first == second
def interpretComparison(expr):
if len(expr.args) != 2:
raise Exception("comparisons have two operands")
left = evaluate(expr.args[0])
if type(left) not in (int, float):
raise Exception("'left' must be a number")
right = evaluate(expr.args[1])
if type(right) not in (int, float):
raise Exception("'right' must be a number")
if expr.symbol.name == ">":
return left > right
elif expr.symbol.name == ">=":
return left >= right
elif expr.symbol.name == "<":
return left < right
elif expr.symbol.name == "<=":
return left <= right
def interpretTerm(expr):
if len(expr.args) < 1:
raise Exception("term has at least one operand")
res = 0
for arg in expr.args:
ev = evaluate(arg)
if type(ev) not in (int, float):
raise Exception("term must be a number")
if expr.symbol.name == "+":
res += ev
elif expr.symbol.name == "-":
res -= ev
return res
def interpretFactor(expr):
if expr.symbol.name == "/":
if len(expr.args) != 2:
raise Exception("'/' requires two operands")
num = evaluate(expr.args[0])
if type(num) not in (int, float):
raise Exception("numerator must be a number")
denom = evaluate(expr.args[1])
if type(denom) not in (int, float):
raise Exception("denominator must be a number")
return num / denom # TODO floats and ints
else:
if len(expr.args) < 2:
raise Exception("'*' requires at least two operands")
first = evaluate(expr.args[0])
if type(first) not in (int, float):
raise Exception("'*' operand must be a number")
res = first
for arg in expr.args[1:]:
tmp = evaluate(arg)
if type(tmp) not in (int, float):
raise Exception("'*' operand must be a number")
res = res * tmp
return res
def interpretNot(expr):
if len(expr.args) != 1:
raise Exception("'not' takes one operand")
res = evaluate(expr.args[0])
if res not in (True, False):
raise Exception("'not' only works on booleans")
return not res
def interpretIf(expr):
# if cond t-branch [f-branch]
if len(expr.args) not in (2, 3):
raise Exception("too many or too few operands")
cond = evaluate(expr.args[0])
if cond not in (True, False):
raise Exception("'if' condition must be boolean")
if cond:
return evaluate(expr.args[1])
elif len(expr.args) == 3:
return evaluate(expr.args[2])
return None # this shouldn't be reached
def interpretPrint(expr):
if len(expr.args) == 0:
print()
elif len(expr.args) == 1:
ev = evaluate(expr.args[0])
if not isinstance(ev, str):
raise Exception("can only 'print' strings")
print(ev)
else:
raise Exception("'print' takes zero or one argument")
return None # print returns nothing
|