solpl 2.6.6__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.6/solpl.egg-info → solpl-2.7.0}/PKG-INFO +6 -1
- {solpl-2.6.6 → solpl-2.7.0}/README.md +5 -0
- {solpl-2.6.6 → solpl-2.7.0}/interpreter/sol.py +276 -130
- {solpl-2.6.6 → solpl-2.7.0}/pyproject.toml +1 -1
- {solpl-2.6.6 → solpl-2.7.0/solpl.egg-info}/PKG-INFO +6 -1
- {solpl-2.6.6 → solpl-2.7.0}/LICENSE +0 -0
- {solpl-2.6.6 → solpl-2.7.0}/setup.cfg +0 -0
- {solpl-2.6.6 → solpl-2.7.0}/solpl.egg-info/SOURCES.txt +0 -0
- {solpl-2.6.6 → solpl-2.7.0}/solpl.egg-info/dependency_links.txt +0 -0
- {solpl-2.6.6 → solpl-2.7.0}/solpl.egg-info/entry_points.txt +0 -0
- {solpl-2.6.6 → solpl-2.7.0}/solpl.egg-info/top_level.txt +0 -0
|
@@ -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
|
|
@@ -14,10 +14,10 @@ class SolInterpreter:
|
|
|
14
14
|
self.variables = {}
|
|
15
15
|
self.thread_local = threading.local()
|
|
16
16
|
self.functions = {}
|
|
17
|
-
self.struct_blueprints = {}
|
|
17
|
+
self.struct_blueprints = {}
|
|
18
18
|
self.lines = []
|
|
19
19
|
self.threads = []
|
|
20
|
-
self._server_started = False
|
|
20
|
+
self._server_started = False
|
|
21
21
|
|
|
22
22
|
@property
|
|
23
23
|
def scope_stack(self):
|
|
@@ -25,83 +25,156 @@ 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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return parts
|
|
78
|
-
|
|
79
|
-
plus_parts = split_outside_quotes(expr, '+')
|
|
99
|
+
|
|
100
|
+
# Handle parentheses
|
|
101
|
+
idx = 0
|
|
102
|
+
while True:
|
|
103
|
+
end_idx = expr.find(')', idx)
|
|
104
|
+
if end_idx == -1:
|
|
105
|
+
break
|
|
106
|
+
start_idx = expr.rfind('(', 0, end_idx)
|
|
107
|
+
if start_idx == -1:
|
|
108
|
+
break
|
|
109
|
+
# Skip function calls
|
|
110
|
+
if start_idx > 0 and (expr[start_idx - 1].isalnum() or expr[start_idx - 1] == '_'):
|
|
111
|
+
idx = end_idx + 1
|
|
112
|
+
continue
|
|
113
|
+
inner_expr = expr[start_idx + 1:end_idx]
|
|
114
|
+
inner_result = self._compute(inner_expr)
|
|
115
|
+
expr = expr[:start_idx] + str(inner_result) + expr[end_idx + 1:]
|
|
116
|
+
idx = 0
|
|
117
|
+
|
|
118
|
+
# Handle string concatenation with +
|
|
119
|
+
plus_parts = self._split_outside_quotes(expr, '+')
|
|
80
120
|
if len(plus_parts) > 1:
|
|
81
|
-
|
|
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:
|
|
82
128
|
result = ""
|
|
83
129
|
for p in plus_parts:
|
|
84
130
|
val = self._resolve(p.strip())
|
|
85
131
|
result += str(val) if val is not None else ""
|
|
86
132
|
return result
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
|
|
105
178
|
return self._resolve(expr)
|
|
106
179
|
|
|
107
180
|
def _lookup(self, name):
|
|
@@ -119,6 +192,14 @@ class SolInterpreter:
|
|
|
119
192
|
if val.startswith("_fetch(") and val.endswith(")"):
|
|
120
193
|
url_expr = val[7:-1].strip()
|
|
121
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()
|
|
122
203
|
if "(" in val and val.endswith(")") and not val.startswith("_"):
|
|
123
204
|
parts = val.split("(", 1)
|
|
124
205
|
name = parts[0].strip()
|
|
@@ -128,7 +209,7 @@ class SolInterpreter:
|
|
|
128
209
|
return self._call_function(name, args)
|
|
129
210
|
if "[" in val and "]" in val:
|
|
130
211
|
try:
|
|
131
|
-
name, idx_part = val.split("[")
|
|
212
|
+
name, idx_part = val.split("[", 1)
|
|
132
213
|
idx = int(idx_part.replace("]", "").strip())
|
|
133
214
|
container = self._lookup(name)
|
|
134
215
|
if isinstance(container, list): return container[idx]
|
|
@@ -143,20 +224,8 @@ class SolInterpreter:
|
|
|
143
224
|
|
|
144
225
|
if val.replace('.', '', 1).isdigit() or (val.startswith('-') and val[1:].replace('.', '', 1).isdigit()):
|
|
145
226
|
return float(val) if '.' in val else int(val)
|
|
146
|
-
|
|
147
|
-
return None
|
|
148
|
-
|
|
149
|
-
def _async_worker(self, expr, action):
|
|
150
|
-
result = self._compute(expr)
|
|
151
|
-
if action == "_out": print(f"[ASYNC OUTPUT] {result}")
|
|
152
227
|
|
|
153
|
-
|
|
154
|
-
user_val = input(prompt)
|
|
155
|
-
try:
|
|
156
|
-
if '.' in user_val: user_val = float(user_val)
|
|
157
|
-
else: user_val = int(user_val)
|
|
158
|
-
except ValueError: pass
|
|
159
|
-
self._assign(var_name, user_val)
|
|
228
|
+
return None
|
|
160
229
|
|
|
161
230
|
def _native_fetch(self, url):
|
|
162
231
|
try:
|
|
@@ -184,17 +253,17 @@ class SolInterpreter:
|
|
|
184
253
|
self.threads.append(t)
|
|
185
254
|
|
|
186
255
|
def _evaluate_condition(self, cond):
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
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:
|
|
195
263
|
return self._compute(left.strip()) < self._compute(right.strip())
|
|
196
|
-
|
|
197
|
-
|
|
264
|
+
# Greater than
|
|
265
|
+
left, right = self._split_on_op(cond, ">")
|
|
266
|
+
if left is not None:
|
|
198
267
|
return self._compute(left.strip()) > self._compute(right.strip())
|
|
199
268
|
return False
|
|
200
269
|
|
|
@@ -211,8 +280,21 @@ class SolInterpreter:
|
|
|
211
280
|
return pc
|
|
212
281
|
|
|
213
282
|
def execute(self, code):
|
|
214
|
-
|
|
215
|
-
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
|
+
|
|
216
298
|
self.threads = []
|
|
217
299
|
pc = 0
|
|
218
300
|
while pc < len(self.lines):
|
|
@@ -220,7 +302,10 @@ class SolInterpreter:
|
|
|
220
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")):
|
|
221
303
|
parts = line.split("(")
|
|
222
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 []}
|
|
223
|
-
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
|
|
224
309
|
pc += 1
|
|
225
310
|
for i, line in enumerate(self.lines):
|
|
226
311
|
if line == "main()":
|
|
@@ -253,7 +338,9 @@ class SolInterpreter:
|
|
|
253
338
|
return self._run_with_scope(self.functions[name]["start_pc"], self.functions[name]["params"], args)
|
|
254
339
|
|
|
255
340
|
def _run_with_scope(self, start_pc, params, args):
|
|
256
|
-
local_scope = {
|
|
341
|
+
local_scope = {}
|
|
342
|
+
for p, a in zip(params, args):
|
|
343
|
+
local_scope[p] = a
|
|
257
344
|
self.scope_stack.append(local_scope)
|
|
258
345
|
try: self._run_block(start_pc)
|
|
259
346
|
except ReturnSignal as sig: return sig.value
|
|
@@ -261,55 +348,114 @@ class SolInterpreter:
|
|
|
261
348
|
return 0
|
|
262
349
|
|
|
263
350
|
def _execute_line(self, line):
|
|
264
|
-
|
|
351
|
+
# Handle imports
|
|
352
|
+
if line.startswith("_get("):
|
|
265
353
|
filename = line.split('"')[1]
|
|
266
354
|
self._import_file(filename)
|
|
267
355
|
return
|
|
268
|
-
|
|
356
|
+
|
|
357
|
+
# Handle piping input
|
|
358
|
+
if ">>" in line and "_in" in line:
|
|
359
|
+
parts = [p.strip() for p in line.split(">>")]
|
|
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:
|
|
269
382
|
parts = [p.strip() for p in line.split(">>")]
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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:
|
|
286
411
|
parts = [p.strip() for p in line.replace("_add(", "").replace(")", "").split(",")]
|
|
287
412
|
container = self._lookup(parts[0])
|
|
288
413
|
if isinstance(container, list): container.insert(int(parts[1]), self._resolve(parts[2]))
|
|
289
|
-
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
if "_remove(" in line:
|
|
290
417
|
parts = [p.strip() for p in line.replace("_remove(", "").replace(")", "").split(",")]
|
|
291
418
|
container = self._lookup(parts[0])
|
|
292
419
|
if isinstance(container, list): container.pop(int(parts[1]))
|
|
293
|
-
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
if "_listen(" in line:
|
|
294
423
|
p, h = [p.strip() for p in line.replace("_listen(", "").replace(")", "").split(",")]
|
|
295
424
|
self._native_listen(self._compute(p), h)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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)]
|
|
302
438
|
if "." in var:
|
|
303
439
|
obj, prop = var.split(".", 1)
|
|
304
440
|
target = self._lookup(obj)
|
|
305
441
|
if isinstance(target, dict): target[prop] = self._compute(val)
|
|
306
|
-
else:
|
|
307
|
-
|
|
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("_"):
|
|
308
449
|
parts = line.split("(")
|
|
309
|
-
|
|
310
|
-
|
|
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))
|
|
311
455
|
t.start(); self.threads.append(t)
|
|
312
|
-
else:
|
|
456
|
+
else:
|
|
457
|
+
self._call_function(func_name, args)
|
|
458
|
+
return
|
|
313
459
|
|
|
314
460
|
def _run_block(self, start_pc):
|
|
315
461
|
pc = start_pc
|
|
@@ -345,8 +491,9 @@ class SolInterpreter:
|
|
|
345
491
|
elif line.startswith("for "):
|
|
346
492
|
parts = line.split("for ", 1)[1].split("->")
|
|
347
493
|
var_name = parts[0].strip()
|
|
348
|
-
|
|
349
|
-
|
|
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()))
|
|
350
497
|
end_pc = self._get_block_end(pc)
|
|
351
498
|
if is_executing and start_v <= end_v:
|
|
352
499
|
self._assign(var_name, start_v)
|
|
@@ -365,7 +512,7 @@ class SolInterpreter:
|
|
|
365
512
|
pc = top["start_pc"] - 1
|
|
366
513
|
pc += 1; continue
|
|
367
514
|
if is_executing:
|
|
368
|
-
if
|
|
515
|
+
if self._has_guard(line):
|
|
369
516
|
cond, action = line.split("?", 1)
|
|
370
517
|
if self._evaluate_condition(cond.strip()): self._execute_line(action.strip())
|
|
371
518
|
else: self._execute_line(line)
|
|
@@ -383,4 +530,3 @@ def main():
|
|
|
383
530
|
|
|
384
531
|
if __name__ == "__main__":
|
|
385
532
|
main()
|
|
386
|
-
|
|
@@ -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
|
|
File without changes
|