solpl 2.7.0__tar.gz → 4.0.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.7.0/solpl.egg-info → solpl-4.0.0}/PKG-INFO +1 -1
- {solpl-2.7.0 → solpl-4.0.0}/interpreter/sol.py +111 -55
- {solpl-2.7.0 → solpl-4.0.0}/pyproject.toml +1 -1
- {solpl-2.7.0 → solpl-4.0.0/solpl.egg-info}/PKG-INFO +1 -1
- {solpl-2.7.0 → solpl-4.0.0}/LICENSE +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/README.md +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/setup.cfg +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/solpl.egg-info/SOURCES.txt +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/solpl.egg-info/dependency_links.txt +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/solpl.egg-info/entry_points.txt +0 -0
- {solpl-2.7.0 → solpl-4.0.0}/solpl.egg-info/top_level.txt +0 -0
|
@@ -41,7 +41,6 @@ class SolInterpreter:
|
|
|
41
41
|
return False
|
|
42
42
|
|
|
43
43
|
def _split_on_op(self, string, op):
|
|
44
|
-
"""Split on first occurrence of op outside quotes. Returns (left, right) or (None, None)."""
|
|
45
44
|
in_quote = False
|
|
46
45
|
quote_char = None
|
|
47
46
|
for i, char in enumerate(string):
|
|
@@ -96,7 +95,6 @@ class SolInterpreter:
|
|
|
96
95
|
|
|
97
96
|
def _compute(self, expr):
|
|
98
97
|
expr = str(expr).strip()
|
|
99
|
-
|
|
100
98
|
# Handle parentheses
|
|
101
99
|
idx = 0
|
|
102
100
|
while True:
|
|
@@ -106,7 +104,6 @@ class SolInterpreter:
|
|
|
106
104
|
start_idx = expr.rfind('(', 0, end_idx)
|
|
107
105
|
if start_idx == -1:
|
|
108
106
|
break
|
|
109
|
-
# Skip function calls
|
|
110
107
|
if start_idx > 0 and (expr[start_idx - 1].isalnum() or expr[start_idx - 1] == '_'):
|
|
111
108
|
idx = end_idx + 1
|
|
112
109
|
continue
|
|
@@ -114,8 +111,7 @@ class SolInterpreter:
|
|
|
114
111
|
inner_result = self._compute(inner_expr)
|
|
115
112
|
expr = expr[:start_idx] + str(inner_result) + expr[end_idx + 1:]
|
|
116
113
|
idx = 0
|
|
117
|
-
|
|
118
|
-
# Handle string concatenation with +
|
|
114
|
+
# String concatenation with +
|
|
119
115
|
plus_parts = self._split_outside_quotes(expr, '+')
|
|
120
116
|
if len(plus_parts) > 1:
|
|
121
117
|
is_string_concat = False
|
|
@@ -130,16 +126,14 @@ class SolInterpreter:
|
|
|
130
126
|
val = self._resolve(p.strip())
|
|
131
127
|
result += str(val) if val is not None else ""
|
|
132
128
|
return result
|
|
133
|
-
|
|
134
|
-
# Check for equality (=) - comparison operator
|
|
129
|
+
# Equality comparison (=)
|
|
135
130
|
left, right = self._split_on_op(expr, '=')
|
|
136
131
|
if left is not None:
|
|
137
132
|
r1 = self._resolve(left.strip())
|
|
138
133
|
r2 = self._resolve(right.strip())
|
|
139
134
|
if r1 is not None and r2 is not None:
|
|
140
135
|
return 1 if r1 == r2 else 0
|
|
141
|
-
|
|
142
|
-
# Level 1: + and -
|
|
136
|
+
# + and -
|
|
143
137
|
for op in ['+', '-']:
|
|
144
138
|
idx = self._find_last_op(expr, op)
|
|
145
139
|
if idx != -1:
|
|
@@ -153,8 +147,7 @@ class SolInterpreter:
|
|
|
153
147
|
val2 = float(r2)
|
|
154
148
|
if op == '+': return val1 + val2
|
|
155
149
|
if op == '-': return val1 - val2
|
|
156
|
-
|
|
157
|
-
# Level 2: * and :
|
|
150
|
+
# * and :
|
|
158
151
|
last_idx = -1
|
|
159
152
|
last_op = None
|
|
160
153
|
for op in ['*', ':']:
|
|
@@ -174,7 +167,6 @@ class SolInterpreter:
|
|
|
174
167
|
val2 = float(r2)
|
|
175
168
|
if last_op == '*': return val1 * val2
|
|
176
169
|
if last_op == ':': return val1 / val2
|
|
177
|
-
|
|
178
170
|
return self._resolve(expr)
|
|
179
171
|
|
|
180
172
|
def _lookup(self, name):
|
|
@@ -207,6 +199,11 @@ class SolInterpreter:
|
|
|
207
199
|
args_str = parts[1][:-1].strip()
|
|
208
200
|
args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
|
|
209
201
|
return self._call_function(name, args)
|
|
202
|
+
for func_name in self.functions:
|
|
203
|
+
if func_name.endswith("." + name):
|
|
204
|
+
args_str = parts[1][:-1].strip()
|
|
205
|
+
args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
|
|
206
|
+
return self._call_function(func_name, args)
|
|
210
207
|
if "[" in val and "]" in val:
|
|
211
208
|
try:
|
|
212
209
|
name, idx_part = val.split("[", 1)
|
|
@@ -215,16 +212,23 @@ class SolInterpreter:
|
|
|
215
212
|
if isinstance(container, list): return container[idx]
|
|
216
213
|
except (ValueError, IndexError, KeyError): pass
|
|
217
214
|
if "." in val:
|
|
218
|
-
|
|
219
|
-
obj = self._lookup(
|
|
220
|
-
|
|
221
|
-
|
|
215
|
+
parts = val.split(".")
|
|
216
|
+
obj = self._lookup(parts[0])
|
|
217
|
+
for part in parts[1:]:
|
|
218
|
+
if isinstance(obj, dict):
|
|
219
|
+
obj = obj.get(part, 0)
|
|
220
|
+
else:
|
|
221
|
+
return 0
|
|
222
|
+
return obj
|
|
222
223
|
for scope in reversed(self.scope_stack):
|
|
223
224
|
if val in scope: return scope[val]
|
|
224
|
-
|
|
225
|
+
if val in self.functions:
|
|
226
|
+
return ("func_ref", val)
|
|
227
|
+
for func_name in self.functions:
|
|
228
|
+
if func_name.endswith("." + val):
|
|
229
|
+
return ("func_ref", func_name)
|
|
225
230
|
if val.replace('.', '', 1).isdigit() or (val.startswith('-') and val[1:].replace('.', '', 1).isdigit()):
|
|
226
231
|
return float(val) if '.' in val else int(val)
|
|
227
|
-
|
|
228
232
|
return None
|
|
229
233
|
|
|
230
234
|
def _native_fetch(self, url):
|
|
@@ -253,15 +257,12 @@ class SolInterpreter:
|
|
|
253
257
|
self.threads.append(t)
|
|
254
258
|
|
|
255
259
|
def _evaluate_condition(self, cond):
|
|
256
|
-
# Equality comparison uses =
|
|
257
260
|
left, right = self._split_on_op(cond, "=")
|
|
258
261
|
if left is not None:
|
|
259
|
-
return self._compute(cond)
|
|
260
|
-
# Less than
|
|
262
|
+
return self._compute(cond)
|
|
261
263
|
left, right = self._split_on_op(cond, "<")
|
|
262
264
|
if left is not None:
|
|
263
265
|
return self._compute(left.strip()) < self._compute(right.strip())
|
|
264
|
-
# Greater than
|
|
265
266
|
left, right = self._split_on_op(cond, ">")
|
|
266
267
|
if left is not None:
|
|
267
268
|
return self._compute(left.strip()) > self._compute(right.strip())
|
|
@@ -279,33 +280,78 @@ class SolInterpreter:
|
|
|
279
280
|
pc += 1
|
|
280
281
|
return pc
|
|
281
282
|
|
|
283
|
+
def _import_file(self, filename):
|
|
284
|
+
if not os.path.exists(filename):
|
|
285
|
+
print(f"Error: Module '{filename}' not found.")
|
|
286
|
+
return
|
|
287
|
+
with open(filename, "r") as f:
|
|
288
|
+
code = f.read()
|
|
289
|
+
module_name = os.path.splitext(os.path.basename(filename))[0]
|
|
290
|
+
lines = []
|
|
291
|
+
for l in code.split('\n'):
|
|
292
|
+
l = l.strip()
|
|
293
|
+
if not l: continue
|
|
294
|
+
if l.startswith(';'): continue
|
|
295
|
+
if l.startswith('//'): continue
|
|
296
|
+
if ';' in l:
|
|
297
|
+
l = l.split(';', 1)[0].strip()
|
|
298
|
+
if l:
|
|
299
|
+
lines.append(l)
|
|
300
|
+
# Parse structs first
|
|
301
|
+
for pc, line in enumerate(lines):
|
|
302
|
+
if line.endswith("{}") and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("_"):
|
|
303
|
+
struct_name = line.split("{}")[0].strip()
|
|
304
|
+
fields = {}
|
|
305
|
+
npc = pc + 1
|
|
306
|
+
while npc < len(lines) and lines[npc] != "end":
|
|
307
|
+
if "->" in lines[npc]:
|
|
308
|
+
key, val = [x.strip() for x in lines[npc].split("->")]
|
|
309
|
+
fields[key] = self._resolve(val)
|
|
310
|
+
npc += 1
|
|
311
|
+
self.struct_blueprints[struct_name] = fields
|
|
312
|
+
self.variables[struct_name] = fields.copy()
|
|
313
|
+
# Parse functions
|
|
314
|
+
for pc, line in enumerate(lines):
|
|
315
|
+
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 ", "while ", "for ", "elseif", "else")):
|
|
316
|
+
parts = line.split("(")
|
|
317
|
+
name = parts[0].strip()
|
|
318
|
+
params_str = parts[1].replace(")", "").strip()
|
|
319
|
+
params = [p.strip() for p in params_str.split(",")] if params_str else []
|
|
320
|
+
self.functions[f"{module_name}.{name}"] = {"start_pc": pc + 1, "params": params, "source_lines": lines}
|
|
321
|
+
|
|
282
322
|
def execute(self, code):
|
|
283
323
|
raw_lines = code.split('\n')
|
|
284
324
|
self.lines = []
|
|
285
325
|
for l in raw_lines:
|
|
286
326
|
l = l.strip()
|
|
287
|
-
if not l:
|
|
288
|
-
|
|
289
|
-
if l.startswith('
|
|
290
|
-
continue
|
|
291
|
-
if l.startswith('//'):
|
|
292
|
-
continue
|
|
327
|
+
if not l: continue
|
|
328
|
+
if l.startswith(';'): continue
|
|
329
|
+
if l.startswith('//'): continue
|
|
293
330
|
if ';' in l:
|
|
294
331
|
l = l.split(';', 1)[0].strip()
|
|
295
332
|
if l:
|
|
296
333
|
self.lines.append(l)
|
|
297
|
-
|
|
298
334
|
self.threads = []
|
|
299
335
|
pc = 0
|
|
336
|
+
depth = 0
|
|
300
337
|
while pc < len(self.lines):
|
|
301
338
|
line = self.lines[pc]
|
|
302
|
-
if
|
|
339
|
+
if line == "end":
|
|
340
|
+
depth -= 1
|
|
341
|
+
pc += 1
|
|
342
|
+
continue
|
|
343
|
+
if depth == 0 and "(" 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 ", "while ", "for ", "elseif", "else")):
|
|
303
344
|
parts = line.split("(")
|
|
304
|
-
|
|
305
|
-
|
|
345
|
+
func_name = parts[0].strip()
|
|
346
|
+
if not func_name.startswith("_"):
|
|
347
|
+
self.functions[func_name] = {"start_pc": pc + 1, "params": [p.strip() for p in parts[1].replace(")", "").split(",")] if parts[1].replace(")", "").strip() else []}
|
|
348
|
+
depth += 1
|
|
349
|
+
elif depth == 0 and line.endswith("{}") and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("_"):
|
|
306
350
|
pc = self._parse_struct(pc)
|
|
307
351
|
pc += 1
|
|
308
352
|
continue
|
|
353
|
+
elif depth > 0 and line.startswith(("if ", "while ", "for ")):
|
|
354
|
+
depth += 1
|
|
309
355
|
pc += 1
|
|
310
356
|
for i, line in enumerate(self.lines):
|
|
311
357
|
if line == "main()":
|
|
@@ -325,7 +371,7 @@ class SolInterpreter:
|
|
|
325
371
|
fields[key] = self._resolve(val)
|
|
326
372
|
pc += 1
|
|
327
373
|
self.struct_blueprints[struct_name] = fields
|
|
328
|
-
self.
|
|
374
|
+
self.variables[struct_name] = fields.copy()
|
|
329
375
|
return pc
|
|
330
376
|
|
|
331
377
|
def _call_function(self, name, args):
|
|
@@ -348,13 +394,18 @@ class SolInterpreter:
|
|
|
348
394
|
return 0
|
|
349
395
|
|
|
350
396
|
def _execute_line(self, line):
|
|
351
|
-
# Handle imports
|
|
352
397
|
if line.startswith("_get("):
|
|
353
398
|
filename = line.split('"')[1]
|
|
354
399
|
self._import_file(filename)
|
|
355
400
|
return
|
|
356
|
-
|
|
357
|
-
|
|
401
|
+
if line.startswith("_call("):
|
|
402
|
+
inner = line[6:-1].strip()
|
|
403
|
+
parts = [p.strip() for p in inner.split(",")]
|
|
404
|
+
func_name = self._resolve(parts[0])
|
|
405
|
+
if isinstance(func_name, tuple) and func_name[0] == "func_ref":
|
|
406
|
+
func_name = func_name[1]
|
|
407
|
+
args = [self._compute(p) for p in parts[1:]]
|
|
408
|
+
return self._call_function(func_name, args)
|
|
358
409
|
if ">>" in line and "_in" in line:
|
|
359
410
|
parts = [p.strip() for p in line.split(">>")]
|
|
360
411
|
if len(parts) >= 3 and parts[1] == "_in":
|
|
@@ -365,19 +416,14 @@ class SolInterpreter:
|
|
|
365
416
|
try: self._assign(var_name, float(val) if '.' in val else int(val))
|
|
366
417
|
except: self._assign(var_name, val)
|
|
367
418
|
return
|
|
368
|
-
|
|
369
|
-
# Check if async
|
|
370
419
|
is_async = False
|
|
371
420
|
if ">>" in line:
|
|
372
421
|
parts = [p.strip() for p in line.split(">>")]
|
|
373
422
|
if parts[-1] == "_async":
|
|
374
423
|
line = ">>".join(line.split(">>")[:-1]).strip()
|
|
375
424
|
is_async = True
|
|
376
|
-
|
|
377
425
|
if line.startswith("_return ->"):
|
|
378
426
|
raise ReturnSignal(self._compute(line.split("->", 1)[1]))
|
|
379
|
-
|
|
380
|
-
# Handle _out with piping
|
|
381
427
|
if ">>" in line and "_out" in line:
|
|
382
428
|
parts = [p.strip() for p in line.split(">>")]
|
|
383
429
|
if len(parts) == 2 and parts[1] == "_out":
|
|
@@ -388,7 +434,6 @@ class SolInterpreter:
|
|
|
388
434
|
else:
|
|
389
435
|
print(val if val is not None else "")
|
|
390
436
|
return
|
|
391
|
-
|
|
392
437
|
if line.startswith("_out"):
|
|
393
438
|
val = self._compute(line.split("->", 1)[1].strip())
|
|
394
439
|
if is_async:
|
|
@@ -397,7 +442,6 @@ class SolInterpreter:
|
|
|
397
442
|
else:
|
|
398
443
|
print(val if val is not None else "")
|
|
399
444
|
return
|
|
400
|
-
|
|
401
445
|
if line.startswith("_in"):
|
|
402
446
|
if "->" in line:
|
|
403
447
|
var_name = line.split("->", 1)[1].strip()
|
|
@@ -406,45 +450,44 @@ class SolInterpreter:
|
|
|
406
450
|
try: self._assign(var_name, float(val) if '.' in val else int(val))
|
|
407
451
|
except: self._assign(var_name, val)
|
|
408
452
|
return
|
|
409
|
-
|
|
410
453
|
if "_add(" in line:
|
|
411
454
|
parts = [p.strip() for p in line.replace("_add(", "").replace(")", "").split(",")]
|
|
412
455
|
container = self._lookup(parts[0])
|
|
413
456
|
if isinstance(container, list): container.insert(int(parts[1]), self._resolve(parts[2]))
|
|
414
457
|
return
|
|
415
|
-
|
|
416
458
|
if "_remove(" in line:
|
|
417
459
|
parts = [p.strip() for p in line.replace("_remove(", "").replace(")", "").split(",")]
|
|
418
460
|
container = self._lookup(parts[0])
|
|
419
461
|
if isinstance(container, list): container.pop(int(parts[1]))
|
|
420
462
|
return
|
|
421
|
-
|
|
422
463
|
if "_listen(" in line:
|
|
423
464
|
p, h = [p.strip() for p in line.replace("_listen(", "").replace(")", "").split(",")]
|
|
424
465
|
self._native_listen(self._compute(p), h)
|
|
425
466
|
return
|
|
426
|
-
|
|
427
|
-
# List declaration
|
|
428
467
|
if "<" in line and ">" in line and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("_"):
|
|
429
468
|
parts = line.split("<")
|
|
430
469
|
if len(parts) == 2 and ">" in parts[1]:
|
|
431
470
|
list_name = parts[1].split(">")[0].strip()
|
|
432
471
|
self._assign(list_name, [])
|
|
433
472
|
return
|
|
434
|
-
|
|
435
|
-
# Assignment - ONLY with ->
|
|
436
473
|
if "->" in line:
|
|
437
474
|
var, val = [x.strip() for x in line.split("->", 1)]
|
|
438
475
|
if "." in var:
|
|
439
|
-
|
|
440
|
-
target = self._lookup(
|
|
441
|
-
if isinstance(target, dict):
|
|
476
|
+
parts = var.split(".")
|
|
477
|
+
target = self._lookup(parts[0])
|
|
478
|
+
if isinstance(target, dict):
|
|
479
|
+
for part in parts[1:-1]:
|
|
480
|
+
if isinstance(target, dict) and part in target:
|
|
481
|
+
target = target[part]
|
|
482
|
+
else:
|
|
483
|
+
target = None
|
|
484
|
+
break
|
|
485
|
+
if target is not None and isinstance(target, dict):
|
|
486
|
+
target[parts[-1]] = self._compute(val)
|
|
442
487
|
else:
|
|
443
488
|
computed = self._compute(val)
|
|
444
489
|
self._assign(var, computed)
|
|
445
490
|
return
|
|
446
|
-
|
|
447
|
-
# Function call
|
|
448
491
|
if "(" in line and line.endswith(")") and not line.startswith("_"):
|
|
449
492
|
parts = line.split("(")
|
|
450
493
|
func_name = parts[0].strip()
|
|
@@ -463,6 +506,19 @@ class SolInterpreter:
|
|
|
463
506
|
while pc < len(self.lines):
|
|
464
507
|
line = self.lines[pc]
|
|
465
508
|
is_executing = all(level["executing"] for level in control_stack)
|
|
509
|
+
if is_executing and "(" 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 ", "while ", "for ", "elseif", "else")):
|
|
510
|
+
func_candidate = line.split("(")[0].strip()
|
|
511
|
+
if func_candidate in self.functions:
|
|
512
|
+
depth = 1
|
|
513
|
+
npc = pc + 1
|
|
514
|
+
while npc < len(self.lines) and depth > 0:
|
|
515
|
+
if self.lines[npc].startswith(("if ", "while ", "for ")):
|
|
516
|
+
depth += 1
|
|
517
|
+
elif self.lines[npc] == "end":
|
|
518
|
+
depth -= 1
|
|
519
|
+
npc += 1
|
|
520
|
+
pc = npc
|
|
521
|
+
continue
|
|
466
522
|
if line.startswith("if "):
|
|
467
523
|
control_stack.append({"type": "if", "executing": is_executing and self._evaluate_condition(line.split("if ", 1)[1].strip()), "any_branch_true": False})
|
|
468
524
|
pc += 1; continue
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|