-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdumbo.py
162 lines (136 loc) · 5.42 KB
/
dumbo.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
from __future__ import annotations
from typing import Union
import dumboParser as dp
from visitors import Visitor
from data import data_parser
from collections import ChainMap
import argh
class Scope(ChainMap):
"""Variant of ChainMap that allows direct updates to inner scopes
source : https://docs.python.org/3/library/collections.html#collections.ChainMap"""
def __setitem__(self, key, value):
for mapping in self.maps:
if key in mapping:
mapping[key] = value
return
self.maps[0][key] = value
def __delitem__(self, key):
for mapping in self.maps:
if key in mapping:
del mapping[key]
return
raise KeyError(key)
class BadReferenceError(Exception):
def __init__(self, variable_name=''):
super().__init__(f"Variable '{variable_name}' used before assignment")
class NotIterableError(Exception):
def __init__(self, variable_name=''):
super().__init__(f"Variable '{variable_name}' is not iterable")
class Interpreter(Visitor):
def __init__(self, scope, verbose=False):
self.scope = Scope(scope)
self.result = ''
self.verbose = verbose
self.replacements = {
bool: lambda x: 'true' if x else 'false',
list: lambda x: str(x).replace('[', '(').replace(']', ')'),
int: lambda x: str(x),
str: lambda x: x
}
def visit_print_element(self, element: dp.PrintElement) -> None:
tmp = element.str_expression
if type(element.str_expression) not in dp.primitives:
tmp = element.str_expression.accept(self)
result = self.replacements[type(tmp)](tmp)
self.result += result
if self.verbose:
print(result, end='')
def visit_for_element(self, element: dp.ForElement) -> None:
if type(element.iterator) is dp.VariableElement:
iterator = element.iterator.accept(self)
else:
iterator = element.iterator
if type(iterator) is not list:
raise NotIterableError(element.iterator.name)
for string in iterator:
self.scope[element.iterator_var.name] = string
self.scope = self.scope.new_child()
element.expressions_list.accept(self)
self.scope = self.scope.parents
def visit_se_element(self, element: dp.SEElement) -> str:
res = ''
for e in element.subExpressions:
if type(e) not in dp.primitives:
e = e.accept(self)
res += self.replacements[type(e)](e)
return res
def visit_ae_element(self, element: dp.AEElement) -> int:
operations = {
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x // y
}
left = element.left
right = element.right
if type(left) is not int:
left = left.accept(self)
if type(right) is not int:
right = right.accept(self)
return operations[element.op](left, right)
def visit_be_element(self, element: dp.BEElement) -> bool:
operations = {
'and': lambda x, y: x and y,
'or': lambda x, y: x or y,
'<': lambda x, y: x < y,
'>': lambda x, y: x > y,
'=': lambda x, y: x == y,
'!=': lambda x, y: x != y
}
left = element.left
right = element.right
if type(left) not in [bool, int]:
left = left.accept(self)
if type(right) not in [bool, int]:
right = right.accept(self)
return operations[element.op](left, right)
def visit_expressions_list_element(self, element: dp.ExpressionsListElement) -> None:
for exp in element.expressions_list:
exp.accept(self)
def visit_assign_element(self, element: dp.AssignElement) -> None:
if type(element.value) in dp.primitives:
self.scope[element.variable.name] = element.value
else:
self.scope[element.variable.name] = element.value.accept(self)
def visit_program_element(self, element: dp.ProgramElement) -> None:
for el in element.content:
if type(el) is str:
self.result += el
if self.verbose:
print(el, end='')
else:
el.accept(self)
if self.verbose:
print()
def visit_variable_element(self, element: dp.VariableElement) -> Union[int, str, bool, list[str]]:
if element.name in self.scope:
return self.scope[element.name]
raise BadReferenceError(element.name)
def visit_if_element(self, element: dp.IfElement) -> None:
condition = element.boolean_expression if type(element.boolean_expression) is bool \
else element.boolean_expression.accept(self)
if condition:
self.scope = self.scope.new_child()
element.expressions_list.accept(self)
self.scope = self.scope.parents
def main(data_file_name, src_file_name):
with open(data_file_name) as data_file:
data = data_file.read()
scope = data_parser.parse(data)
with open(src_file_name) as src_file:
src = src_file.read()
program = dp.dumbo_parser.parse(src)
interpreter = Interpreter(scope, verbose=True)
program.accept(interpreter)
if __name__ == '__main__':
argh.dispatch_command(main)