solpl 2.1.0__tar.gz → 2.2.0__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.
- {solpl-2.1.0/solpl.egg-info → solpl-2.2.0}/PKG-INFO +1 -1
- solpl-2.2.0/interpreter/sol.py +462 -0
- {solpl-2.1.0 → solpl-2.2.0}/pyproject.toml +3 -3
- {solpl-2.1.0 → solpl-2.2.0/solpl.egg-info}/PKG-INFO +1 -1
- {solpl-2.1.0 → solpl-2.2.0}/solpl.egg-info/SOURCES.txt +1 -0
- solpl-2.2.0/solpl.egg-info/entry_points.txt +2 -0
- solpl-2.2.0/solpl.egg-info/top_level.txt +1 -0
- solpl-2.1.0/solpl.egg-info/entry_points.txt +0 -2
- solpl-2.1.0/solpl.egg-info/top_level.txt +0 -1
- {solpl-2.1.0 → solpl-2.2.0}/LICENSE +0 -0
- {solpl-2.1.0 → solpl-2.2.0}/README.md +0 -0
- {solpl-2.1.0 → solpl-2.2.0}/setup.cfg +0 -0
- {solpl-2.1.0 → solpl-2.2.0}/solpl.egg-info/dependency_links.txt +0 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import urllib.request
|
|
7
|
+
import http.server
|
|
8
|
+
|
|
9
|
+
class ReturnSignal(Exception):
|
|
10
|
+
def __init__(self, value):
|
|
11
|
+
self.value = value
|
|
12
|
+
|
|
13
|
+
class SolInterpreter:
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.variables = {}
|
|
16
|
+
self.thread_local = threading.local()
|
|
17
|
+
self.functions = {}
|
|
18
|
+
self.struct_blueprints = {}
|
|
19
|
+
self.lines = []
|
|
20
|
+
self.threads = []
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def scope_stack(self):
|
|
24
|
+
if not hasattr(self.thread_local, "stack"):
|
|
25
|
+
self.thread_local.stack = [self.variables]
|
|
26
|
+
return self.thread_local.stack
|
|
27
|
+
|
|
28
|
+
def _compute(self, expr):
|
|
29
|
+
expr = str(expr).strip()
|
|
30
|
+
|
|
31
|
+
if "+" in expr and any(c.isalpha() or c in ['"', "'"] for c in expr):
|
|
32
|
+
parts = expr.split("+")
|
|
33
|
+
result = ""
|
|
34
|
+
for p in parts:
|
|
35
|
+
val = str(self._resolve(p.strip()))
|
|
36
|
+
result += val
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
ops = ['+', '-', '*', ':']
|
|
40
|
+
for op in ops:
|
|
41
|
+
if op in expr:
|
|
42
|
+
try:
|
|
43
|
+
left, right = expr.split(op, 1)
|
|
44
|
+
val1 = float(self._resolve(left))
|
|
45
|
+
val2 = float(self._resolve(right))
|
|
46
|
+
if op == '+': return val1 + val2
|
|
47
|
+
if op == '-': return val1 - val2
|
|
48
|
+
if op == '*': return val1 * val2
|
|
49
|
+
if op == ':': return val1 / val2
|
|
50
|
+
except ValueError:
|
|
51
|
+
continue
|
|
52
|
+
return self._resolve(expr)
|
|
53
|
+
|
|
54
|
+
def _lookup(self, name):
|
|
55
|
+
for scope in reversed(self.scope_stack):
|
|
56
|
+
if name in scope:
|
|
57
|
+
return scope[name]
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
def _assign(self, name, val):
|
|
61
|
+
self.scope_stack[-1][name] = val
|
|
62
|
+
|
|
63
|
+
def _resolve(self, val):
|
|
64
|
+
val = str(val).strip()
|
|
65
|
+
|
|
66
|
+
if val.startswith("_fetch(") and val.endswith(")"):
|
|
67
|
+
url_expr = val[7:-1].strip()
|
|
68
|
+
url = str(self._compute(url_expr))
|
|
69
|
+
return self._native_fetch(url)
|
|
70
|
+
|
|
71
|
+
if "(" in val and val.endswith(")") and not val.startswith("_"):
|
|
72
|
+
parts = val.split("(", 1)
|
|
73
|
+
name = parts[0].strip()
|
|
74
|
+
if name in self.functions:
|
|
75
|
+
args_str = parts[1][:-1].strip()
|
|
76
|
+
args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
|
|
77
|
+
return self._call_function(name, args)
|
|
78
|
+
|
|
79
|
+
if "[" in val and "]" in val:
|
|
80
|
+
try:
|
|
81
|
+
name, idx_part = val.split("[")
|
|
82
|
+
idx = int(idx_part.replace("]", "").strip())
|
|
83
|
+
container = self._lookup(name)
|
|
84
|
+
if isinstance(container, list):
|
|
85
|
+
return container[idx]
|
|
86
|
+
except (ValueError, IndexError, KeyError): pass
|
|
87
|
+
if "." in val:
|
|
88
|
+
obj_name, prop = val.split(".", 1)
|
|
89
|
+
obj = self._lookup(obj_name)
|
|
90
|
+
if isinstance(obj, dict):
|
|
91
|
+
return obj.get(prop, 0)
|
|
92
|
+
|
|
93
|
+
existing_val = self._lookup(val)
|
|
94
|
+
if existing_val is not None: return existing_val
|
|
95
|
+
try: return float(val) if '.' in val else int(val)
|
|
96
|
+
except: return val.replace('"', '').replace("'", "")
|
|
97
|
+
|
|
98
|
+
def _async_worker(self, expr, action):
|
|
99
|
+
result = self._compute(expr)
|
|
100
|
+
if action == "_out":
|
|
101
|
+
print(f"[ASYNC OUTPUT] {result}")
|
|
102
|
+
|
|
103
|
+
def _async_input_worker(self, prompt, var_name):
|
|
104
|
+
user_val = input(prompt)
|
|
105
|
+
try:
|
|
106
|
+
if '.' in user_val: user_val = float(user_val)
|
|
107
|
+
else: user_val = int(user_val)
|
|
108
|
+
except ValueError: pass
|
|
109
|
+
self._assign(var_name, user_val)
|
|
110
|
+
|
|
111
|
+
def _native_fetch(self, url):
|
|
112
|
+
try:
|
|
113
|
+
with urllib.request.urlopen(url, timeout=5) as response:
|
|
114
|
+
# Fix: Added errors='replace' to handle non-UTF-8 bytes safely
|
|
115
|
+
return response.read().decode('utf-8', errors='replace')
|
|
116
|
+
except Exception as e:
|
|
117
|
+
return f"Error fetching URL: {str(e)}"
|
|
118
|
+
|
|
119
|
+
def _native_listen(self, port, handler_name):
|
|
120
|
+
interpreter_instance = self
|
|
121
|
+
class SolHTTPHandler(http.server.BaseHTTPRequestHandler):
|
|
122
|
+
def do_GET(self):
|
|
123
|
+
self.send_response(200)
|
|
124
|
+
self.send_header("Content-type", "text/html")
|
|
125
|
+
self.end_headers()
|
|
126
|
+
response_body = interpreter_instance._call_function(handler_name, [self.path])
|
|
127
|
+
self.wfile.write(bytes(str(response_body), "utf-8"))
|
|
128
|
+
def log_message(self, format, *args):
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
def server_thread():
|
|
132
|
+
server = http.server.HTTPServer(("0.0.0.0", int(port)), SolHTTPHandler)
|
|
133
|
+
server.serve_forever()
|
|
134
|
+
|
|
135
|
+
t = threading.Thread(target=server_thread, daemon=True)
|
|
136
|
+
t.start()
|
|
137
|
+
self.threads.append(t)
|
|
138
|
+
|
|
139
|
+
def _evaluate_condition(self, cond):
|
|
140
|
+
if "=" in cond:
|
|
141
|
+
left, right = cond.split("=")
|
|
142
|
+
return self._compute(left.strip()) == self._compute(right.strip())
|
|
143
|
+
elif "<" in cond:
|
|
144
|
+
left, right = cond.split("<")
|
|
145
|
+
return self._compute(left.strip()) < self._compute(right.strip())
|
|
146
|
+
elif ">" in cond:
|
|
147
|
+
left, right = cond.split(">")
|
|
148
|
+
return self._compute(left.strip()) > self._compute(right.strip())
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
def _get_block_end(self, start_pc):
|
|
152
|
+
depth = 1
|
|
153
|
+
pc = start_pc + 1
|
|
154
|
+
while pc < len(self.lines):
|
|
155
|
+
line = self.lines[pc]
|
|
156
|
+
if line.startswith("if ") or line.startswith("while ") or line.startswith("for "):
|
|
157
|
+
depth += 1
|
|
158
|
+
elif line == "end":
|
|
159
|
+
depth -= 1
|
|
160
|
+
if depth == 0:
|
|
161
|
+
return pc
|
|
162
|
+
pc += 1
|
|
163
|
+
return pc
|
|
164
|
+
|
|
165
|
+
def execute(self, code):
|
|
166
|
+
self.lines = []
|
|
167
|
+
for raw_line in code.split('\n'):
|
|
168
|
+
if ';' in raw_line:
|
|
169
|
+
raw_line = raw_line.split(';', 1)[0]
|
|
170
|
+
trimmed = raw_line.strip()
|
|
171
|
+
if trimmed:
|
|
172
|
+
self.lines.append(trimmed)
|
|
173
|
+
|
|
174
|
+
self.threads = []
|
|
175
|
+
|
|
176
|
+
pc = 0
|
|
177
|
+
while pc < len(self.lines):
|
|
178
|
+
line = self.lines[pc]
|
|
179
|
+
if "(" in line and ")" in line and "main" not in line and "end" not in line and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("if ") and not line.startswith("while ") and not line.startswith("for "):
|
|
180
|
+
parts = line.split("(")
|
|
181
|
+
name = parts[0].strip()
|
|
182
|
+
params_str = parts[1].replace(")", "").strip()
|
|
183
|
+
params = [p.strip() for p in params_str.split(",")] if params_str else []
|
|
184
|
+
self.functions[name] = {"start_pc": pc + 1, "params": params}
|
|
185
|
+
elif "{" in line:
|
|
186
|
+
pc = self._parse_struct(pc)
|
|
187
|
+
continue
|
|
188
|
+
pc += 1
|
|
189
|
+
|
|
190
|
+
for i, line in enumerate(self.lines):
|
|
191
|
+
if line == "main()":
|
|
192
|
+
try:
|
|
193
|
+
self._run_block(i + 1)
|
|
194
|
+
except ReturnSignal:
|
|
195
|
+
pass
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
for t in self.threads:
|
|
199
|
+
if t.is_alive() and not t.daemon:
|
|
200
|
+
t.join()
|
|
201
|
+
|
|
202
|
+
def _parse_struct(self, pc):
|
|
203
|
+
line = self.lines[pc]
|
|
204
|
+
struct_name = line.split("{}")[0].strip()
|
|
205
|
+
fields = {}
|
|
206
|
+
pc += 1
|
|
207
|
+
while pc < len(self.lines) and self.lines[pc] != "end":
|
|
208
|
+
if "->" in self.lines[pc]:
|
|
209
|
+
key, val = [x.strip() for x in self.lines[pc].split("->")]
|
|
210
|
+
fields[key] = self._resolve(val)
|
|
211
|
+
pc += 1
|
|
212
|
+
self.struct_blueprints[struct_name] = fields
|
|
213
|
+
self._assign(struct_name, fields.copy())
|
|
214
|
+
return pc
|
|
215
|
+
|
|
216
|
+
def _call_function(self, name, args):
|
|
217
|
+
func_info = self.functions[name]
|
|
218
|
+
start_pc = func_info["start_pc"]
|
|
219
|
+
params = func_info["params"]
|
|
220
|
+
|
|
221
|
+
local_scope = {}
|
|
222
|
+
for param, arg in zip(params, args):
|
|
223
|
+
local_scope[param] = arg
|
|
224
|
+
|
|
225
|
+
self.scope_stack.append(local_scope)
|
|
226
|
+
|
|
227
|
+
return_value = 0
|
|
228
|
+
try:
|
|
229
|
+
self._run_block(start_pc)
|
|
230
|
+
except ReturnSignal as sig:
|
|
231
|
+
return_value = sig.value
|
|
232
|
+
finally:
|
|
233
|
+
self.scope_stack.pop()
|
|
234
|
+
|
|
235
|
+
return return_value
|
|
236
|
+
|
|
237
|
+
def _execute_line(self, line):
|
|
238
|
+
if ">>" in line and "_input" in line:
|
|
239
|
+
parts = [p.strip() for p in line.split(">>")]
|
|
240
|
+
if len(parts) == 3 and parts[1] == "_input":
|
|
241
|
+
prompt_string = str(self._compute(parts[0]))
|
|
242
|
+
target_variable = parts[2]
|
|
243
|
+
|
|
244
|
+
t = threading.Thread(target=self._async_input_worker, args=(prompt_string, target_variable))
|
|
245
|
+
t.start()
|
|
246
|
+
self.threads.append(t)
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
is_async_func = False
|
|
250
|
+
if ">>" in line and "_async" in line and "(" in line and not line.startswith("_"):
|
|
251
|
+
is_async_func = True
|
|
252
|
+
line = line.split(">>")[0].strip()
|
|
253
|
+
|
|
254
|
+
if line.startswith("_return ->"):
|
|
255
|
+
val_expr = line.split("->", 1)[1].strip()
|
|
256
|
+
result = self._compute(val_expr)
|
|
257
|
+
raise ReturnSignal(result)
|
|
258
|
+
|
|
259
|
+
elif ">>" in line and "_async" in line and not is_async_func:
|
|
260
|
+
parts = line.split(">>")
|
|
261
|
+
t = threading.Thread(target=self._async_worker, args=(parts[0].strip(), parts[2].strip()))
|
|
262
|
+
t.start()
|
|
263
|
+
self.threads.append(t)
|
|
264
|
+
|
|
265
|
+
elif line.startswith("_in"):
|
|
266
|
+
if "(" in line and ")" in line:
|
|
267
|
+
parts = line.split("(")
|
|
268
|
+
prompt = parts[1].split(")")[0].strip().replace('"', '').replace("'", "")
|
|
269
|
+
var_name = line.split("->")[1].strip()
|
|
270
|
+
else:
|
|
271
|
+
var_name = line.split("->")[1].strip()
|
|
272
|
+
prompt = f"[INPUT REQUIRED FOR %s]: " % var_name
|
|
273
|
+
|
|
274
|
+
user_val = input(prompt + " ")
|
|
275
|
+
try:
|
|
276
|
+
if '.' in user_val: user_val = float(user_val)
|
|
277
|
+
else: user_val = int(user_val)
|
|
278
|
+
except ValueError: pass
|
|
279
|
+
self._assign(var_name, user_val)
|
|
280
|
+
|
|
281
|
+
elif "_add(" in line:
|
|
282
|
+
content = line.replace("_add(", "").replace(")", "")
|
|
283
|
+
parts = [p.strip() for p in content.split(",")]
|
|
284
|
+
container = self._lookup(parts[0])
|
|
285
|
+
if isinstance(container, list):
|
|
286
|
+
container.insert(int(parts[1]), self._resolve(parts[2]))
|
|
287
|
+
elif "_remove(" in line:
|
|
288
|
+
content = line.replace("_remove(", "").replace(")", "")
|
|
289
|
+
parts = [p.strip() for p in content.split(",")]
|
|
290
|
+
container = self._lookup(parts[0])
|
|
291
|
+
if isinstance(container, list):
|
|
292
|
+
container.pop(int(parts[1]))
|
|
293
|
+
elif "_listen(" in line:
|
|
294
|
+
content = line.replace("_listen(", "").replace(")", "")
|
|
295
|
+
port_expr, handler_expr = [p.strip() for p in content.split(",")]
|
|
296
|
+
port = self._compute(port_expr)
|
|
297
|
+
self._native_listen(port, handler_expr)
|
|
298
|
+
elif "_out" in line:
|
|
299
|
+
val = line.split("->")[1].strip()
|
|
300
|
+
print(self._compute(val))
|
|
301
|
+
|
|
302
|
+
elif "mylist<" in line and ">" in line:
|
|
303
|
+
var_name = line.split("<")[1].split(">")[0]
|
|
304
|
+
self._assign(var_name, [])
|
|
305
|
+
|
|
306
|
+
elif "->" in line:
|
|
307
|
+
var, val = [x.strip() for x in line.split("->")]
|
|
308
|
+
if "." in var:
|
|
309
|
+
obj, prop = var.split(".", 1)
|
|
310
|
+
target_obj = self._lookup(obj)
|
|
311
|
+
if isinstance(target_obj, dict):
|
|
312
|
+
target_obj[prop] = self._compute(val)
|
|
313
|
+
else:
|
|
314
|
+
self._assign(var, self._compute(val))
|
|
315
|
+
else:
|
|
316
|
+
if "(" in line and line.endswith(")") and not line.startswith("_"):
|
|
317
|
+
parts = line.split("(")
|
|
318
|
+
name = parts[0].strip()
|
|
319
|
+
if name in self.functions:
|
|
320
|
+
args_str = parts[1][:-1].strip()
|
|
321
|
+
args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
|
|
322
|
+
|
|
323
|
+
if is_async_func:
|
|
324
|
+
t = threading.Thread(target=self._call_function, args=(name, args))
|
|
325
|
+
t.start()
|
|
326
|
+
self.threads.append(t)
|
|
327
|
+
else:
|
|
328
|
+
self._call_function(name, args)
|
|
329
|
+
|
|
330
|
+
def _run_block(self, start_pc):
|
|
331
|
+
pc = start_pc
|
|
332
|
+
control_stack = []
|
|
333
|
+
|
|
334
|
+
while pc < len(self.lines):
|
|
335
|
+
line = self.lines[pc]
|
|
336
|
+
is_executing = all(level["executing"] for level in control_stack)
|
|
337
|
+
|
|
338
|
+
if line.startswith("if "):
|
|
339
|
+
cond = line.split("if ", 1)[1].strip()
|
|
340
|
+
if is_executing:
|
|
341
|
+
cond_true = self._evaluate_condition(cond)
|
|
342
|
+
control_stack.append({"type": "if", "executing": cond_true, "any_branch_true": cond_true})
|
|
343
|
+
else:
|
|
344
|
+
control_stack.append({"type": "if", "executing": False, "any_branch_true": True})
|
|
345
|
+
pc += 1
|
|
346
|
+
continue
|
|
347
|
+
|
|
348
|
+
elif line.startswith("elseif "):
|
|
349
|
+
cond = line.split("elseif ", 1)[1].strip()
|
|
350
|
+
parent_active = all(level["executing"] for level in control_stack[:-1]) if len(control_stack) > 1 else True
|
|
351
|
+
|
|
352
|
+
if control_stack and control_stack[-1]["type"] == "if":
|
|
353
|
+
if parent_active and not control_stack[-1]["any_branch_true"]:
|
|
354
|
+
cond_true = self._evaluate_condition(cond)
|
|
355
|
+
control_stack[-1]["executing"] = cond_true
|
|
356
|
+
control_stack[-1]["any_branch_true"] = cond_true
|
|
357
|
+
else:
|
|
358
|
+
control_stack[-1]["executing"] = False
|
|
359
|
+
pc += 1
|
|
360
|
+
continue
|
|
361
|
+
|
|
362
|
+
elif line == "else":
|
|
363
|
+
parent_active = all(level["executing"] for level in control_stack[:-1]) if len(control_stack) > 1 else True
|
|
364
|
+
if control_stack and control_stack[-1]["type"] == "if":
|
|
365
|
+
if parent_active and not control_stack[-1]["any_branch_true"]:
|
|
366
|
+
control_stack[-1]["executing"] = True
|
|
367
|
+
control_stack[-1]["any_branch_true"] = True
|
|
368
|
+
else:
|
|
369
|
+
control_stack[-1]["executing"] = False
|
|
370
|
+
pc += 1
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
elif line.startswith("while "):
|
|
374
|
+
cond = line.split("while ", 1)[1].strip()
|
|
375
|
+
if is_executing:
|
|
376
|
+
end_pc = self._get_block_end(pc)
|
|
377
|
+
if self._evaluate_condition(cond):
|
|
378
|
+
control_stack.append({"type": "while", "executing": True, "start_pc": pc, "end_pc": end_pc})
|
|
379
|
+
pc += 1
|
|
380
|
+
else:
|
|
381
|
+
pc = end_pc + 1
|
|
382
|
+
else:
|
|
383
|
+
control_stack.append({"type": "while", "executing": False})
|
|
384
|
+
pc += 1
|
|
385
|
+
continue
|
|
386
|
+
|
|
387
|
+
elif line.startswith("for "):
|
|
388
|
+
if is_executing:
|
|
389
|
+
parts = line.split("for ", 1)[1].split("->")
|
|
390
|
+
var_name = parts[0].strip()
|
|
391
|
+
range_parts = parts[1].split(" to ")
|
|
392
|
+
start_val = int(self._compute(range_parts[0].strip()))
|
|
393
|
+
end_val = int(self._compute(range_parts[1].strip()))
|
|
394
|
+
|
|
395
|
+
self._assign(var_name, start_val)
|
|
396
|
+
end_pc = self._get_block_end(pc)
|
|
397
|
+
|
|
398
|
+
if start_val <= end_val:
|
|
399
|
+
control_stack.append({"type": "for", "executing": True, "var": var_name, "end_val": end_val, "start_pc": pc, "end_pc": end_pc})
|
|
400
|
+
pc += 1
|
|
401
|
+
else:
|
|
402
|
+
pc = end_pc + 1
|
|
403
|
+
else:
|
|
404
|
+
control_stack.append({"type": "for", "executing": False})
|
|
405
|
+
pc += 1
|
|
406
|
+
continue
|
|
407
|
+
|
|
408
|
+
elif line == "end":
|
|
409
|
+
if control_stack:
|
|
410
|
+
top = control_stack[-1]
|
|
411
|
+
if top.get("type") == "while" and top["executing"]:
|
|
412
|
+
pc = top["start_pc"]
|
|
413
|
+
control_stack.pop()
|
|
414
|
+
continue
|
|
415
|
+
elif top.get("type") == "for" and top["executing"]:
|
|
416
|
+
var_name = top["var"]
|
|
417
|
+
next_val = self._lookup(var_name) + 1
|
|
418
|
+
if next_val <= top["end_val"]:
|
|
419
|
+
self._assign(var_name, next_val)
|
|
420
|
+
pc = top["start_pc"] + 1
|
|
421
|
+
continue
|
|
422
|
+
else:
|
|
423
|
+
control_stack.pop()
|
|
424
|
+
pc += 1
|
|
425
|
+
continue
|
|
426
|
+
else:
|
|
427
|
+
control_stack.pop()
|
|
428
|
+
pc += 1
|
|
429
|
+
continue
|
|
430
|
+
else:
|
|
431
|
+
break
|
|
432
|
+
|
|
433
|
+
if is_executing:
|
|
434
|
+
if "?" in line:
|
|
435
|
+
cond, action = line.split("?", 1)
|
|
436
|
+
if self._evaluate_condition(cond.strip()):
|
|
437
|
+
self._execute_line(action.strip())
|
|
438
|
+
else:
|
|
439
|
+
self._execute_line(line)
|
|
440
|
+
pc += 1
|
|
441
|
+
|
|
442
|
+
def main():
|
|
443
|
+
if len(sys.argv) < 2:
|
|
444
|
+
print("Error: No script file provided.\nUsage: solpl <filename.sol>", file=sys.stderr)
|
|
445
|
+
sys.exit(1)
|
|
446
|
+
|
|
447
|
+
filename = sys.argv[1]
|
|
448
|
+
if not filename.endswith(".sol"):
|
|
449
|
+
print("Error: File must have a '.sol' extension.", file=sys.stderr)
|
|
450
|
+
sys.exit(1)
|
|
451
|
+
if not os.path.exists(filename):
|
|
452
|
+
print(f"Error: File '{filename}' not found.", file=sys.stderr)
|
|
453
|
+
sys.exit(1)
|
|
454
|
+
|
|
455
|
+
with open(filename, "r") as f:
|
|
456
|
+
code_content = f.read()
|
|
457
|
+
|
|
458
|
+
engine = SolInterpreter()
|
|
459
|
+
engine.execute(code_content)
|
|
460
|
+
|
|
461
|
+
if __name__ == "__main__":
|
|
462
|
+
main()
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "solpl"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "Sol Programming Language Interpreter"
|
|
9
9
|
requires-python = ">=3.7"
|
|
10
10
|
|
|
11
11
|
[project.scripts]
|
|
12
|
-
solpl = "sol:main"
|
|
12
|
+
solpl = "interpreter.sol:main"
|
|
13
13
|
|
|
14
14
|
[tool.setuptools]
|
|
15
|
-
|
|
15
|
+
packages = ["interpreter"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
interpreter
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
sol
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|