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