solpl 2.6.5__tar.gz → 2.7.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.6.5 → solpl-2.7.0}/LICENSE +1 -1
- {solpl-2.6.5/solpl.egg-info → solpl-2.7.0}/PKG-INFO +6 -1
- {solpl-2.6.5 → solpl-2.7.0}/README.md +5 -0
- {solpl-2.6.5 → solpl-2.7.0}/interpreter/sol.py +261 -136
- {solpl-2.6.5 → solpl-2.7.0}/pyproject.toml +1 -1
- {solpl-2.6.5 → solpl-2.7.0/solpl.egg-info}/PKG-INFO +6 -1
- {solpl-2.6.5 → solpl-2.7.0}/setup.cfg +0 -0
- {solpl-2.6.5 → solpl-2.7.0}/solpl.egg-info/SOURCES.txt +0 -0
- {solpl-2.6.5 → solpl-2.7.0}/solpl.egg-info/dependency_links.txt +0 -0
- {solpl-2.6.5 → solpl-2.7.0}/solpl.egg-info/entry_points.txt +0 -0
- {solpl-2.6.5 → solpl-2.7.0}/solpl.egg-info/top_level.txt +0 -0
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2026 stefand-0
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solpl
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: A lightweight, multi-threaded interpreter for the Sol programming language
|
|
5
5
|
Author-email: stefand-0 <stefand-0@users.noreply.github.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -82,6 +82,11 @@ main()
|
|
|
82
82
|
end
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
> [!IMPORTANT]
|
|
86
|
+
> Asynchronous functions cannot use the _return method to return a value. Please use _out instead.
|
|
87
|
+
|
|
88
|
+
> [!TIP]
|
|
89
|
+
> Make use of the standard libraries for extra data types, such as Booleans, HashMaps etc.
|
|
85
90
|
# License
|
|
86
91
|
|
|
87
92
|
This repository is licensed with Apache License 2.0, see the LICENSE file for more details
|
|
@@ -69,6 +69,11 @@ main()
|
|
|
69
69
|
end
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
> [!IMPORTANT]
|
|
73
|
+
> Asynchronous functions cannot use the _return method to return a value. Please use _out instead.
|
|
74
|
+
|
|
75
|
+
> [!TIP]
|
|
76
|
+
> Make use of the standard libraries for extra data types, such as Booleans, HashMaps etc.
|
|
72
77
|
# License
|
|
73
78
|
|
|
74
79
|
This repository is licensed with Apache License 2.0, see the LICENSE file for more details
|
|
@@ -25,35 +25,80 @@ class SolInterpreter:
|
|
|
25
25
|
self.thread_local.stack = [self.variables]
|
|
26
26
|
return self.thread_local.stack
|
|
27
27
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
def _has_guard(self, line):
|
|
29
|
+
in_quote = False
|
|
30
|
+
quote_char = None
|
|
31
|
+
for char in line:
|
|
32
|
+
if char in ['"', "'"]:
|
|
33
|
+
if not in_quote:
|
|
34
|
+
in_quote = True
|
|
35
|
+
quote_char = char
|
|
36
|
+
elif char == quote_char:
|
|
37
|
+
in_quote = False
|
|
38
|
+
quote_char = None
|
|
39
|
+
elif not in_quote and char == '?':
|
|
40
|
+
return True
|
|
41
|
+
return False
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
def _split_on_op(self, string, op):
|
|
44
|
+
"""Split on first occurrence of op outside quotes. Returns (left, right) or (None, None)."""
|
|
45
|
+
in_quote = False
|
|
46
|
+
quote_char = None
|
|
47
|
+
for i, char in enumerate(string):
|
|
48
|
+
if char in ['"', "'"]:
|
|
49
|
+
if not in_quote:
|
|
50
|
+
in_quote = True
|
|
51
|
+
quote_char = char
|
|
52
|
+
elif char == quote_char:
|
|
53
|
+
in_quote = False
|
|
54
|
+
quote_char = None
|
|
55
|
+
elif not in_quote and char == op:
|
|
56
|
+
return string[:i], string[i+1:]
|
|
57
|
+
return None, None
|
|
36
58
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
def _split_outside_quotes(self, string, op):
|
|
60
|
+
parts = []
|
|
61
|
+
current = []
|
|
62
|
+
in_quote = False
|
|
63
|
+
quote_char = None
|
|
64
|
+
for char in string:
|
|
65
|
+
if char in ['"', "'"]:
|
|
66
|
+
if not in_quote:
|
|
67
|
+
in_quote = True
|
|
68
|
+
quote_char = char
|
|
69
|
+
elif char == quote_char:
|
|
70
|
+
in_quote = False
|
|
71
|
+
quote_char = None
|
|
72
|
+
current.append(char)
|
|
73
|
+
elif not in_quote and char == op:
|
|
74
|
+
parts.append("".join(current))
|
|
75
|
+
current = []
|
|
76
|
+
else:
|
|
77
|
+
current.append(char)
|
|
78
|
+
parts.append("".join(current))
|
|
79
|
+
return parts
|
|
45
80
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
81
|
+
def _find_last_op(self, string, op):
|
|
82
|
+
in_quote = False
|
|
83
|
+
quote_char = None
|
|
84
|
+
last_idx = -1
|
|
85
|
+
for i, char in enumerate(string):
|
|
86
|
+
if char in ['"', "'"]:
|
|
87
|
+
if not in_quote:
|
|
88
|
+
in_quote = True
|
|
89
|
+
quote_char = char
|
|
90
|
+
elif char == quote_char:
|
|
91
|
+
in_quote = False
|
|
92
|
+
quote_char = None
|
|
93
|
+
elif not in_quote and char == op:
|
|
94
|
+
last_idx = i
|
|
95
|
+
return last_idx
|
|
53
96
|
|
|
54
97
|
def _compute(self, expr):
|
|
55
98
|
expr = str(expr).strip()
|
|
56
|
-
|
|
99
|
+
|
|
100
|
+
# Handle parentheses
|
|
101
|
+
idx = 0
|
|
57
102
|
while True:
|
|
58
103
|
end_idx = expr.find(')', idx)
|
|
59
104
|
if end_idx == -1:
|
|
@@ -61,68 +106,75 @@ class SolInterpreter:
|
|
|
61
106
|
start_idx = expr.rfind('(', 0, end_idx)
|
|
62
107
|
if start_idx == -1:
|
|
63
108
|
break
|
|
64
|
-
|
|
65
|
-
# If there's a letter, number, or underscore right before the '(',
|
|
66
|
-
# it's a function call (like my_func() or _fetch()), so skip it here!
|
|
109
|
+
# Skip function calls
|
|
67
110
|
if start_idx > 0 and (expr[start_idx - 1].isalnum() or expr[start_idx - 1] == '_'):
|
|
68
111
|
idx = end_idx + 1
|
|
69
112
|
continue
|
|
70
|
-
|
|
71
|
-
# Extract and evaluate the pure math inside the brackets
|
|
72
113
|
inner_expr = expr[start_idx + 1:end_idx]
|
|
73
114
|
inner_result = self._compute(inner_expr)
|
|
74
|
-
|
|
75
|
-
# Stitch the evaluated number back into the main formula string
|
|
76
115
|
expr = expr[:start_idx] + str(inner_result) + expr[end_idx + 1:]
|
|
77
|
-
idx = 0
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
in_quote = False
|
|
82
|
-
quote_char = None
|
|
83
|
-
for char in string:
|
|
84
|
-
if char in ['"', "'"]:
|
|
85
|
-
if not in_quote:
|
|
86
|
-
in_quote = True
|
|
87
|
-
quote_char = char
|
|
88
|
-
elif char == quote_char:
|
|
89
|
-
in_quote = False
|
|
90
|
-
quote_char = None
|
|
91
|
-
current.append(char)
|
|
92
|
-
elif not in_quote and char == op:
|
|
93
|
-
parts.append("".join(current))
|
|
94
|
-
current = []
|
|
95
|
-
else:
|
|
96
|
-
current.append(char)
|
|
97
|
-
parts.append("".join(current))
|
|
98
|
-
return parts
|
|
99
|
-
|
|
100
|
-
plus_parts = split_outside_quotes(expr, '+')
|
|
116
|
+
idx = 0
|
|
117
|
+
|
|
118
|
+
# Handle string concatenation with +
|
|
119
|
+
plus_parts = self._split_outside_quotes(expr, '+')
|
|
101
120
|
if len(plus_parts) > 1:
|
|
102
|
-
|
|
121
|
+
is_string_concat = False
|
|
122
|
+
for p in plus_parts:
|
|
123
|
+
p = p.strip()
|
|
124
|
+
if (p.startswith('"') and p.endswith('"')) or (p.startswith("'") and p.endswith("'")):
|
|
125
|
+
is_string_concat = True
|
|
126
|
+
break
|
|
127
|
+
if is_string_concat:
|
|
103
128
|
result = ""
|
|
104
129
|
for p in plus_parts:
|
|
105
130
|
val = self._resolve(p.strip())
|
|
106
131
|
result += str(val) if val is not None else ""
|
|
107
132
|
return result
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
|
|
134
|
+
# Check for equality (=) - comparison operator
|
|
135
|
+
left, right = self._split_on_op(expr, '=')
|
|
136
|
+
if left is not None:
|
|
137
|
+
r1 = self._resolve(left.strip())
|
|
138
|
+
r2 = self._resolve(right.strip())
|
|
139
|
+
if r1 is not None and r2 is not None:
|
|
140
|
+
return 1 if r1 == r2 else 0
|
|
141
|
+
|
|
142
|
+
# Level 1: + and -
|
|
143
|
+
for op in ['+', '-']:
|
|
144
|
+
idx = self._find_last_op(expr, op)
|
|
145
|
+
if idx != -1:
|
|
146
|
+
left = expr[:idx].strip()
|
|
147
|
+
right = expr[idx+1:].strip()
|
|
148
|
+
r1 = self._resolve(left)
|
|
149
|
+
r2 = self._resolve(right)
|
|
150
|
+
if r1 is None or r2 is None:
|
|
151
|
+
continue
|
|
152
|
+
val1 = float(r1)
|
|
153
|
+
val2 = float(r2)
|
|
154
|
+
if op == '+': return val1 + val2
|
|
155
|
+
if op == '-': return val1 - val2
|
|
156
|
+
|
|
157
|
+
# Level 2: * and :
|
|
158
|
+
last_idx = -1
|
|
159
|
+
last_op = None
|
|
160
|
+
for op in ['*', ':']:
|
|
161
|
+
idx = self._find_last_op(expr, op)
|
|
162
|
+
if idx > last_idx:
|
|
163
|
+
last_idx = idx
|
|
164
|
+
last_op = op
|
|
165
|
+
if last_idx != -1:
|
|
166
|
+
left = expr[:last_idx].strip()
|
|
167
|
+
right = expr[last_idx+1:].strip()
|
|
168
|
+
r1 = self._resolve(left)
|
|
169
|
+
r2 = self._resolve(right)
|
|
170
|
+
if r1 is None or r2 is None:
|
|
171
|
+
pass
|
|
172
|
+
else:
|
|
173
|
+
val1 = float(r1)
|
|
174
|
+
val2 = float(r2)
|
|
175
|
+
if last_op == '*': return val1 * val2
|
|
176
|
+
if last_op == ':': return val1 / val2
|
|
177
|
+
|
|
126
178
|
return self._resolve(expr)
|
|
127
179
|
|
|
128
180
|
def _lookup(self, name):
|
|
@@ -140,6 +192,14 @@ class SolInterpreter:
|
|
|
140
192
|
if val.startswith("_fetch(") and val.endswith(")"):
|
|
141
193
|
url_expr = val[7:-1].strip()
|
|
142
194
|
return self._native_fetch(str(self._compute(url_expr)))
|
|
195
|
+
if val.endswith("{}") and "{" not in val[:-2]:
|
|
196
|
+
struct_name = val[:-2].strip()
|
|
197
|
+
blueprint = self.struct_blueprints.get(struct_name)
|
|
198
|
+
if blueprint is not None:
|
|
199
|
+
return blueprint.copy()
|
|
200
|
+
obj = self._lookup(struct_name)
|
|
201
|
+
if isinstance(obj, dict):
|
|
202
|
+
return obj.copy()
|
|
143
203
|
if "(" in val and val.endswith(")") and not val.startswith("_"):
|
|
144
204
|
parts = val.split("(", 1)
|
|
145
205
|
name = parts[0].strip()
|
|
@@ -149,7 +209,7 @@ class SolInterpreter:
|
|
|
149
209
|
return self._call_function(name, args)
|
|
150
210
|
if "[" in val and "]" in val:
|
|
151
211
|
try:
|
|
152
|
-
name, idx_part = val.split("[")
|
|
212
|
+
name, idx_part = val.split("[", 1)
|
|
153
213
|
idx = int(idx_part.replace("]", "").strip())
|
|
154
214
|
container = self._lookup(name)
|
|
155
215
|
if isinstance(container, list): return container[idx]
|
|
@@ -162,23 +222,10 @@ class SolInterpreter:
|
|
|
162
222
|
for scope in reversed(self.scope_stack):
|
|
163
223
|
if val in scope: return scope[val]
|
|
164
224
|
|
|
165
|
-
# FIX: Only try to parse as number if it looks like one
|
|
166
225
|
if val.replace('.', '', 1).isdigit() or (val.startswith('-') and val[1:].replace('.', '', 1).isdigit()):
|
|
167
226
|
return float(val) if '.' in val else int(val)
|
|
168
|
-
|
|
169
|
-
return None
|
|
170
|
-
|
|
171
|
-
def _async_worker(self, expr, action):
|
|
172
|
-
result = self._compute(expr)
|
|
173
|
-
if action == "_out": print(f"[ASYNC OUTPUT] {result}")
|
|
174
227
|
|
|
175
|
-
|
|
176
|
-
user_val = input(prompt)
|
|
177
|
-
try:
|
|
178
|
-
if '.' in user_val: user_val = float(user_val)
|
|
179
|
-
else: user_val = int(user_val)
|
|
180
|
-
except ValueError: pass
|
|
181
|
-
self._assign(var_name, user_val)
|
|
228
|
+
return None
|
|
182
229
|
|
|
183
230
|
def _native_fetch(self, url):
|
|
184
231
|
try:
|
|
@@ -206,17 +253,17 @@ class SolInterpreter:
|
|
|
206
253
|
self.threads.append(t)
|
|
207
254
|
|
|
208
255
|
def _evaluate_condition(self, cond):
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
left, right = cond.split("<")
|
|
256
|
+
# Equality comparison uses =
|
|
257
|
+
left, right = self._split_on_op(cond, "=")
|
|
258
|
+
if left is not None:
|
|
259
|
+
return self._compute(cond) # _compute handles =
|
|
260
|
+
# Less than
|
|
261
|
+
left, right = self._split_on_op(cond, "<")
|
|
262
|
+
if left is not None:
|
|
217
263
|
return self._compute(left.strip()) < self._compute(right.strip())
|
|
218
|
-
|
|
219
|
-
|
|
264
|
+
# Greater than
|
|
265
|
+
left, right = self._split_on_op(cond, ">")
|
|
266
|
+
if left is not None:
|
|
220
267
|
return self._compute(left.strip()) > self._compute(right.strip())
|
|
221
268
|
return False
|
|
222
269
|
|
|
@@ -233,8 +280,21 @@ class SolInterpreter:
|
|
|
233
280
|
return pc
|
|
234
281
|
|
|
235
282
|
def execute(self, code):
|
|
236
|
-
|
|
237
|
-
self.lines = [
|
|
283
|
+
raw_lines = code.split('\n')
|
|
284
|
+
self.lines = []
|
|
285
|
+
for l in raw_lines:
|
|
286
|
+
l = l.strip()
|
|
287
|
+
if not l:
|
|
288
|
+
continue
|
|
289
|
+
if l.startswith(';'):
|
|
290
|
+
continue
|
|
291
|
+
if l.startswith('//'):
|
|
292
|
+
continue
|
|
293
|
+
if ';' in l:
|
|
294
|
+
l = l.split(';', 1)[0].strip()
|
|
295
|
+
if l:
|
|
296
|
+
self.lines.append(l)
|
|
297
|
+
|
|
238
298
|
self.threads = []
|
|
239
299
|
pc = 0
|
|
240
300
|
while pc < len(self.lines):
|
|
@@ -242,7 +302,10 @@ class SolInterpreter:
|
|
|
242
302
|
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")):
|
|
243
303
|
parts = line.split("(")
|
|
244
304
|
self.functions[parts[0].strip()] = {"start_pc": pc + 1, "params": [p.strip() for p in parts[1].replace(")", "").split(",")] if parts[1].replace(")", "").strip() else []}
|
|
245
|
-
elif "{" in line
|
|
305
|
+
elif line.endswith("{}") and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("_"):
|
|
306
|
+
pc = self._parse_struct(pc)
|
|
307
|
+
pc += 1
|
|
308
|
+
continue
|
|
246
309
|
pc += 1
|
|
247
310
|
for i, line in enumerate(self.lines):
|
|
248
311
|
if line == "main()":
|
|
@@ -275,7 +338,9 @@ class SolInterpreter:
|
|
|
275
338
|
return self._run_with_scope(self.functions[name]["start_pc"], self.functions[name]["params"], args)
|
|
276
339
|
|
|
277
340
|
def _run_with_scope(self, start_pc, params, args):
|
|
278
|
-
local_scope = {
|
|
341
|
+
local_scope = {}
|
|
342
|
+
for p, a in zip(params, args):
|
|
343
|
+
local_scope[p] = a
|
|
279
344
|
self.scope_stack.append(local_scope)
|
|
280
345
|
try: self._run_block(start_pc)
|
|
281
346
|
except ReturnSignal as sig: return sig.value
|
|
@@ -283,55 +348,114 @@ class SolInterpreter:
|
|
|
283
348
|
return 0
|
|
284
349
|
|
|
285
350
|
def _execute_line(self, line):
|
|
286
|
-
|
|
351
|
+
# Handle imports
|
|
352
|
+
if line.startswith("_get("):
|
|
287
353
|
filename = line.split('"')[1]
|
|
288
354
|
self._import_file(filename)
|
|
289
355
|
return
|
|
290
|
-
|
|
356
|
+
|
|
357
|
+
# Handle piping input
|
|
358
|
+
if ">>" in line and "_in" in line:
|
|
291
359
|
parts = [p.strip() for p in line.split(">>")]
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
360
|
+
if len(parts) >= 3 and parts[1] == "_in":
|
|
361
|
+
prompt = str(self._compute(parts[0]))
|
|
362
|
+
var_name = parts[2]
|
|
363
|
+
sys.stdout.write(prompt + " "); sys.stdout.flush()
|
|
364
|
+
val = sys.stdin.readline().strip()
|
|
365
|
+
try: self._assign(var_name, float(val) if '.' in val else int(val))
|
|
366
|
+
except: self._assign(var_name, val)
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
# Check if async
|
|
370
|
+
is_async = False
|
|
371
|
+
if ">>" in line:
|
|
372
|
+
parts = [p.strip() for p in line.split(">>")]
|
|
373
|
+
if parts[-1] == "_async":
|
|
374
|
+
line = ">>".join(line.split(">>")[:-1]).strip()
|
|
375
|
+
is_async = True
|
|
376
|
+
|
|
377
|
+
if line.startswith("_return ->"):
|
|
378
|
+
raise ReturnSignal(self._compute(line.split("->", 1)[1]))
|
|
379
|
+
|
|
380
|
+
# Handle _out with piping
|
|
381
|
+
if ">>" in line and "_out" in line:
|
|
382
|
+
parts = [p.strip() for p in line.split(">>")]
|
|
383
|
+
if len(parts) == 2 and parts[1] == "_out":
|
|
384
|
+
val = self._compute(parts[0])
|
|
385
|
+
if is_async:
|
|
386
|
+
t = threading.Thread(target=lambda v=val: print(v if v is not None else ""))
|
|
387
|
+
t.start(); self.threads.append(t)
|
|
388
|
+
else:
|
|
389
|
+
print(val if val is not None else "")
|
|
390
|
+
return
|
|
391
|
+
|
|
392
|
+
if line.startswith("_out"):
|
|
393
|
+
val = self._compute(line.split("->", 1)[1].strip())
|
|
394
|
+
if is_async:
|
|
395
|
+
t = threading.Thread(target=lambda v=val: print(v if v is not None else ""))
|
|
396
|
+
t.start(); self.threads.append(t)
|
|
397
|
+
else:
|
|
398
|
+
print(val if val is not None else "")
|
|
399
|
+
return
|
|
400
|
+
|
|
401
|
+
if line.startswith("_in"):
|
|
402
|
+
if "->" in line:
|
|
403
|
+
var_name = line.split("->", 1)[1].strip()
|
|
404
|
+
sys.stdout.write(f"Input {var_name}: "); sys.stdout.flush()
|
|
405
|
+
val = sys.stdin.readline().strip()
|
|
406
|
+
try: self._assign(var_name, float(val) if '.' in val else int(val))
|
|
407
|
+
except: self._assign(var_name, val)
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
if "_add(" in line:
|
|
308
411
|
parts = [p.strip() for p in line.replace("_add(", "").replace(")", "").split(",")]
|
|
309
412
|
container = self._lookup(parts[0])
|
|
310
413
|
if isinstance(container, list): container.insert(int(parts[1]), self._resolve(parts[2]))
|
|
311
|
-
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
if "_remove(" in line:
|
|
312
417
|
parts = [p.strip() for p in line.replace("_remove(", "").replace(")", "").split(",")]
|
|
313
418
|
container = self._lookup(parts[0])
|
|
314
419
|
if isinstance(container, list): container.pop(int(parts[1]))
|
|
315
|
-
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
if "_listen(" in line:
|
|
316
423
|
p, h = [p.strip() for p in line.replace("_listen(", "").replace(")", "").split(",")]
|
|
317
424
|
self._native_listen(self._compute(p), h)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
425
|
+
return
|
|
426
|
+
|
|
427
|
+
# List declaration
|
|
428
|
+
if "<" in line and ">" in line and "->" not in line and ">>" not in line and "?" not in line and not line.startswith("_"):
|
|
429
|
+
parts = line.split("<")
|
|
430
|
+
if len(parts) == 2 and ">" in parts[1]:
|
|
431
|
+
list_name = parts[1].split(">")[0].strip()
|
|
432
|
+
self._assign(list_name, [])
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
# Assignment - ONLY with ->
|
|
436
|
+
if "->" in line:
|
|
437
|
+
var, val = [x.strip() for x in line.split("->", 1)]
|
|
324
438
|
if "." in var:
|
|
325
439
|
obj, prop = var.split(".", 1)
|
|
326
440
|
target = self._lookup(obj)
|
|
327
441
|
if isinstance(target, dict): target[prop] = self._compute(val)
|
|
328
|
-
else:
|
|
329
|
-
|
|
442
|
+
else:
|
|
443
|
+
computed = self._compute(val)
|
|
444
|
+
self._assign(var, computed)
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
# Function call
|
|
448
|
+
if "(" in line and line.endswith(")") and not line.startswith("_"):
|
|
330
449
|
parts = line.split("(")
|
|
331
|
-
|
|
332
|
-
|
|
450
|
+
func_name = parts[0].strip()
|
|
451
|
+
args_str = parts[1][:-1].strip()
|
|
452
|
+
args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
|
|
453
|
+
if is_async:
|
|
454
|
+
t = threading.Thread(target=self._call_function, args=(func_name, args))
|
|
333
455
|
t.start(); self.threads.append(t)
|
|
334
|
-
else:
|
|
456
|
+
else:
|
|
457
|
+
self._call_function(func_name, args)
|
|
458
|
+
return
|
|
335
459
|
|
|
336
460
|
def _run_block(self, start_pc):
|
|
337
461
|
pc = start_pc
|
|
@@ -367,8 +491,9 @@ class SolInterpreter:
|
|
|
367
491
|
elif line.startswith("for "):
|
|
368
492
|
parts = line.split("for ", 1)[1].split("->")
|
|
369
493
|
var_name = parts[0].strip()
|
|
370
|
-
|
|
371
|
-
|
|
494
|
+
range_parts = parts[1].split(" to ")
|
|
495
|
+
start_v = int(self._compute(range_parts[0].strip()))
|
|
496
|
+
end_v = int(self._compute(range_parts[1].strip()))
|
|
372
497
|
end_pc = self._get_block_end(pc)
|
|
373
498
|
if is_executing and start_v <= end_v:
|
|
374
499
|
self._assign(var_name, start_v)
|
|
@@ -387,7 +512,7 @@ class SolInterpreter:
|
|
|
387
512
|
pc = top["start_pc"] - 1
|
|
388
513
|
pc += 1; continue
|
|
389
514
|
if is_executing:
|
|
390
|
-
if
|
|
515
|
+
if self._has_guard(line):
|
|
391
516
|
cond, action = line.split("?", 1)
|
|
392
517
|
if self._evaluate_condition(cond.strip()): self._execute_line(action.strip())
|
|
393
518
|
else: self._execute_line(line)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solpl
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: A lightweight, multi-threaded interpreter for the Sol programming language
|
|
5
5
|
Author-email: stefand-0 <stefand-0@users.noreply.github.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -82,6 +82,11 @@ main()
|
|
|
82
82
|
end
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
> [!IMPORTANT]
|
|
86
|
+
> Asynchronous functions cannot use the _return method to return a value. Please use _out instead.
|
|
87
|
+
|
|
88
|
+
> [!TIP]
|
|
89
|
+
> Make use of the standard libraries for extra data types, such as Booleans, HashMaps etc.
|
|
85
90
|
# License
|
|
86
91
|
|
|
87
92
|
This repository is licensed with Apache License 2.0, see the LICENSE file for more details
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|