hexus-lang 0.1.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hexus_lang-0.1.0b1/Hexus/cli.py +57 -0
- hexus_lang-0.1.0b1/Hexus/interpreter.py +515 -0
- hexus_lang-0.1.0b1/Hexus/lexer.py +42 -0
- hexus_lang-0.1.0b1/Hexus/main.py +34 -0
- hexus_lang-0.1.0b1/Hexus/modules/__init__.py +0 -0
- hexus_lang-0.1.0b1/Hexus/modules/file.py +191 -0
- hexus_lang-0.1.0b1/Hexus/modules/time.py +197 -0
- hexus_lang-0.1.0b1/Hexus/parser.py +924 -0
- hexus_lang-0.1.0b1/LICENSE +674 -0
- hexus_lang-0.1.0b1/PKG-INFO +685 -0
- hexus_lang-0.1.0b1/README.md +1 -0
- hexus_lang-0.1.0b1/hexus_lang.egg-info/PKG-INFO +685 -0
- hexus_lang-0.1.0b1/hexus_lang.egg-info/SOURCES.txt +16 -0
- hexus_lang-0.1.0b1/hexus_lang.egg-info/dependency_links.txt +1 -0
- hexus_lang-0.1.0b1/hexus_lang.egg-info/entry_points.txt +2 -0
- hexus_lang-0.1.0b1/hexus_lang.egg-info/top_level.txt +1 -0
- hexus_lang-0.1.0b1/pyproject.toml +17 -0
- hexus_lang-0.1.0b1/setup.cfg +4 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
from Hexus.lexer import tokenizer_tokens
|
|
4
|
+
from Hexus.parser import HexusParser
|
|
5
|
+
from Hexus.interpreter import HexusInterpreter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
if len(sys.argv) < 2:
|
|
10
|
+
print("Hexus Programming Language")
|
|
11
|
+
print("Use: hexus <file.he>")
|
|
12
|
+
sys.exit(1)
|
|
13
|
+
|
|
14
|
+
file_name = sys.argv[1]
|
|
15
|
+
|
|
16
|
+
if file_name in ["--help", "-h"]:
|
|
17
|
+
print("Hexus Programming Language CLI")
|
|
18
|
+
print("Use: hexus <file.he>")
|
|
19
|
+
sys.exit(0)
|
|
20
|
+
|
|
21
|
+
if not os.path.exists(file_name):
|
|
22
|
+
print(f"Error: File '{file_name}' does not exist!")
|
|
23
|
+
sys.exit(1)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
with open(file_name, "r", encoding="utf-8") as file:
|
|
27
|
+
source_code = file.read()
|
|
28
|
+
except Exception as e:
|
|
29
|
+
print(f"Error while openning file: {e}")
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
token_list = tokenizer_tokens(source_code)
|
|
34
|
+
clear_tokens = [t for t in token_list if t[0] != "SKIP"]
|
|
35
|
+
|
|
36
|
+
parser = HexusParser(clear_tokens)
|
|
37
|
+
program_tree = parser.parse()
|
|
38
|
+
|
|
39
|
+
interpreter = HexusInterpreter()
|
|
40
|
+
interpreter.interpret(program_tree)
|
|
41
|
+
|
|
42
|
+
except SyntaxError as e:
|
|
43
|
+
print(f"\n{e}")
|
|
44
|
+
sys.exit(1)
|
|
45
|
+
except NameError as e:
|
|
46
|
+
print(f"\n\n{e}")
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
except KeyboardInterrupt:
|
|
49
|
+
print("\nThe program's operation was interrupted (Ctrl+C).")
|
|
50
|
+
sys.exit(0)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"\nUnexpected error:\n{e}")
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
from Hexus.parser import FunctionDefNode
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
import importlib
|
|
4
|
+
import pkgutil
|
|
5
|
+
from Hexus import modules
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BreakException(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class ContinueException(Exception):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
class ReturnException(Exception):
|
|
15
|
+
def __init__(self, value):
|
|
16
|
+
self.value = value
|
|
17
|
+
|
|
18
|
+
class Environment:
|
|
19
|
+
def __init__(self, parent=None):
|
|
20
|
+
self.vars = {}
|
|
21
|
+
self.parent = parent
|
|
22
|
+
|
|
23
|
+
def get(self, name):
|
|
24
|
+
if name in self.vars:
|
|
25
|
+
return self.vars[name]
|
|
26
|
+
if self.parent:
|
|
27
|
+
return self.parent.get(name)
|
|
28
|
+
raise NameError(f"Variable '{name}' is not defined!!!")
|
|
29
|
+
|
|
30
|
+
def set(self, name, value):
|
|
31
|
+
self.vars[name] = value
|
|
32
|
+
|
|
33
|
+
def __contains__(self, name):
|
|
34
|
+
try:
|
|
35
|
+
self.get(name)
|
|
36
|
+
return True
|
|
37
|
+
except NameError:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class HexusInterpreter:
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.env = Environment()
|
|
45
|
+
self._register_type1_modules()
|
|
46
|
+
self.timer = 0
|
|
47
|
+
self.timer2 = 0
|
|
48
|
+
self.timerset = False
|
|
49
|
+
|
|
50
|
+
def _register_type1_modules(self):
|
|
51
|
+
for _, mod_name, _ in pkgutil.iter_modules(modules.__path__):
|
|
52
|
+
if mod_name.endswith("_ext"):
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
py_mod = importlib.import_module(f".modules.{mod_name}", package=__package__)
|
|
56
|
+
for attr_name in dir(py_mod):
|
|
57
|
+
attr = getattr(py_mod, attr_name)
|
|
58
|
+
if attr_name.endswith("Interpreter") and hasattr(attr, "register_handlers"):
|
|
59
|
+
attr.register_handlers(self)
|
|
60
|
+
|
|
61
|
+
def visit(self, node) -> Any:
|
|
62
|
+
method_name = f"visit_{type(node).__name__}"
|
|
63
|
+
visitor: Callable[[Any], Any] = getattr(self, method_name, self.generic_visit)
|
|
64
|
+
return visitor(node)
|
|
65
|
+
|
|
66
|
+
def generic_visit(self, node, *args, **kwargs):
|
|
67
|
+
_ = args, kwargs, self
|
|
68
|
+
raise Exception(f"Interpreter error: No visit method defined for {type(node).__name__}")
|
|
69
|
+
|
|
70
|
+
def visit_NumberNode(self, node):
|
|
71
|
+
_ = self
|
|
72
|
+
value = str(node.value)
|
|
73
|
+
if "." in value:
|
|
74
|
+
return float(value)
|
|
75
|
+
return int(value)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def visit_StringNode(self, node):
|
|
79
|
+
if isinstance(node.value, str):
|
|
80
|
+
words_list = node.value.split()
|
|
81
|
+
else:
|
|
82
|
+
words_list = node.value
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if len(words_list) == 1:
|
|
86
|
+
text = words_list[0][1:-1]
|
|
87
|
+
if text.startswith("{") and text.endswith("}"):
|
|
88
|
+
var_name = text[1:-1]
|
|
89
|
+
if var_name in self.env:
|
|
90
|
+
return str(self.env.get(var_name))
|
|
91
|
+
return text
|
|
92
|
+
first = words_list[0][1:]
|
|
93
|
+
middle = words_list[1:-1]
|
|
94
|
+
last = words_list[-1][:-1]
|
|
95
|
+
newtxt = [first] + middle + [last]
|
|
96
|
+
for i, t in enumerate(newtxt):
|
|
97
|
+
if t.startswith("{") and t.endswith("}"):
|
|
98
|
+
t = t[1:-1]
|
|
99
|
+
if t in self.env:
|
|
100
|
+
txt = self.env.get(t)
|
|
101
|
+
newtxt[i] = str(txt)
|
|
102
|
+
|
|
103
|
+
full_txt = " ".join(newtxt)
|
|
104
|
+
return full_txt
|
|
105
|
+
|
|
106
|
+
def visit_VariableNode(self, node):
|
|
107
|
+
if node.name in self.env:
|
|
108
|
+
return self.env.get(node.name)
|
|
109
|
+
raise NameError(f"Variable '{node.name}' is not defined!!!")
|
|
110
|
+
|
|
111
|
+
def visit_NowNode(self, node):
|
|
112
|
+
_ = node, self
|
|
113
|
+
import time
|
|
114
|
+
now = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
115
|
+
return now
|
|
116
|
+
|
|
117
|
+
def visit_BoolNode(self, node):
|
|
118
|
+
_ = self
|
|
119
|
+
return bool(node.value)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def visit_BinaryOpNode(self, node):
|
|
123
|
+
left_val = self.visit(node.left)
|
|
124
|
+
right_val = self.visit(node.right)
|
|
125
|
+
|
|
126
|
+
if node.op == "+": return left_val + right_val
|
|
127
|
+
if node.op == "-": return left_val - right_val
|
|
128
|
+
if node.op == "*": return left_val * right_val
|
|
129
|
+
if node.op == "/": return left_val / right_val
|
|
130
|
+
if node.op == "==": return left_val == right_val
|
|
131
|
+
if node.op == "!=": return left_val != right_val
|
|
132
|
+
if node.op == "and": return bool(left_val) and bool(right_val)
|
|
133
|
+
if node.op == "or": return bool(left_val) or bool(right_val)
|
|
134
|
+
if node.op == ">": return left_val > right_val
|
|
135
|
+
if node.op == "<": return left_val < right_val
|
|
136
|
+
if node.op == "<=": return left_val <= right_val
|
|
137
|
+
if node.op == ">=": return left_val >= right_val
|
|
138
|
+
if node.op == "%": return left_val % right_val
|
|
139
|
+
raise ValueError(f"Unknown binary operator: {node.op}")
|
|
140
|
+
|
|
141
|
+
def visit_SendCommandNode(self, node):
|
|
142
|
+
result = self.visit(node.text_value)
|
|
143
|
+
if node.target == "console":
|
|
144
|
+
print(result)
|
|
145
|
+
|
|
146
|
+
def visit_SetVar(self, node):
|
|
147
|
+
var = node.var_name.strip()
|
|
148
|
+
if node.list is True:
|
|
149
|
+
if node.value is None:
|
|
150
|
+
self.env.set(var, [])
|
|
151
|
+
return
|
|
152
|
+
elif node.value:
|
|
153
|
+
list_val = []
|
|
154
|
+
for v in node.value:
|
|
155
|
+
result = self.visit(v)
|
|
156
|
+
list_val.append(result)
|
|
157
|
+
self.env.set(var, list_val)
|
|
158
|
+
return
|
|
159
|
+
value = self.visit(node.value)
|
|
160
|
+
self.env.set(var, value)
|
|
161
|
+
|
|
162
|
+
def visit_ReadCommandNode(self, node):
|
|
163
|
+
value = self.visit(node.text_value)
|
|
164
|
+
var = node.var_name.strip()
|
|
165
|
+
v = None
|
|
166
|
+
if node.target == "console":
|
|
167
|
+
v = input(value)
|
|
168
|
+
|
|
169
|
+
if v.isdigit():
|
|
170
|
+
v = int(v)
|
|
171
|
+
else:
|
|
172
|
+
try:
|
|
173
|
+
v = float(v)
|
|
174
|
+
except ValueError:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
self.env.set(var, v)
|
|
178
|
+
|
|
179
|
+
def visit_ComNode(self, node):
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
def visit_IfNode(self, node):
|
|
183
|
+
exp = self.visit(node.exp)
|
|
184
|
+
if exp:
|
|
185
|
+
for val in node.value:
|
|
186
|
+
self.visit(val)
|
|
187
|
+
return
|
|
188
|
+
elifv = node.elifv
|
|
189
|
+
if elifv:
|
|
190
|
+
for e_exp, e_value in elifv.items():
|
|
191
|
+
if bool(self.visit(e_exp)):
|
|
192
|
+
for val in e_value:
|
|
193
|
+
self.visit(val)
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
if node.value2:
|
|
197
|
+
for val in node.value2:
|
|
198
|
+
self.visit(val)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def visit_ListAddNode(self, node):
|
|
202
|
+
var_name = node.var.name
|
|
203
|
+
current_value = self.env.get(var_name)
|
|
204
|
+
value = self.visit(node.value)
|
|
205
|
+
if isinstance(current_value, list):
|
|
206
|
+
if not node.pos:
|
|
207
|
+
pos = len(current_value) + 1
|
|
208
|
+
else:
|
|
209
|
+
pos = self.visit(node.pos)
|
|
210
|
+
pos -= 1
|
|
211
|
+
current_value.insert(pos, value)
|
|
212
|
+
self.env.set(var_name, current_value)
|
|
213
|
+
else:
|
|
214
|
+
raise NameError(f"Interpreter Error: Variable '{var_name}' (value: {current_value}) is not a list")
|
|
215
|
+
|
|
216
|
+
def visit_ListRemoveNode(self, node):
|
|
217
|
+
value = None
|
|
218
|
+
var_name = node.var.name
|
|
219
|
+
current_value = self.env.get(var_name)
|
|
220
|
+
if node.value:
|
|
221
|
+
value = self.visit(node.value)
|
|
222
|
+
if isinstance(current_value, list):
|
|
223
|
+
if node.pos:
|
|
224
|
+
pos = self.visit(node.pos)
|
|
225
|
+
pos -= 1
|
|
226
|
+
current_value.pop(pos)
|
|
227
|
+
self.env.set(var_name, current_value)
|
|
228
|
+
elif value:
|
|
229
|
+
current_value.remove(value)
|
|
230
|
+
self.env.set(var_name, current_value)
|
|
231
|
+
else:
|
|
232
|
+
raise NameError(f"Interpreter Error: Variable '{var_name}' (value: {current_value}) is not a list")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def visit_WaitNode(self, node):
|
|
236
|
+
import time
|
|
237
|
+
|
|
238
|
+
value = self.visit(node.value)
|
|
239
|
+
|
|
240
|
+
if hasattr(node.value2, "name"):
|
|
241
|
+
value2 = node.value2.name
|
|
242
|
+
else:
|
|
243
|
+
value2 = self.visit(node.value2)
|
|
244
|
+
|
|
245
|
+
if value2 == "s":
|
|
246
|
+
time.sleep(value)
|
|
247
|
+
elif value2 == "m":
|
|
248
|
+
time.sleep(value * 60)
|
|
249
|
+
elif value2 == "h":
|
|
250
|
+
time.sleep(value * 60 * 60)
|
|
251
|
+
elif value2 == "d":
|
|
252
|
+
time.sleep(value * 60 * 60 * 24)
|
|
253
|
+
else:
|
|
254
|
+
raise ValueError(f"Unknown wait unit: {value2}")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def visit_WhileNode(self, node):
|
|
258
|
+
while bool(self.visit(node.exp)):
|
|
259
|
+
try:
|
|
260
|
+
for val in node.value:
|
|
261
|
+
try:
|
|
262
|
+
self.visit(val)
|
|
263
|
+
except ContinueException:
|
|
264
|
+
break
|
|
265
|
+
except BreakException:
|
|
266
|
+
break
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def visit_RepeatNode(self, node):
|
|
270
|
+
value = self.visit(node.value)
|
|
271
|
+
value2 = node.value2
|
|
272
|
+
time = 1
|
|
273
|
+
while time <= value:
|
|
274
|
+
try:
|
|
275
|
+
for val in value2:
|
|
276
|
+
try:
|
|
277
|
+
self.visit(val)
|
|
278
|
+
except ContinueException:
|
|
279
|
+
break
|
|
280
|
+
time += 1
|
|
281
|
+
except BreakException:
|
|
282
|
+
break
|
|
283
|
+
|
|
284
|
+
def visit_ClearNode(self, node):
|
|
285
|
+
_ = node, self
|
|
286
|
+
import subprocess
|
|
287
|
+
import os
|
|
288
|
+
subprocess.run('cls' if os.name == 'nt' else 'clear', shell=True)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def visit_StopNode(self, node):
|
|
292
|
+
if node.value is None:
|
|
293
|
+
value = None
|
|
294
|
+
else:
|
|
295
|
+
value = self.visit(node.value)
|
|
296
|
+
import sys
|
|
297
|
+
if value is None:
|
|
298
|
+
sys.exit("Program stop")
|
|
299
|
+
else:
|
|
300
|
+
sys.exit(value)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def visit_MakeNode(self, node):
|
|
304
|
+
var = node.var.strip()
|
|
305
|
+
value = node.value
|
|
306
|
+
current_str = self.env.get(var)
|
|
307
|
+
if value == "lower":
|
|
308
|
+
self.env.set(var, current_str.lower())
|
|
309
|
+
elif value == "upper":
|
|
310
|
+
self.env.set(var, current_str.upper())
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def visit_MinusNode(self, node):
|
|
314
|
+
return -self.visit(node.value)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def visit_PlusNode(self, node):
|
|
318
|
+
return +self.visit(node.value)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def visit_NotNode(self, node):
|
|
322
|
+
value = self.visit(node.value)
|
|
323
|
+
return not value
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def visit_LengthNode(self, node):
|
|
327
|
+
var = self.visit(node.var)
|
|
328
|
+
return len(var)
|
|
329
|
+
|
|
330
|
+
def visit_TimeNode(self, node):
|
|
331
|
+
_ = self
|
|
332
|
+
import time
|
|
333
|
+
value = node.value
|
|
334
|
+
if value == "hour":
|
|
335
|
+
return time.strftime("%H")
|
|
336
|
+
elif value == "minute":
|
|
337
|
+
return time.strftime("%M")
|
|
338
|
+
elif value == "second":
|
|
339
|
+
return time.strftime("%S")
|
|
340
|
+
else:
|
|
341
|
+
raise NameError(f"Don't know {value}")
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def visit_BreakNode(self, node):
|
|
345
|
+
raise BreakException
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def visit_ContinueNode(self, node):
|
|
349
|
+
raise ContinueException
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def visit_FunctionDefNode(self, node):
|
|
353
|
+
self.env.set(node.name, node)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def visit_ReturnNode(self, node):
|
|
357
|
+
value = None
|
|
358
|
+
if node.value is not None:
|
|
359
|
+
value = self.visit(node.value)
|
|
360
|
+
raise ReturnException(value)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def visit_FunctionCallNode(self, node):
|
|
364
|
+
func_node = self.env.get(node.name)
|
|
365
|
+
if not isinstance(func_node, FunctionDefNode):
|
|
366
|
+
raise TypeError(f"'{node.name}' is not a function")
|
|
367
|
+
if len(node.args) != len(func_node.para):
|
|
368
|
+
raise TypeError(f"Function '{node.name}' expects {len(func_node.para)} arguments, but got {len(node.args)}")
|
|
369
|
+
arg_values = [self.visit(arg) for arg in node.args]
|
|
370
|
+
previous_env = self.env
|
|
371
|
+
local_env = Environment(parent=previous_env)
|
|
372
|
+
for para_name, val in zip (func_node.para, arg_values):
|
|
373
|
+
local_env.set(para_name, val)
|
|
374
|
+
self.env = local_env
|
|
375
|
+
return_value = None
|
|
376
|
+
try:
|
|
377
|
+
for stmt in func_node.body:
|
|
378
|
+
self.visit(stmt)
|
|
379
|
+
except ReturnException as ret:
|
|
380
|
+
return_value = ret.value
|
|
381
|
+
finally:
|
|
382
|
+
self.env = previous_env
|
|
383
|
+
|
|
384
|
+
return return_value
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def visit_StartTimerNode(self, node):
|
|
388
|
+
_ = node
|
|
389
|
+
import time
|
|
390
|
+
self.timer = time.time()
|
|
391
|
+
self.timerset = False
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def visit_StopTimerNode(self, node):
|
|
395
|
+
_ = node
|
|
396
|
+
import time
|
|
397
|
+
now = time.time()
|
|
398
|
+
self.timer2 = round(now - self.timer)
|
|
399
|
+
self.timerset = True
|
|
400
|
+
|
|
401
|
+
def visit_TimerNode(self, node):
|
|
402
|
+
_ = node
|
|
403
|
+
if not self.timerset:
|
|
404
|
+
import time
|
|
405
|
+
now = time.time()
|
|
406
|
+
self.timer2 = round(now - self.timer)
|
|
407
|
+
return self.timer2
|
|
408
|
+
else:
|
|
409
|
+
return self.timer2
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def visit_IndexNode(self, node):
|
|
414
|
+
target = self.visit(node.target)
|
|
415
|
+
pos = self.visit(node.pos)
|
|
416
|
+
try:
|
|
417
|
+
return target[pos - 1]
|
|
418
|
+
except Exception:
|
|
419
|
+
raise TypeError(f"{target} is not a list")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def visit_RandomNode(self, node):
|
|
423
|
+
value1 = self.visit(node.value1)
|
|
424
|
+
value2 = self.visit(node.value2)
|
|
425
|
+
import random
|
|
426
|
+
return random.randrange(value1, value2)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def visit_ForNode(self, node):
|
|
431
|
+
if hasattr(node.value, "name"):
|
|
432
|
+
var_name = node.value.name
|
|
433
|
+
else:
|
|
434
|
+
raise TypeError("???")
|
|
435
|
+
list_value = self.visit(node.list_value)
|
|
436
|
+
had_var = var_name in self.env.vars
|
|
437
|
+
old_value = self.env.vars.get(var_name)
|
|
438
|
+
try:
|
|
439
|
+
for item in list(list_value):
|
|
440
|
+
self.env.set(var_name, item)
|
|
441
|
+
try:
|
|
442
|
+
for stmt in node.value2:
|
|
443
|
+
try:
|
|
444
|
+
self.visit(stmt)
|
|
445
|
+
except ContinueException:
|
|
446
|
+
break
|
|
447
|
+
except BreakException:
|
|
448
|
+
break
|
|
449
|
+
finally:
|
|
450
|
+
if had_var:
|
|
451
|
+
self.env.vars[var_name] = old_value
|
|
452
|
+
else:
|
|
453
|
+
self.env.vars.pop(var_name, None)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def visit_VarPlusNode(self, node):
|
|
458
|
+
value = node.value
|
|
459
|
+
var = node.var.strip()
|
|
460
|
+
old_value = self.env.vars[var]
|
|
461
|
+
value = old_value + int(value)
|
|
462
|
+
self.env.vars[var] = value
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def visit_VarMinusNode(self, node):
|
|
468
|
+
value = node.value
|
|
469
|
+
var = node.var.strip()
|
|
470
|
+
old_value = self.env.vars[var]
|
|
471
|
+
value = old_value - int(value)
|
|
472
|
+
self.env.vars[var] = value
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def visit_NumberForNode(self, node):
|
|
477
|
+
if hasattr(node.var, "name"):
|
|
478
|
+
var_name = node.var.name
|
|
479
|
+
else:
|
|
480
|
+
raise TypeError("???")
|
|
481
|
+
start_value = self.visit(node.value)
|
|
482
|
+
end_value = self.visit(node.value2)
|
|
483
|
+
had_var = var_name in self.env.vars
|
|
484
|
+
old_value = self.env.vars.get(var_name)
|
|
485
|
+
try:
|
|
486
|
+
for item in range(start_value, end_value):
|
|
487
|
+
self.env.set(var_name, item)
|
|
488
|
+
|
|
489
|
+
try:
|
|
490
|
+
for stmt in node.block:
|
|
491
|
+
try:
|
|
492
|
+
self.visit(stmt)
|
|
493
|
+
except ContinueException:
|
|
494
|
+
break
|
|
495
|
+
except BreakException:
|
|
496
|
+
break
|
|
497
|
+
finally:
|
|
498
|
+
if had_var:
|
|
499
|
+
self.env.vars[var_name] = old_value
|
|
500
|
+
else:
|
|
501
|
+
self.env.vars.pop(var_name, None)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def interpret(self, nodes):
|
|
514
|
+
for node in nodes:
|
|
515
|
+
self.visit(node)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
token_pattern = [
|
|
4
|
+
("HASH", r"#"),
|
|
5
|
+
("INT", r"\d+"),
|
|
6
|
+
("BOOL", r"\b(True|False)\b"),
|
|
7
|
+
("DASHS", r"\/\/"),
|
|
8
|
+
("OP", r">=|<=|==|!=|%|[+\-=*/><]"),
|
|
9
|
+
("STRING", r'"[^"\\]*(?:\\.[^"\\]*)*"'),
|
|
10
|
+
("VAR", r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
|
11
|
+
("LBRACE", r'\{'),
|
|
12
|
+
("RBRACE", r'\}'),
|
|
13
|
+
("LSBRACE", r"\["),
|
|
14
|
+
("RSBRACE", r"\]"),
|
|
15
|
+
("SKIP", r'[ \t]+'),
|
|
16
|
+
("NEWLINE", r'\n'),
|
|
17
|
+
("COMMA", r"\,"),
|
|
18
|
+
("DOT", r"\."),
|
|
19
|
+
("LPAREN", r"\("),
|
|
20
|
+
("RPAREN", r"\)")
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
def tokenizer(text, definition):
|
|
24
|
+
patter = []
|
|
25
|
+
for name, regex in definition:
|
|
26
|
+
patter.append(f"(?P<{name}>{regex})")
|
|
27
|
+
|
|
28
|
+
full_regex = "|".join(patter)
|
|
29
|
+
|
|
30
|
+
tokens = []
|
|
31
|
+
|
|
32
|
+
for match in re.finditer(full_regex, text):
|
|
33
|
+
kind = match.lastgroup
|
|
34
|
+
value = match.group()
|
|
35
|
+
|
|
36
|
+
tokens.append((kind, value))
|
|
37
|
+
|
|
38
|
+
return tokens
|
|
39
|
+
|
|
40
|
+
def tokenizer_tokens(text):
|
|
41
|
+
tokens = tokenizer(text, token_pattern)
|
|
42
|
+
return tokens
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from Hexus.interpreter import HexusInterpreter
|
|
2
|
+
from Hexus.parser import HexusParser
|
|
3
|
+
from Hexus.lexer import tokenizer_tokens
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
file_name = "test.he"
|
|
7
|
+
try:
|
|
8
|
+
with open(file_name, "r", encoding="utf-8") as file:
|
|
9
|
+
source_code = file.read()
|
|
10
|
+
|
|
11
|
+
except FileNotFoundError:
|
|
12
|
+
print(f"File {file_name} not found!!!")
|
|
13
|
+
return
|
|
14
|
+
token_list = tokenizer_tokens(source_code)
|
|
15
|
+
|
|
16
|
+
clear_tokens = [t for t in token_list if t[0] != "SKIP"]
|
|
17
|
+
try:
|
|
18
|
+
for token in clear_tokens:
|
|
19
|
+
print(token)
|
|
20
|
+
parser = HexusParser(clear_tokens)
|
|
21
|
+
|
|
22
|
+
program_tree = parser.parse()
|
|
23
|
+
|
|
24
|
+
print(program_tree)
|
|
25
|
+
|
|
26
|
+
interpreter = HexusInterpreter()
|
|
27
|
+
|
|
28
|
+
interpreter.interpret(program_tree)
|
|
29
|
+
|
|
30
|
+
except SyntaxError as e:
|
|
31
|
+
print(e)
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
File without changes
|