solpl 2.2.0__tar.gz → 2.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solpl
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Sol Programming Language Interpreter
5
5
  Requires-Python: >=3.7
6
6
  License-File: LICENSE
@@ -0,0 +1,342 @@
1
+ import threading
2
+ import time
3
+ import sys
4
+ import os
5
+ import urllib.request
6
+ import http.server
7
+
8
+ class ReturnSignal(Exception):
9
+ def __init__(self, value):
10
+ self.value = value
11
+
12
+ class SolInterpreter:
13
+ def __init__(self):
14
+ self.variables = {}
15
+ self.thread_local = threading.local()
16
+ self.functions = {}
17
+ self.struct_blueprints = {}
18
+ self.lines = []
19
+ self.threads = []
20
+ self._server_started = False
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 _import_file(self, filename):
29
+ if not os.path.exists(filename):
30
+ print(f"Error: Module '{filename}' not found.")
31
+ return
32
+
33
+ with open(filename, "r") as f:
34
+ code = f.read()
35
+ module_name = filename.replace(".sol", "")
36
+ lines = [l.strip() for l in code.split('\n') if l.strip()]
37
+ for pc, line in enumerate(lines):
38
+ 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")):
39
+ parts = line.split("(")
40
+ name = parts[0].strip()
41
+ params_str = parts[1].replace(")", "").strip()
42
+ params = [p.strip() for p in params_str.split(",")] if params_str else []
43
+ self.functions[f"{module_name}.{name}"] = {"start_pc": pc + 1, "params": params, "source_lines": lines}
44
+
45
+ def _compute(self, expr):
46
+ expr = str(expr).strip()
47
+ if "+" in expr and any(c.isalpha() or c in ['"', "'"] for c in expr):
48
+ parts = expr.split("+")
49
+ result = ""
50
+ for p in parts:
51
+ val = self._resolve(p.strip())
52
+ result += str(val) if val is not None else ""
53
+ return result
54
+ ops = ['+', '-', '*', ':']
55
+ for op in ops:
56
+ if op in expr:
57
+ try:
58
+ left, right = expr.split(op, 1)
59
+ r1 = self._resolve(left)
60
+ r2 = self._resolve(right)
61
+ if r1 is None or r2 is None: continue
62
+ val1 = float(r1)
63
+ val2 = float(r2)
64
+ if op == '+': return val1 + val2
65
+ if op == '-': return val1 - val2
66
+ if op == '*': return val1 * val2
67
+ if op == ':': return val1 / val2
68
+ except (ValueError, TypeError): continue
69
+ return self._resolve(expr)
70
+
71
+ def _lookup(self, name):
72
+ for scope in reversed(self.scope_stack):
73
+ if name in scope: return scope[name]
74
+ return None
75
+
76
+ def _assign(self, name, val):
77
+ self.scope_stack[-1][name] = val
78
+
79
+ def _resolve(self, val):
80
+ val = str(val).strip()
81
+ if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
82
+ return val[1:-1]
83
+ if val.startswith("_fetch(") and val.endswith(")"):
84
+ url_expr = val[7:-1].strip()
85
+ return self._native_fetch(str(self._compute(url_expr)))
86
+ if "(" in val and val.endswith(")") and not val.startswith("_"):
87
+ parts = val.split("(", 1)
88
+ name = parts[0].strip()
89
+ if name in self.functions:
90
+ args_str = parts[1][:-1].strip()
91
+ args = [self._compute(a.strip()) for a in args_str.split(",")] if args_str else []
92
+ return self._call_function(name, args)
93
+ if "[" in val and "]" in val:
94
+ try:
95
+ name, idx_part = val.split("[")
96
+ idx = int(idx_part.replace("]", "").strip())
97
+ container = self._lookup(name)
98
+ if isinstance(container, list): return container[idx]
99
+ except (ValueError, IndexError, KeyError): pass
100
+ if "." in val:
101
+ obj_name, prop = val.split(".", 1)
102
+ obj = self._lookup(obj_name)
103
+ if isinstance(obj, dict): return obj.get(prop, 0)
104
+ existing_val = self._lookup(val)
105
+ if existing_val is not None: return existing_val
106
+ try: return float(val) if '.' in val else int(val)
107
+ except: return None
108
+
109
+ def _async_worker(self, expr, action):
110
+ result = self._compute(expr)
111
+ if action == "_out": print(f"[ASYNC OUTPUT] {result}")
112
+
113
+ def _async_input_worker(self, prompt, var_name):
114
+ user_val = input(prompt)
115
+ try:
116
+ if '.' in user_val: user_val = float(user_val)
117
+ else: user_val = int(user_val)
118
+ except ValueError: pass
119
+ self._assign(var_name, user_val)
120
+
121
+ def _native_fetch(self, url):
122
+ try:
123
+ with urllib.request.urlopen(url, timeout=5) as response:
124
+ return response.read().decode('utf-8', errors='replace')
125
+ except Exception as e: return f"Error: {str(e)}"
126
+
127
+ def _native_listen(self, port, handler_name):
128
+ if self._server_started: return
129
+ self._server_started = True
130
+ interpreter_instance = self
131
+ class SolHTTPHandler(http.server.BaseHTTPRequestHandler):
132
+ def do_GET(self):
133
+ self.send_response(200)
134
+ self.send_header("Content-type", "text/html")
135
+ self.end_headers()
136
+ response_body = interpreter_instance._call_function(handler_name, [self.path])
137
+ self.wfile.write(bytes(str(response_body), "utf-8"))
138
+ def log_message(self, format, *args): pass
139
+ def server_thread():
140
+ server = http.server.HTTPServer(("0.0.0.0", int(port)), SolHTTPHandler)
141
+ server.serve_forever()
142
+ t = threading.Thread(target=server_thread, daemon=True)
143
+ t.start()
144
+ self.threads.append(t)
145
+
146
+ def _evaluate_condition(self, cond):
147
+ if "=" in cond:
148
+ left, right = cond.split("=")
149
+ return self._compute(left.strip()) == self._compute(right.strip())
150
+ elif "<" in cond:
151
+ left, right = cond.split("<")
152
+ return self._compute(left.strip()) < self._compute(right.strip())
153
+ elif ">" in cond:
154
+ left, right = cond.split(">")
155
+ return self._compute(left.strip()) > self._compute(right.strip())
156
+ return False
157
+
158
+ def _get_block_end(self, start_pc):
159
+ depth = 1
160
+ pc = start_pc + 1
161
+ while pc < len(self.lines):
162
+ line = self.lines[pc]
163
+ if line.startswith("if ") or line.startswith("while ") or line.startswith("for "): depth += 1
164
+ elif line == "end":
165
+ depth -= 1
166
+ if depth == 0: return pc
167
+ pc += 1
168
+ return pc
169
+
170
+ def execute(self, code):
171
+ self.lines = [l.strip() for l in code.split('\n') if l.strip() and ';' not in l or (';' in l and l.split(';', 1)[0].strip())]
172
+ self.lines = [l.split(';', 1)[0].strip() for l in self.lines]
173
+ self.threads = []
174
+ pc = 0
175
+ while pc < len(self.lines):
176
+ line = self.lines[pc]
177
+ 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")):
178
+ parts = line.split("(")
179
+ 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 []}
180
+ elif "{" in line: pc = self._parse_struct(pc)
181
+ pc += 1
182
+ for i, line in enumerate(self.lines):
183
+ if line == "main()":
184
+ try: self._run_block(i + 1)
185
+ except ReturnSignal: pass
186
+ break
187
+ for t in self.threads:
188
+ if t.is_alive() and not t.daemon: t.join()
189
+
190
+ def _parse_struct(self, pc):
191
+ struct_name = self.lines[pc].split("{}")[0].strip()
192
+ fields = {}
193
+ pc += 1
194
+ while pc < len(self.lines) and self.lines[pc] != "end":
195
+ if "->" in self.lines[pc]:
196
+ key, val = [x.strip() for x in self.lines[pc].split("->")]
197
+ fields[key] = self._resolve(val)
198
+ pc += 1
199
+ self.struct_blueprints[struct_name] = fields
200
+ self._assign(struct_name, fields.copy())
201
+ return pc
202
+
203
+ def _call_function(self, name, args):
204
+ if "." in name and name in self.functions:
205
+ orig_lines = self.lines
206
+ self.lines = self.functions[name]["source_lines"]
207
+ res = self._run_with_scope(self.functions[name]["start_pc"], self.functions[name]["params"], args)
208
+ self.lines = orig_lines
209
+ return res
210
+ return self._run_with_scope(self.functions[name]["start_pc"], self.functions[name]["params"], args)
211
+
212
+ def _run_with_scope(self, start_pc, params, args):
213
+ local_scope = {p: a for p, a in zip(params, args)}
214
+ self.scope_stack.append(local_scope)
215
+ try: self._run_block(start_pc)
216
+ except ReturnSignal as sig: return sig.value
217
+ finally: self.scope_stack.pop()
218
+ return 0
219
+
220
+ def _execute_line(self, line):
221
+ if "_get(" in line:
222
+ filename = line.split('"')[1]
223
+ self._import_file(filename)
224
+ return
225
+ if ">>" in line and "_input" in line:
226
+ parts = [p.strip() for p in line.split(">>")]
227
+ t = threading.Thread(target=self._async_input_worker, args=(str(self._compute(parts[0])), parts[2]))
228
+ t.start(); self.threads.append(t); return
229
+ is_async_func = ">>" in line and "_async" in line and "(" in line and not line.startswith("_")
230
+ if is_async_func: line = line.split(">>")[0].strip()
231
+ if line.startswith("_return ->"): raise ReturnSignal(self._compute(line.split("->", 1)[1]))
232
+ elif ">>" in line and "_async" in line:
233
+ parts = line.split(">>")
234
+ t = threading.Thread(target=self._async_worker, args=(parts[0].strip(), parts[2].strip()))
235
+ t.start(); self.threads.append(t)
236
+ elif line.startswith("_in"):
237
+ prompt = line.split('(')[1].split(')')[0].replace('"', '').replace("'", "") if "(" in line else f"Input {line.split('->')[1].strip()}: "
238
+ sys.stdout.write(prompt + " "); sys.stdout.flush()
239
+ val = sys.stdin.readline().strip()
240
+ try: self._assign(line.split("->")[1].strip(), float(val) if '.' in val else int(val))
241
+ except: self._assign(line.split("->")[1].strip(), val)
242
+ elif "_add(" in line:
243
+ parts = [p.strip() for p in line.replace("_add(", "").replace(")", "").split(",")]
244
+ container = self._lookup(parts[0])
245
+ if isinstance(container, list): container.insert(int(parts[1]), self._resolve(parts[2]))
246
+ elif "_remove(" in line:
247
+ parts = [p.strip() for p in line.replace("_remove(", "").replace(")", "").split(",")]
248
+ container = self._lookup(parts[0])
249
+ if isinstance(container, list): container.pop(int(parts[1]))
250
+ elif "_listen(" in line:
251
+ p, h = [p.strip() for p in line.replace("_listen(", "").replace(")", "").split(",")]
252
+ self._native_listen(self._compute(p), h)
253
+ elif "_out" in line:
254
+ val = self._compute(line.split("->")[1].strip())
255
+ print(val if val is not None else "Error")
256
+ elif "mylist<" in line: self._assign(line.split("<")[1].split(">")[0], [])
257
+ elif "->" in line:
258
+ var, val = [x.strip() for x in line.split("->")]
259
+ if "." in var:
260
+ obj, prop = var.split(".", 1)
261
+ target = self._lookup(obj)
262
+ if isinstance(target, dict): target[prop] = self._compute(val)
263
+ else: self._assign(var, self._compute(val))
264
+ elif "(" in line and line.endswith(")") and not line.startswith("_"):
265
+ parts = line.split("(")
266
+ if is_async_func:
267
+ t = threading.Thread(target=self._call_function, args=(parts[0].strip(), [self._compute(a.strip()) for a in parts[1].replace(")", "").split(",")]))
268
+ t.start(); self.threads.append(t)
269
+ else: self._call_function(parts[0].strip(), [self._compute(a.strip()) for a in parts[1].replace(")", "").split(",")])
270
+
271
+ def _run_block(self, start_pc):
272
+ pc = start_pc
273
+ control_stack = []
274
+ while pc < len(self.lines):
275
+ line = self.lines[pc]
276
+ is_executing = all(level["executing"] for level in control_stack)
277
+ if line.startswith("if "):
278
+ control_stack.append({"type": "if", "executing": is_executing and self._evaluate_condition(line.split("if ", 1)[1].strip()), "any_branch_true": False})
279
+ pc += 1; continue
280
+ elif line.startswith("elseif "):
281
+ cond = line.split("elseif ", 1)[1].strip()
282
+ parent_active = all(level["executing"] for level in control_stack[:-1]) if len(control_stack) > 1 else True
283
+ if control_stack and control_stack[-1]["type"] == "if":
284
+ if parent_active and not control_stack[-1]["any_branch_true"] and self._evaluate_condition(cond):
285
+ control_stack[-1]["executing"] = True
286
+ control_stack[-1]["any_branch_true"] = True
287
+ else: control_stack[-1]["executing"] = False
288
+ pc += 1; continue
289
+ elif line == "else":
290
+ parent_active = all(level["executing"] for level in control_stack[:-1]) if len(control_stack) > 1 else True
291
+ if control_stack and control_stack[-1]["type"] == "if":
292
+ control_stack[-1]["executing"] = parent_active and not control_stack[-1]["any_branch_true"]
293
+ pc += 1; continue
294
+ elif line.startswith("while "):
295
+ cond = line.split("while ", 1)[1].strip()
296
+ end_pc = self._get_block_end(pc)
297
+ if is_executing and self._evaluate_condition(cond):
298
+ control_stack.append({"type": "while", "executing": True, "start_pc": pc, "end_pc": end_pc})
299
+ pc += 1
300
+ else: pc = end_pc + 1
301
+ continue
302
+ elif line.startswith("for "):
303
+ parts = line.split("for ", 1)[1].split("->")
304
+ var_name = parts[0].strip()
305
+ start_v = int(self._compute(parts[1].split(" to ")[0].strip()))
306
+ end_v = int(self._compute(parts[1].split(" to ")[1].strip()))
307
+ end_pc = self._get_block_end(pc)
308
+ if is_executing and start_v <= end_v:
309
+ self._assign(var_name, start_v)
310
+ control_stack.append({"type": "for", "executing": True, "var": var_name, "end_val": end_v, "start_pc": pc, "end_pc": end_pc})
311
+ pc += 1
312
+ else: pc = end_pc + 1
313
+ continue
314
+ elif line == "end":
315
+ if control_stack:
316
+ top = control_stack.pop()
317
+ if top.get("type") == "while" and top["executing"]: pc = top["start_pc"] - 1
318
+ elif top.get("type") == "for" and top["executing"]:
319
+ curr = self._lookup(top["var"]) + 1
320
+ if curr <= top["end_val"]:
321
+ self._assign(top["var"], curr)
322
+ pc = top["start_pc"] - 1
323
+ pc += 1; continue
324
+ if is_executing:
325
+ if "?" in line:
326
+ cond, action = line.split("?", 1)
327
+ if self._evaluate_condition(cond.strip()): self._execute_line(action.strip())
328
+ else: self._execute_line(line)
329
+ pc += 1
330
+
331
+ def main():
332
+ engine = SolInterpreter()
333
+ if not sys.stdin.isatty():
334
+ code = sys.stdin.read()
335
+ elif len(sys.argv) > 1:
336
+ with open(sys.argv[1], "r") as f: code = f.read()
337
+ else:
338
+ print("Usage: solpl <file.sol> OR cat file.sol | python sol.py"); sys.exit(1)
339
+ engine.execute(code)
340
+
341
+ if __name__ == "__main__":
342
+ main()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "solpl"
7
- version = "2.2.0"
7
+ version = "2.3.0"
8
8
  description = "Sol Programming Language Interpreter"
9
9
  requires-python = ">=3.7"
10
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solpl
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Sol Programming Language Interpreter
5
5
  Requires-Python: >=3.7
6
6
  License-File: LICENSE
@@ -1,462 +0,0 @@
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()
File without changes
File without changes
File without changes
File without changes