-
Notifications
You must be signed in to change notification settings - Fork 0
/
Interpreter.py
241 lines (198 loc) · 8.74 KB
/
Interpreter.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
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
from Stmt import *
from Expr import *
from typing import Any
from TokenType import TokenType
from Environment import Environment
from JesseCallable import JesseCallable
from JesseRuntimeError import JesseRuntimeError
from JesseFunction import JesseFunction
from Clock import Clock
from ReturnException import ReturnException
class Interpreter:
'''
The interpreter class for the Jesse programming language.
It implements all the visitor methods for the AST nodes.
'''
def __init__(self, jesse:object,source:str) -> None:
self.jesse = jesse
self.globals = Environment()
self.environment = self.globals
self.source = source
self.lines = source.splitlines()
self.globals.define("clock", Clock())
# Mappping of expressions to their depth in the stack
self.locals = {}
def interpret(self, statements: List[Stmt]) -> None:
try:
for statement in statements:
self.execute(statement)
except JesseRuntimeError as error:
pos = error.pos
code = self.lines[pos[0] - 1]
self.jesse.runtime_error(code,pos,error)
def visit_literal_expr(self, expr: Literal) -> Any:
return expr.value
def visit_logical_expr(self, expr: Logical) -> Any:
left = self.evaluate(expr.left)
if expr.operator.token_type == TokenType.OR:
if self.is_truthy(left):
return left
else:
if not self.is_truthy(left):
return left
return self.evaluate(expr.right)
def visit_unary_expr(self, expr: Unary) -> Any:
right = self.evaluate(expr.right)
if expr.operator.token_type == TokenType.BANG:
return not self.is_truthy(right)
elif expr.operator.token_type == TokenType.MINUS:
self.check_number_operand(expr.operator, right)
return -right
# Unreachable.
return None
def visit_variable_expr(self, expr: Expr) -> None:
return self.look_up_variable(expr.name, expr)
def look_up_variable(self, name: Token, expr: Expr) -> Any:
distance = self.locals.get(expr)
print(self.locals)
if distance is not None:
return self.environment.get_at(distance, name.lexeme)
else:
return self.globals.get(name)
def check_number_operand(self, operator: Token, operand: Any) -> None:
if isinstance(operand, float):
return
raise JesseRuntimeError(operator.pos, "operands must be a numbers, its basic math yo")
def check_number_operands(self, operator: Token, left: Any, right: Any) -> None:
if isinstance(left, float) and isinstance(right, float):
return
raise JesseRuntimeError(operator.pos, "operands must be numbers, its basic math yo")
def is_truthy(self, obj: Any) -> bool:
if obj is None or obj == 0:
return False
if isinstance(obj, bool):
return obj
return True
def is_equal(self, a: Any, b: Any) -> bool:
return a == b
def stringify(self, obj: Any) -> str:
# Convert None to "i am the danger".
# Convert True to "cartel" and False to "dea"
# Convert floats to strings and append "gm" at the end
if obj is None:
return "i am the danger"
if isinstance(obj, bool):
return "cartel" if obj else "dea"
if isinstance(obj, float):
return f"{obj:g}gm"
if isinstance(obj, str):
return obj.strip('"')
return str(obj)
def visit_grouping_expr(self, expr: Grouping) -> Any:
return self.evaluate(expr.expression)
def evaluate(self, expr: Expr) -> Any:
return expr.accept(self)
def execute(self, stmt: Stmt) -> None:
stmt.accept(self)
def resolve(self, expr:Expr, depth:int) -> None:
self.locals[expr] = depth
def execute_block(self, statements: List[Stmt], environment: Environment) -> None:
previous = self.environment
try:
self.environment = environment
for statement in statements:
self.execute(statement)
finally:
self.environment = previous
def visit_block_stmt(self, stmt: Block) -> None:
self.execute_block(stmt.statements, Environment(self.environment))
def visit_jesseif_stmt(self, stmt: JesseIf) -> None:
if self.is_truthy(self.evaluate(stmt.condition)):
self.execute(stmt.then_branch)
elif stmt.else_branch is not None:
self.execute(stmt.else_branch)
def visit_expression_stmt(self, stmt: Expression) -> None:
self.evaluate(stmt.expression)
def visit_bettercall_stmt(self, stmt: BetterCall) -> None:
function = JesseFunction(stmt,self.environment)
self.environment.define(stmt.name.lexeme, function)
return None
def visit_saymyname_stmt(self, stmt: SayMyName) -> None:
value = self.evaluate(stmt.expression)
print(self.stringify(value))
def visit_return_stmt(self, stmt: Return) -> None:
value = None
if stmt.value != None:
value = self.evaluate(stmt.value)
raise ReturnException(value)
def visit_cook_stmt(self, stmt: Cook) -> None:
value = None
if(stmt.initializer != None):
value = self.evaluate(stmt.initializer)
self.environment.define(stmt.name.lexeme,value)
def visit_theonewhoknocks_stmt(self, stmt: TheOneWhoKnocks) -> None:
while self.is_truthy(self.evaluate(stmt.condition)):
self.execute(stmt.body)
return None
def visit_assign_expr(self, expr: Assign) -> Any:
value = self.evaluate(expr.value)
distance = self.locals.get(expr)
if distance is not None:
self.environment.assign_at(distance, expr.name, value)
else:
self.globals.assign(expr.name, value)
return value
def visit_ternary_expr(self, expr: Ternary) -> Any:
condition = self.evaluate(expr.condition)
if self.is_truthy(condition):
return self.evaluate(expr.then_branch)
else:
return self.evaluate(expr.else_branch)
def visit_binary_expr(self, expr: Binary) -> Any:
left = self.evaluate(expr.left)
right = self.evaluate(expr.right)
if expr.operator.token_type == TokenType.MINUS:
self.check_number_operands(expr.operator, left, right)
return left - right
elif expr.operator.token_type == TokenType.SLASH:
self.check_number_operands(expr.operator, left, right)
return left / right
elif expr.operator.token_type == TokenType.STAR:
self.check_number_operands(expr.operator, left, right)
return left * right
elif expr.operator.token_type == TokenType.PLUS:
if isinstance(left, float) and isinstance(right, float):
return left + right
if isinstance(left, str) and isinstance(right, str):
return left + right
raise JesseRuntimeError(expr.operator.pos, "operands must be two numbers or two strings yo")
elif expr.operator.token_type == TokenType.GREATER:
self.check_number_operands(expr.operator, left, right)
return left > right
elif expr.operator.token_type == TokenType.GREATER_EQUAL:
self.check_number_operands(expr.operator, left, right)
return left >= right
elif expr.operator.token_type == TokenType.LESS:
self.check_number_operands(expr.operator, left, right)
return left < right
elif expr.operator.token_type == TokenType.LESS_EQUAL:
self.check_number_operands(expr.operator, left, right)
return left <= right
elif expr.operator.token_type == TokenType.BANG_EQUAL:
return not self.is_equal(left, right)
elif expr.operator.token_type == TokenType.EQUAL_EQUAL:
return self.is_equal(left, right)
def visit_call_expr(self,expr: Call) -> Any:
# Evaluate the callee, usually an identifier
callee = self.evaluate(expr.callee)
arguments = []
# Evaluate the arguments which can also be identifiers or literals
for argument in expr.arguments:
arguments.append(self.evaluate(argument))
# Check if the callee is a JesseCallable object
if not isinstance(callee, JesseCallable):
raise JesseRuntimeError(expr.paren.pos,"you can only call functions and classes yo")
# Check arity of the function
if len(arguments) != callee.arity():
raise JesseRuntimeError(expr.paren.pos,f"expected {callee.arity()} arguments but got {len(arguments)} yo")
return callee.call(self,arguments)