-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathInterpreter.py
More file actions
305 lines (253 loc) · 12.4 KB
/
Copy pathInterpreter.py
File metadata and controls
305 lines (253 loc) · 12.4 KB
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# From Lark's Documentation:
# The interpreter walks the tree starting at the root, it visits the tree from the root to the leaves (top-down).
# For each node it calls its methods (inherited) according to tree.data. Differently from transformer, the interpreter
# does not automatically visit the sub-branches, unless it is explicitly told to do so.
from copy import deepcopy
# Interpreter allows to implement branching, loops and functions
from lark.visitors import Interpreter
from Transformer import TreeToJS
from SymbolTable import symbol_table, SymbolTable
from error_handling import *
# the transformer is used to visit the tree from the leaves to the root (bottom-up)
js_transformer = TreeToJS(symbol_table=symbol_table)
js_falsy_values = ['undefined', 'null', 'NaN', False, 0, "", "0"]
class JavaScriptInterpreter(Interpreter):
def start(self, tree):
return self.visit_children(tree)
def if_statement(self, tree):
condition = js_transformer.transform(tree.children[0])
new_symbol_table = SymbolTable(parent=deepcopy(js_transformer.symbol_table)) # create a new symbol table for the if statement
js_transformer.symbol_table = new_symbol_table # update the symbol table of the transformer
if condition not in js_falsy_values: # JavaScript falsy values
true_branch = self.visit(tree.children[1]) # visit the true branch
if not true_branch:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return 'undefined'
elif type(true_branch) == list:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return true_branch[-1]
else:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return true_branch
elif len(tree.children) == 3: # if there is else branch
false_branch = self.visit(tree.children[2]) # visit the false branch
if not false_branch:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return 'undefined'
elif type(false_branch) == list:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return false_branch[-1]
else:
js_transformer.symbol_table = deepcopy(
js_transformer.symbol_table.parent) # update the symbol table of the transformer
return false_branch
def while_statement(self, tree):
"""
while statement has its own block scope, thus we have to create a new symbol table to manage this scope
"""
condition = js_transformer.transform(tree.children[0]) # evaluate the condition
new_symbol_table = SymbolTable(parent=deepcopy(js_transformer.symbol_table)) # create a new symbol table for the while statement
js_transformer.symbol_table = new_symbol_table # update the symbol table of the transformer
while condition not in js_falsy_values:
js_transformer.symbol_table.table = {} # clear the scope for the next iteration
if tree.children[1].data == 'block':
for i in range(len(tree.children[1].children)):
if tree.children[1].children[i].data == 'return_statement':
out = self.visit(tree.children[1].children[i])
js_transformer.symbol_table = deepcopy(js_transformer.symbol_table.parent)
return out
else:
out = self.visit(tree.children[1].children[i])
elif tree.children[1].data == 'return_statement':
out = self.visit(tree.children[1])
js_transformer.symbol_table = deepcopy(js_transformer.symbol_table.parent)
return out
else:
out = self.visit(tree.children[1])
condition = js_transformer.transform(tree.children[0]) # evaluate the condition
js_transformer.symbol_table = deepcopy(js_transformer.symbol_table.parent) # update the symbol table of the transformer
if type(out) == list:
return out[-1]
else:
return out
def ternary_condition_statement(self, tree):
condition = js_transformer.transform(tree.children[0])
if condition not in js_falsy_values:
true_branch = self.visit(tree.children[1])
if type(true_branch) == list:
raise Exception('SyntaxError: Unexpected token')
else:
return true_branch
else:
false_branch = self.visit(tree.children[2])
if type(false_branch) == list:
raise Exception('SyntaxError: Unexpected token')
else:
return false_branch
@staticmethod
def function_declaration(tree):
try:
declaration = tree.children[0]
identifier = tree.children[1]
if identifier in reserved_words:
raise ReservedWordAsIdentifier
if len(tree.children) == 5: # if there are no parameters
parameter_list = []
function_body = tree.children[4] # it is a subtree
else:
if str(type(tree.children[3])) == "<class 'lark.lexer.Token'>":
parameter_list = [tree.children[3]]
elif str(type(tree.children[3])) == "<class 'lark.tree.Tree'>":
parameter_list = tree.children[3].children
function_body = tree.children[5] # it is a subtree
symbol_table.insert(identifier, {'declaration': declaration, 'parameter_list': parameter_list,
'body': function_body, 'type': declaration}) # insert the function body a subtree to be evaluated later
return 'undefined'
except ReservedWordAsIdentifier:
print('SyntaxError: Unexpected token ' + identifier)
def function_call(self, tree):
try:
identifier = tree.children[0]
# take the argument list
if len(tree.children) == 3:
argument_list = [] # if there are no arguments
elif len(tree.children[2].children) > 1: # if there are more than one argument
argument_list = js_transformer.transform(tree.children[2]).children
else: # if there is only one argument convert to list to adapt to the interface used for other cases
argument_list = [js_transformer.transform(tree.children[2])]
# search for the function in the symbol table
function = symbol_table.find(identifier)
# check if the identifier is associated with function
if function['declaration'] == 'function':
# take the parameter list
parameter_list = function['parameter_list']
# create a new symbol table for the function
new_symbol_table = SymbolTable(parent=deepcopy(js_transformer.symbol_table))
# take the function body
function_body = function['body']
for i in range(len(parameter_list)):
# insert the parameter in the new symbol table
new_symbol_table.insert(parameter_list[i], {'declaration': 'var', 'value': argument_list[i],
'type': type(argument_list[i])})
# update the symbol table of the transformer
js_transformer.symbol_table = new_symbol_table
if function_body.data == 'block':
for i in range(len(function_body.children)): # in case of a block, execute all the statements in it
if function_body.children[i].data == 'return_statement':
out = js_transformer.transform(function_body.children[i])
# update the symbol table of the transformer
js_transformer.symbol_table = deepcopy(new_symbol_table.parent)
return out
else:
visited_body = self.visit(function_body.children[i])
# update the symbol table of the transformer
js_transformer.symbol_table = deepcopy(new_symbol_table.parent)
return 'undefined'
elif function_body.data == 'return_statement':
out = js_transformer.transform(function_body)
# update the symbol table of the transformer
js_transformer.symbol_table = deepcopy(new_symbol_table.parent)
return out
else: # the body doesn't contain a return statement, neither a block
visited_body = self.visit(function_body)
# update the symbol table of the transformer
js_transformer.symbol_table = deepcopy(new_symbol_table.parent)
return 'undefined'
else:
raise IsNotAFunction # the identifier is not associated with a function
except IsNotAFunction:
print('TypeError: ' + identifier + ' is not a function')
except ReferenceError:
print('ReferenceError: ' + identifier + ' is not defined')
@staticmethod
def return_statement(tree):
return js_transformer.transform(tree)
@staticmethod
def print_statement(tree):
return js_transformer.transform(tree)
@staticmethod
def input_statement(tree):
return js_transformer.transform(tree)
def variable_statement(self, tree):
for i in range(len(tree.children)):
if str(type(tree.children[i])) == "<class 'lark.tree.Tree'>":
if tree.children[i].data == 'function_call':
tree.children[i] = self.visit(tree.children[i]) # this is required to assign the value of a function call to a variable
return js_transformer.transform(tree)
@staticmethod
def variable_assignment(tree):
return js_transformer.transform(tree)
@staticmethod
def logical_and(tree):
return js_transformer.transform(tree)
@staticmethod
def logical_or(tree):
return js_transformer.transform(tree)
@staticmethod
def equality(tree):
return js_transformer.transform(tree)
@staticmethod
def inequality(tree):
return js_transformer.transform(tree)
@staticmethod
def strict_equality(tree):
return js_transformer.transform(tree)
@staticmethod
def strict_inequality(tree):
return js_transformer.transform(tree)
@staticmethod
def greater_than(tree):
return js_transformer.transform(tree)
@staticmethod
def greater_than_or_equal(tree):
return js_transformer.transform(tree)
@staticmethod
def less_than(tree):
return js_transformer.transform(tree)
@staticmethod
def less_than_or_equal(tree):
return js_transformer.transform(tree)
@staticmethod
def add(tree):
return js_transformer.transform(tree)
@staticmethod
def sub(tree):
return js_transformer.transform(tree)
@staticmethod
def mul(tree):
return js_transformer.transform(tree)
@staticmethod
def div(tree):
return js_transformer.transform(tree)
@staticmethod
def negative(tree):
return js_transformer.transform(tree)
@staticmethod
def logical_not(tree):
return js_transformer.transform(tree)
@staticmethod
def template_literal(tree):
return js_transformer.transform(tree)
@staticmethod
def factor(tree):
return js_transformer.transform(tree)
@staticmethod
def term(tree):
return js_transformer.transform(tree)
@staticmethod
def expression(tree):
return js_transformer.transform(tree)
@staticmethod
def array(tree):
return js_transformer.transform(tree)
@staticmethod
def array_access(tree):
return js_transformer.transform(tree)
@staticmethod
def array_length(tree):
return js_transformer.transform(tree)