zexus 1.8.2 → 1.8.3
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.
- package/README.md +89 -64
- package/package.json +1 -1
- package/rust_core/Cargo.lock +1 -1
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/builtin_modules.py +50 -13
- package/src/zexus/cli/main.py +46 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/evaluator/bytecode_compiler.py +11 -2
- package/src/zexus/evaluator/core.py +4 -1
- package/src/zexus/evaluator/expressions.py +11 -2
- package/src/zexus/evaluator/functions.py +72 -0
- package/src/zexus/evaluator/resource_limiter.py +1 -1
- package/src/zexus/evaluator/statements.py +44 -4
- package/src/zexus/kernel/__init__.py +34 -0
- package/src/zexus/kernel/hooks.py +276 -0
- package/src/zexus/kernel/registry.py +203 -0
- package/src/zexus/kernel/zir/__init__.py +145 -0
- package/src/zexus/lexer.py +7 -0
- package/src/zexus/object.py +28 -5
- package/src/zexus/parser/parser.py +53 -11
- package/src/zexus/parser/strategy_context.py +179 -10
- package/src/zexus/security.py +26 -2
- package/src/zexus/stdlib/blockchain.py +84 -0
- package/src/zexus/stdlib/http_server.py +2 -2
- package/src/zexus/stdlib/math.py +25 -17
- package/src/zexus/stdlib_integration.py +119 -2
- package/src/zexus/type_checker.py +17 -12
- package/src/zexus/vm/compiler.py +57 -6
- package/src/zexus/vm/fastops.c +4704 -1263
- package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/fastops.pyx +81 -3
- package/src/zexus/vm/optimizer.py +65 -27
- package/src/zexus/vm/vm.py +871 -98
- package/src/zexus/zexus_ast.py +4 -1
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +90 -65
- package/src/zexus.egg-info/SOURCES.txt +51 -0
|
Binary file
|
package/src/zexus/vm/fastops.pyx
CHANGED
|
@@ -148,7 +148,12 @@ def execute(list instrs, list consts, dict env, dict builtins, dict closure_cell
|
|
|
148
148
|
a = a.value
|
|
149
149
|
if hasattr(b, "value"):
|
|
150
150
|
b = b.value
|
|
151
|
-
|
|
151
|
+
if b == 0:
|
|
152
|
+
raise ZeroDivisionError("Division by zero")
|
|
153
|
+
elif isinstance(a, int) and not isinstance(a, bool) and isinstance(b, int) and not isinstance(b, bool):
|
|
154
|
+
stack.append(a // b if a % b == 0 else a / b)
|
|
155
|
+
else:
|
|
156
|
+
stack.append(a / b)
|
|
152
157
|
elif op_name == "MOD":
|
|
153
158
|
b = stack.pop() if stack else 1
|
|
154
159
|
a = stack.pop() if stack else 0
|
|
@@ -197,10 +202,14 @@ def execute(list instrs, list consts, dict env, dict builtins, dict closure_cell
|
|
|
197
202
|
stack.append(elements)
|
|
198
203
|
elif op_name == "BUILD_MAP":
|
|
199
204
|
count = operand if operand is not None else 0
|
|
200
|
-
|
|
205
|
+
pairs = []
|
|
201
206
|
for _ in range(count):
|
|
202
207
|
val = stack.pop(); key = stack.pop()
|
|
203
|
-
|
|
208
|
+
pairs.append((key, val))
|
|
209
|
+
pairs.reverse()
|
|
210
|
+
result = {}
|
|
211
|
+
for k, v in pairs:
|
|
212
|
+
result[k] = v
|
|
204
213
|
stack.append(result)
|
|
205
214
|
elif op_name == "BUILD_SET":
|
|
206
215
|
count = operand if operand is not None else 0
|
|
@@ -214,10 +223,79 @@ def execute(list instrs, list consts, dict env, dict builtins, dict closure_cell
|
|
|
214
223
|
stack.append(obj.get(idx))
|
|
215
224
|
elif isinstance(obj, ZMap):
|
|
216
225
|
stack.append(obj.get(idx))
|
|
226
|
+
elif hasattr(obj, 'data') and hasattr(obj, 'entity_def'):
|
|
227
|
+
idx_str = idx.value if hasattr(idx, 'value') else str(idx) if idx is not None else None
|
|
228
|
+
if idx_str:
|
|
229
|
+
val = obj.get(idx_str)
|
|
230
|
+
if hasattr(val, 'value'):
|
|
231
|
+
stack.append(val.value)
|
|
232
|
+
else:
|
|
233
|
+
stack.append(val)
|
|
234
|
+
else:
|
|
235
|
+
stack.append(None)
|
|
236
|
+
elif isinstance(obj, dict):
|
|
237
|
+
raw_idx = idx.value if hasattr(idx, 'value') else idx
|
|
238
|
+
if raw_idx in obj:
|
|
239
|
+
stack.append(obj[raw_idx])
|
|
240
|
+
elif isinstance(raw_idx, int) and not isinstance(raw_idx, bool):
|
|
241
|
+
keys = list(obj.keys())
|
|
242
|
+
if 0 <= raw_idx < len(keys):
|
|
243
|
+
stack.append(obj[keys[raw_idx]])
|
|
244
|
+
else:
|
|
245
|
+
stack.append(None)
|
|
246
|
+
else:
|
|
247
|
+
stack.append(None)
|
|
217
248
|
else:
|
|
218
249
|
stack.append(obj[idx] if obj is not None else None)
|
|
219
250
|
except Exception:
|
|
220
251
|
stack.append(None)
|
|
252
|
+
elif op_name == "FOR_ITER":
|
|
253
|
+
var_count = operand if operand else 1
|
|
254
|
+
idx = stack.pop() if stack else 0
|
|
255
|
+
obj = stack.pop() if stack else None
|
|
256
|
+
try:
|
|
257
|
+
if isinstance(obj, dict):
|
|
258
|
+
keys = list(obj.keys())
|
|
259
|
+
if isinstance(idx, int) and 0 <= idx < len(keys):
|
|
260
|
+
key = keys[idx]
|
|
261
|
+
if var_count == 2:
|
|
262
|
+
stack.append(key)
|
|
263
|
+
stack.append(obj[key])
|
|
264
|
+
else:
|
|
265
|
+
stack.append(key)
|
|
266
|
+
else:
|
|
267
|
+
for _ in range(var_count):
|
|
268
|
+
stack.append(None)
|
|
269
|
+
elif isinstance(obj, ZMap):
|
|
270
|
+
keys = list(obj.pairs.keys())
|
|
271
|
+
if isinstance(idx, int) and 0 <= idx < len(keys):
|
|
272
|
+
key = keys[idx]
|
|
273
|
+
if var_count == 2:
|
|
274
|
+
stack.append(key)
|
|
275
|
+
stack.append(obj.pairs[key])
|
|
276
|
+
else:
|
|
277
|
+
stack.append(key)
|
|
278
|
+
else:
|
|
279
|
+
for _ in range(var_count):
|
|
280
|
+
stack.append(None)
|
|
281
|
+
elif isinstance(obj, (list, ZList)):
|
|
282
|
+
elems = obj.elements if isinstance(obj, ZList) else obj
|
|
283
|
+
if isinstance(idx, int) and 0 <= idx < len(elems):
|
|
284
|
+
if var_count == 2:
|
|
285
|
+
stack.append(idx)
|
|
286
|
+
stack.append(elems[idx])
|
|
287
|
+
else:
|
|
288
|
+
stack.append(elems[idx])
|
|
289
|
+
else:
|
|
290
|
+
for _ in range(var_count):
|
|
291
|
+
stack.append(None)
|
|
292
|
+
else:
|
|
293
|
+
raise NotImplementedError("FOR_ITER target type not supported")
|
|
294
|
+
except NotImplementedError:
|
|
295
|
+
raise
|
|
296
|
+
except Exception:
|
|
297
|
+
for _ in range(var_count):
|
|
298
|
+
stack.append(None)
|
|
221
299
|
elif op_name == "SLICE":
|
|
222
300
|
end = stack.pop() if stack else None
|
|
223
301
|
start = stack.pop() if stack else None
|
|
@@ -81,6 +81,9 @@ class BytecodeOptimizer:
|
|
|
81
81
|
"JUMP_BACKWARD",
|
|
82
82
|
"LABEL",
|
|
83
83
|
"JUMP_TARGET",
|
|
84
|
+
"SETUP_TRY",
|
|
85
|
+
"POP_TRY",
|
|
86
|
+
"THROW",
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
def _has_control_flow(self, instructions: List[Tuple[str, Any]]) -> bool:
|
|
@@ -94,9 +97,14 @@ class BytecodeOptimizer:
|
|
|
94
97
|
"""Validate that jump targets are sane and within bounds."""
|
|
95
98
|
max_index = len(instructions) - 1
|
|
96
99
|
for idx, (op, operand) in enumerate(instructions):
|
|
97
|
-
if op in ("JUMP", "JUMP_IF_FALSE", "JUMP_IF_TRUE", "JUMP_FORWARD", "JUMP_BACKWARD"):
|
|
100
|
+
if op in ("JUMP", "JUMP_IF_FALSE", "JUMP_IF_TRUE", "JUMP_FORWARD", "JUMP_BACKWARD", "SETUP_TRY"):
|
|
98
101
|
if not isinstance(operand, int):
|
|
99
|
-
|
|
102
|
+
# SETUP_TRY and others require an int target
|
|
103
|
+
if op == "SETUP_TRY":
|
|
104
|
+
return False
|
|
105
|
+
if op in ("JUMP", "JUMP_IF_FALSE", "JUMP_IF_TRUE"):
|
|
106
|
+
return False
|
|
107
|
+
continue
|
|
100
108
|
if operand < 0 or operand > max_index:
|
|
101
109
|
return False
|
|
102
110
|
if op == "JUMP" and operand == idx:
|
|
@@ -404,8 +412,9 @@ class BytecodeOptimizer:
|
|
|
404
412
|
|
|
405
413
|
def _dead_code_elimination(self, instructions: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]:
|
|
406
414
|
"""
|
|
407
|
-
Remove unreachable code after RETURN, unconditional JUMP, etc.
|
|
415
|
+
Remove unreachable code after RETURN, unconditional JUMP, THROW, etc.
|
|
408
416
|
Jump targets are remapped after dead code removal to keep control flow valid.
|
|
417
|
+
SETUP_TRY handler targets are treated as jump targets (catch blocks are reachable).
|
|
409
418
|
"""
|
|
410
419
|
result = []
|
|
411
420
|
in_dead_code = False
|
|
@@ -413,14 +422,24 @@ class BytecodeOptimizer:
|
|
|
413
422
|
removed_indices: Set[int] = set()
|
|
414
423
|
|
|
415
424
|
for idx, (op, operand) in enumerate(instructions):
|
|
425
|
+
# Standard jumps
|
|
416
426
|
if op in ("JUMP", "JUMP_IF_FALSE", "JUMP_IF_TRUE", "JUMP_FORWARD", "JUMP_BACKWARD"):
|
|
417
427
|
if isinstance(operand, int):
|
|
418
428
|
jump_targets.add(operand)
|
|
429
|
+
# SETUP_TRY handler addresses are also jump targets (catch blocks)
|
|
430
|
+
elif op == "SETUP_TRY":
|
|
431
|
+
if isinstance(operand, int):
|
|
432
|
+
jump_targets.add(operand)
|
|
419
433
|
|
|
420
434
|
for idx, (op, operand) in enumerate(instructions):
|
|
421
435
|
if in_dead_code:
|
|
422
|
-
#
|
|
423
|
-
|
|
436
|
+
# Stop skipping at jump targets, labels, or NOP labels
|
|
437
|
+
is_label = (
|
|
438
|
+
op in ("LABEL", "JUMP_TARGET")
|
|
439
|
+
or idx in jump_targets
|
|
440
|
+
or (op == "NOP" and isinstance(operand, str) and operand.startswith("L"))
|
|
441
|
+
)
|
|
442
|
+
if is_label:
|
|
424
443
|
in_dead_code = False
|
|
425
444
|
result.append((op, operand))
|
|
426
445
|
else:
|
|
@@ -429,7 +448,7 @@ class BytecodeOptimizer:
|
|
|
429
448
|
else:
|
|
430
449
|
result.append((op, operand))
|
|
431
450
|
# Mark dead code after unconditional control flow
|
|
432
|
-
if op in ("RETURN", "JUMP"):
|
|
451
|
+
if op in ("RETURN", "JUMP", "THROW"):
|
|
433
452
|
in_dead_code = True
|
|
434
453
|
|
|
435
454
|
# Remap jump targets after dead code removal
|
|
@@ -441,7 +460,8 @@ class BytecodeOptimizer:
|
|
|
441
460
|
index_map[old_idx] = new_idx
|
|
442
461
|
new_idx += 1
|
|
443
462
|
|
|
444
|
-
|
|
463
|
+
# Include SETUP_TRY in jump remapping — its operand is a handler address
|
|
464
|
+
jump_ops = ("JUMP", "JUMP_IF_FALSE", "JUMP_IF_TRUE", "JUMP_FORWARD", "JUMP_BACKWARD", "SETUP_TRY")
|
|
445
465
|
remapped = []
|
|
446
466
|
for op, operand in result:
|
|
447
467
|
if op in jump_ops and isinstance(operand, int) and operand in index_map:
|
|
@@ -569,40 +589,58 @@ class BytecodeOptimizer:
|
|
|
569
589
|
|
|
570
590
|
def _strength_reduction(self, instructions: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]:
|
|
571
591
|
"""
|
|
572
|
-
Replace expensive operations with cheaper equivalents
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
592
|
+
Replace expensive operations with cheaper equivalents.
|
|
593
|
+
|
|
594
|
+
Patterns recognised (where *val* comes from ``LOAD_CONST``):
|
|
595
|
+
``val * 2`` → ``val + val`` (addition cheaper than multiplication)
|
|
596
|
+
``val ** 2`` → ``val * val`` (avoid exponentiation)
|
|
597
|
+
|
|
598
|
+
Only fires when the constant is a literal ``2`` so that the
|
|
599
|
+
semantics are bit-identical for integers.
|
|
578
600
|
"""
|
|
579
601
|
result = []
|
|
580
602
|
i = 0
|
|
581
|
-
|
|
603
|
+
|
|
582
604
|
while i < len(instructions):
|
|
583
|
-
#
|
|
605
|
+
# We need a 3-instruction window: LOAD_x LOAD_CONST OP
|
|
584
606
|
if i + 2 < len(instructions):
|
|
585
607
|
op1, operand1 = instructions[i]
|
|
586
608
|
op2, operand2 = instructions[i + 1]
|
|
587
|
-
op3,
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
609
|
+
op3, _ = instructions[i + 2]
|
|
610
|
+
|
|
611
|
+
if op2 == "LOAD_CONST" and operand2 == 2:
|
|
612
|
+
# x * 2 → x + x (DUP + ADD)
|
|
613
|
+
if op3 == "MUL":
|
|
614
|
+
result.append(instructions[i]) # LOAD_x
|
|
615
|
+
result.append(("DUP", None))
|
|
616
|
+
result.append(("ADD", None))
|
|
617
|
+
self.stats.strength_reductions += 1
|
|
618
|
+
i += 3
|
|
619
|
+
continue
|
|
620
|
+
|
|
621
|
+
# x ** 2 → x * x (DUP + MUL)
|
|
622
|
+
if op3 == "POW":
|
|
623
|
+
result.append(instructions[i]) # LOAD_x
|
|
624
|
+
result.append(("DUP", None))
|
|
625
|
+
result.append(("MUL", None))
|
|
626
|
+
self.stats.strength_reductions += 1
|
|
627
|
+
i += 3
|
|
628
|
+
continue
|
|
629
|
+
|
|
594
630
|
result.append(instructions[i])
|
|
595
631
|
i += 1
|
|
596
|
-
|
|
632
|
+
|
|
597
633
|
return result
|
|
598
634
|
|
|
599
635
|
def _loop_invariant_code_motion(self, instructions: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]:
|
|
600
636
|
"""
|
|
601
|
-
Move loop-invariant computations outside loops
|
|
602
|
-
|
|
603
|
-
This is experimental and requires loop detection
|
|
637
|
+
Move loop-invariant computations outside loops.
|
|
638
|
+
|
|
639
|
+
This is experimental and requires loop detection (back-edge
|
|
640
|
+
analysis). Currently a **no-op** — the instruction list is
|
|
641
|
+
returned unchanged. Gated behind ``level >= 3`` so it never
|
|
642
|
+
fires unless explicitly requested.
|
|
604
643
|
"""
|
|
605
|
-
# Placeholder for future implementation
|
|
606
644
|
return instructions
|
|
607
645
|
|
|
608
646
|
def get_stats(self) -> Dict[str, Any]:
|