zexus 1.8.0 → 1.8.2
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 +34 -6
- package/bin/zexus +12 -2
- package/bin/zpics +12 -2
- package/bin/zpm +12 -2
- package/bin/zx +12 -2
- package/bin/zx-deploy +12 -2
- package/bin/zx-dev +12 -2
- package/bin/zx-run +12 -2
- package/package.json +2 -1
- package/rust_core/Cargo.lock +603 -0
- package/rust_core/Cargo.toml +26 -0
- package/rust_core/README.md +15 -0
- package/rust_core/pyproject.toml +25 -0
- package/rust_core/src/binary_bytecode.rs +543 -0
- package/rust_core/src/contract_vm.rs +643 -0
- package/rust_core/src/executor.rs +847 -0
- package/rust_core/src/hasher.rs +90 -0
- package/rust_core/src/lib.rs +71 -0
- package/rust_core/src/merkle.rs +128 -0
- package/rust_core/src/rust_vm.rs +2313 -0
- package/rust_core/src/signature.rs +79 -0
- package/rust_core/src/state_adapter.rs +281 -0
- package/rust_core/src/validator.rs +116 -0
- package/scripts/postinstall.js +204 -21
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/evaluator/bytecode_compiler.py +150 -52
- package/src/zexus/evaluator/core.py +151 -809
- package/src/zexus/evaluator/expressions.py +27 -22
- package/src/zexus/evaluator/functions.py +171 -126
- package/src/zexus/evaluator/statements.py +55 -112
- package/src/zexus/module_cache.py +20 -9
- package/src/zexus/object.py +330 -38
- package/src/zexus/parser/parser.py +103 -23
- package/src/zexus/parser/strategy_context.py +318 -6
- package/src/zexus/parser/strategy_structural.py +2 -2
- package/src/zexus/persistence.py +46 -17
- package/src/zexus/security.py +140 -234
- package/src/zexus/type_checker.py +44 -5
- package/src/zexus/vm/binary_bytecode.py +7 -3
- package/src/zexus/vm/bytecode.py +6 -0
- package/src/zexus/vm/cache.py +24 -46
- package/src/zexus/vm/compiler.py +549 -68
- package/src/zexus/vm/memory_pool.py +21 -9
- package/src/zexus/vm/vm.py +609 -95
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +56 -12
- package/src/zexus.egg-info/SOURCES.txt +14 -0
- package/src/zexus.egg-info/entry_points.txt +5 -1
- package/src/zexus.egg-info/requires.txt +26 -0
package/src/zexus/vm/compiler.py
CHANGED
|
@@ -67,9 +67,9 @@ class BytecodeCompiler:
|
|
|
67
67
|
if op == Opcode.NOP and isinstance(operand, str) and operand.startswith('L'):
|
|
68
68
|
labels[operand] = i
|
|
69
69
|
|
|
70
|
-
# 2. Update jumps
|
|
70
|
+
# 2. Update jumps (including SETUP_TRY which also uses label targets)
|
|
71
71
|
for i, (op, operand) in enumerate(self.instructions):
|
|
72
|
-
if op in (Opcode.JUMP, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE):
|
|
72
|
+
if op in (Opcode.JUMP, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE, Opcode.SETUP_TRY):
|
|
73
73
|
if isinstance(operand, str) and operand in labels:
|
|
74
74
|
self.instructions[i] = (op, labels[operand])
|
|
75
75
|
|
|
@@ -139,27 +139,24 @@ class BytecodeCompiler:
|
|
|
139
139
|
f"Failed to compile {node_type}: {exc}"
|
|
140
140
|
) from exc
|
|
141
141
|
|
|
142
|
+
def _emit_vm_builtin_call(self, builtin_name: str, payload, discard_result: bool = True) -> None:
|
|
143
|
+
"""Emit a VM-builtin call with the AST payload as a constant.
|
|
144
|
+
|
|
145
|
+
This is used to keep the VM bytecode compiler aligned with the evaluator
|
|
146
|
+
bytecode compiler for directive-like statements that the VM can delegate
|
|
147
|
+
to higher-level helpers when available.
|
|
148
|
+
"""
|
|
149
|
+
payload_idx = self._add_constant(payload)
|
|
150
|
+
self._emit(Opcode.LOAD_CONST, payload_idx)
|
|
151
|
+
name_idx = self._add_constant(builtin_name)
|
|
152
|
+
self._emit(Opcode.CALL_NAME, (name_idx, 1))
|
|
153
|
+
if discard_result:
|
|
154
|
+
self._emit(Opcode.POP)
|
|
155
|
+
|
|
142
156
|
def _unsupported_message(self, node_type: str) -> str:
|
|
143
157
|
"""Return a friendly message for unsupported nodes."""
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"FromStatement": "Module imports",
|
|
147
|
-
"EntityStatement": "Entity declarations",
|
|
148
|
-
"ContractStatement": "Contract declarations",
|
|
149
|
-
"ActionStatement": "Action/function declarations",
|
|
150
|
-
"FunctionStatement": "Function declarations",
|
|
151
|
-
"IfStatement": "If statements",
|
|
152
|
-
"WhileStatement": "While loops",
|
|
153
|
-
"ForStatement": "For loops",
|
|
154
|
-
"ForEachStatement": "For-each loops",
|
|
155
|
-
"TxStatement": "Transaction blocks",
|
|
156
|
-
"RequireStatement": "Require statements",
|
|
157
|
-
"RevertStatement": "Revert statements",
|
|
158
|
-
}
|
|
159
|
-
prefix = hints.get(node_type)
|
|
160
|
-
if prefix:
|
|
161
|
-
return f"{prefix} are not yet supported by the VM bytecode compiler"
|
|
162
|
-
return f"Node type '{node_type}' is not supported by the VM bytecode compiler"
|
|
158
|
+
msg = f"Node type '{node_type}' is not currently supported by the bytecode compiler."
|
|
159
|
+
return msg
|
|
163
160
|
|
|
164
161
|
# ==================== Program & Statements ====================
|
|
165
162
|
|
|
@@ -346,6 +343,9 @@ class BytecodeCompiler:
|
|
|
346
343
|
'>=': Opcode.GTE,
|
|
347
344
|
'&&': Opcode.AND,
|
|
348
345
|
'||': Opcode.OR,
|
|
346
|
+
'and': Opcode.AND,
|
|
347
|
+
'or': Opcode.OR,
|
|
348
|
+
'..': Opcode.ADD, # string concat alias
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
opcode = op_map.get(node.operator)
|
|
@@ -360,7 +360,7 @@ class BytecodeCompiler:
|
|
|
360
360
|
|
|
361
361
|
if node.operator == '-':
|
|
362
362
|
self._emit(Opcode.NEG)
|
|
363
|
-
elif node.operator
|
|
363
|
+
elif node.operator in ('!', 'not'):
|
|
364
364
|
self._emit(Opcode.NOT)
|
|
365
365
|
else:
|
|
366
366
|
raise NotImplementedError(f"Prefix operator {node.operator} not implemented")
|
|
@@ -368,35 +368,33 @@ class BytecodeCompiler:
|
|
|
368
368
|
# ==================== Control Flow ====================
|
|
369
369
|
|
|
370
370
|
def _compile_IfExpression(self, node):
|
|
371
|
-
"""Compile if/else statement"""
|
|
372
|
-
# Compile condition
|
|
373
|
-
self._compile_node(node.condition)
|
|
374
|
-
|
|
375
|
-
# Jump if false
|
|
376
|
-
else_label = self._make_label()
|
|
371
|
+
"""Compile if/elif/else statement"""
|
|
377
372
|
end_label = self._make_label()
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
373
|
+
|
|
374
|
+
# --- Main if ---
|
|
375
|
+
self._compile_node(node.condition)
|
|
376
|
+
next_label = self._make_label()
|
|
377
|
+
self._emit(Opcode.JUMP_IF_FALSE, next_label)
|
|
382
378
|
self._compile_node(node.consequence)
|
|
383
379
|
self._emit(Opcode.JUMP, end_label)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
380
|
+
self._mark_label(next_label)
|
|
381
|
+
|
|
382
|
+
# --- elif chains ---
|
|
383
|
+
elif_parts = getattr(node, 'elif_parts', None) or []
|
|
384
|
+
for elif_cond, elif_body in elif_parts:
|
|
385
|
+
self._compile_node(elif_cond)
|
|
386
|
+
next_elif_label = self._make_label()
|
|
387
|
+
self._emit(Opcode.JUMP_IF_FALSE, next_elif_label)
|
|
388
|
+
self._compile_node(elif_body)
|
|
389
|
+
self._emit(Opcode.JUMP, end_label)
|
|
390
|
+
self._mark_label(next_elif_label)
|
|
391
|
+
|
|
392
|
+
# --- else ---
|
|
389
393
|
if node.alternative:
|
|
390
394
|
self._compile_node(node.alternative)
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
self._emit(Opcode.LOAD_CONST, null_idx)
|
|
395
|
-
|
|
396
|
-
# End label
|
|
397
|
-
self._emit(Opcode.NOP)
|
|
398
|
-
self.instructions[-1] = (Opcode.NOP, end_label)
|
|
399
|
-
|
|
395
|
+
|
|
396
|
+
self._mark_label(end_label)
|
|
397
|
+
|
|
400
398
|
_compile_IfStatement = _compile_IfExpression
|
|
401
399
|
|
|
402
400
|
def _compile_TernaryExpression(self, node):
|
|
@@ -494,7 +492,84 @@ class BytecodeCompiler:
|
|
|
494
492
|
self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
|
|
495
493
|
|
|
496
494
|
self.loop_stack.pop()
|
|
497
|
-
|
|
495
|
+
|
|
496
|
+
def _compile_ForEachStatement(self, node):
|
|
497
|
+
"""Compile for-each loop: for item in iterable { ... }
|
|
498
|
+
|
|
499
|
+
Strategy: index-based iteration using GET_LENGTH + INDEX.
|
|
500
|
+
Generates equivalent of:
|
|
501
|
+
_iter = <iterable>
|
|
502
|
+
_index = 0
|
|
503
|
+
loop_start:
|
|
504
|
+
if _index >= len(_iter): goto end
|
|
505
|
+
item = _iter[_index]
|
|
506
|
+
<body>
|
|
507
|
+
continue_label:
|
|
508
|
+
_index = _index + 1
|
|
509
|
+
goto loop_start
|
|
510
|
+
end:
|
|
511
|
+
"""
|
|
512
|
+
# --- Compile iterable and store in a hidden temp ---
|
|
513
|
+
self._compile_node(node.iterable)
|
|
514
|
+
iter_var = self._make_temp_name("iter")
|
|
515
|
+
iter_const = self._add_constant(iter_var)
|
|
516
|
+
self._emit(Opcode.STORE_NAME, iter_const)
|
|
517
|
+
|
|
518
|
+
# --- Initialise index = 0 ---
|
|
519
|
+
index_var = self._make_temp_name("idx")
|
|
520
|
+
index_const = self._add_constant(index_var)
|
|
521
|
+
zero_const = self._add_constant(0)
|
|
522
|
+
self._emit(Opcode.LOAD_CONST, zero_const)
|
|
523
|
+
self._emit(Opcode.STORE_NAME, index_const)
|
|
524
|
+
|
|
525
|
+
# --- Labels ---
|
|
526
|
+
start_label = self._make_label()
|
|
527
|
+
end_label = self._make_label()
|
|
528
|
+
continue_label = self._make_label()
|
|
529
|
+
|
|
530
|
+
self.loop_stack.append({
|
|
531
|
+
'start': start_label,
|
|
532
|
+
'end': end_label,
|
|
533
|
+
'continue': continue_label,
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
# --- Condition: index < len(iterable) ---
|
|
537
|
+
self._mark_label(start_label)
|
|
538
|
+
self._emit(Opcode.LOAD_NAME, index_const) # push index
|
|
539
|
+
self._emit(Opcode.LOAD_NAME, iter_const) # push iterable
|
|
540
|
+
self.instructions.append(("GET_LENGTH", None)) # push len(iterable)
|
|
541
|
+
self._emit(Opcode.LT) # index < length
|
|
542
|
+
exit_jump_idx = len(self.instructions)
|
|
543
|
+
self._emit(Opcode.JUMP_IF_FALSE, None) # jump to end if false
|
|
544
|
+
|
|
545
|
+
# --- Extract item: item = iterable[index] ---
|
|
546
|
+
self._emit(Opcode.LOAD_NAME, iter_const) # push iterable
|
|
547
|
+
self._emit(Opcode.LOAD_NAME, index_const) # push index
|
|
548
|
+
self._emit(Opcode.INDEX) # iterable[index]
|
|
549
|
+
item_name = node.item.value if hasattr(node.item, 'value') else str(node.item)
|
|
550
|
+
item_const = self._add_constant(item_name)
|
|
551
|
+
self._emit(Opcode.STORE_NAME, item_const) # store as loop variable
|
|
552
|
+
|
|
553
|
+
# --- Body ---
|
|
554
|
+
self._compile_node(node.body)
|
|
555
|
+
|
|
556
|
+
# --- Continue: index += 1 ---
|
|
557
|
+
self._mark_label(continue_label)
|
|
558
|
+
one_const = self._add_constant(1)
|
|
559
|
+
self._emit(Opcode.LOAD_NAME, index_const)
|
|
560
|
+
self._emit(Opcode.LOAD_CONST, one_const)
|
|
561
|
+
self._emit(Opcode.ADD)
|
|
562
|
+
self._emit(Opcode.STORE_NAME, index_const)
|
|
563
|
+
|
|
564
|
+
# --- Jump back ---
|
|
565
|
+
self._emit(Opcode.JUMP, start_label)
|
|
566
|
+
|
|
567
|
+
# --- End ---
|
|
568
|
+
self._mark_label(end_label)
|
|
569
|
+
self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
|
|
570
|
+
|
|
571
|
+
self.loop_stack.pop()
|
|
572
|
+
|
|
498
573
|
# ==================== Function Calls ====================
|
|
499
574
|
|
|
500
575
|
def _compile_CallExpression(self, node):
|
|
@@ -639,6 +714,37 @@ class BytecodeCompiler:
|
|
|
639
714
|
_compile_FunctionStatement = _compile_ActionStatement
|
|
640
715
|
_compile_PureFunctionStatement = _compile_ActionStatement
|
|
641
716
|
|
|
717
|
+
def _compile_LambdaExpression(self, node):
|
|
718
|
+
"""Compile lambda/anonymous function expression.
|
|
719
|
+
|
|
720
|
+
Lambda differs from ActionStatement: it has no name and its value should
|
|
721
|
+
be *pushed* onto the stack (not stored) so it can be used inline, e.g.
|
|
722
|
+
as an argument to a higher-order function.
|
|
723
|
+
"""
|
|
724
|
+
from .bytecode import Bytecode
|
|
725
|
+
|
|
726
|
+
func_compiler = self.__class__()
|
|
727
|
+
func_compiler._compile_node(node.body)
|
|
728
|
+
|
|
729
|
+
# Implicit return of last expression value
|
|
730
|
+
null_idx = func_compiler._add_constant(None)
|
|
731
|
+
func_compiler._emit(Opcode.LOAD_CONST, null_idx)
|
|
732
|
+
func_compiler._emit(Opcode.RETURN)
|
|
733
|
+
|
|
734
|
+
func_compiler._resolve_labels()
|
|
735
|
+
func_bytecode = Bytecode(func_compiler.instructions, func_compiler.constants)
|
|
736
|
+
|
|
737
|
+
params = [p.value if hasattr(p, 'value') else str(p) for p in node.parameters]
|
|
738
|
+
func_desc = {
|
|
739
|
+
"bytecode": func_bytecode,
|
|
740
|
+
"params": params,
|
|
741
|
+
"is_async": False,
|
|
742
|
+
"name": "<lambda>",
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
func_idx = self._add_constant(func_desc)
|
|
746
|
+
self._emit(Opcode.LOAD_CONST, func_idx)
|
|
747
|
+
|
|
642
748
|
def _compile_ConstStatement(self, node):
|
|
643
749
|
"""Compile const declaration (same as Let for current VM)"""
|
|
644
750
|
self._compile_LetStatement(node)
|
|
@@ -801,6 +907,38 @@ class BytecodeCompiler:
|
|
|
801
907
|
self._emit(Opcode.CALL_NAME, (gc_idx, 1))
|
|
802
908
|
self._emit(Opcode.POP)
|
|
803
909
|
|
|
910
|
+
# ------------------------------------------------------------------
|
|
911
|
+
# Directive-like statements (delegate to VM helpers when available)
|
|
912
|
+
# ------------------------------------------------------------------
|
|
913
|
+
|
|
914
|
+
def _compile_NativeStatement(self, node):
|
|
915
|
+
"""Compile native statement via VM helper (if provided)."""
|
|
916
|
+
self._emit_vm_builtin_call("__vm_native_statement__", node, discard_result=False)
|
|
917
|
+
|
|
918
|
+
def _compile_InlineStatement(self, node):
|
|
919
|
+
"""Compile inline optimization directive via VM helper."""
|
|
920
|
+
self._emit_vm_builtin_call("__vm_inline_statement__", node, discard_result=False)
|
|
921
|
+
|
|
922
|
+
def _compile_BufferStatement(self, node):
|
|
923
|
+
"""Compile buffer directive via VM helper."""
|
|
924
|
+
self._emit_vm_builtin_call("__vm_buffer_statement__", node, discard_result=False)
|
|
925
|
+
|
|
926
|
+
def _compile_SIMDStatement(self, node):
|
|
927
|
+
"""Compile SIMD directive via VM helper."""
|
|
928
|
+
self._emit_vm_builtin_call("__vm_simd_statement__", node, discard_result=False)
|
|
929
|
+
|
|
930
|
+
def _compile_DeferStatement(self, node):
|
|
931
|
+
"""Compile defer directive via VM helper."""
|
|
932
|
+
self._emit_vm_builtin_call("__vm_defer_statement__", node, discard_result=False)
|
|
933
|
+
|
|
934
|
+
def _compile_EmitStatement(self, node):
|
|
935
|
+
"""Compile emit statement via VM helper."""
|
|
936
|
+
self._emit_vm_builtin_call("__vm_emit_statement__", node, discard_result=False)
|
|
937
|
+
|
|
938
|
+
def _compile_ProtocolStatement(self, node):
|
|
939
|
+
"""Compile protocol statement via VM helper."""
|
|
940
|
+
self._emit_vm_builtin_call("__vm_protocol_statement__", node, discard_result=False)
|
|
941
|
+
|
|
804
942
|
|
|
805
943
|
def _compile_ContractStatement(self, node):
|
|
806
944
|
"""Compile smart contract definition into bytecode that creates a real
|
|
@@ -921,7 +1059,8 @@ class BytecodeCompiler:
|
|
|
921
1059
|
|
|
922
1060
|
def _compile_AwaitExpression(self, node):
|
|
923
1061
|
"""Compile await expression"""
|
|
924
|
-
|
|
1062
|
+
expr = getattr(node, 'expression', None) or getattr(node, 'argument', None)
|
|
1063
|
+
self._compile_node(expr)
|
|
925
1064
|
self._emit(Opcode.AWAIT)
|
|
926
1065
|
|
|
927
1066
|
def _compile_ThisExpression(self, node):
|
|
@@ -939,31 +1078,45 @@ class BytecodeCompiler:
|
|
|
939
1078
|
# Push name
|
|
940
1079
|
self._emit(Opcode.LOAD_CONST, name_idx)
|
|
941
1080
|
|
|
942
|
-
# Compile
|
|
1081
|
+
# Compile properties and methods from EntityStatement's actual attributes
|
|
943
1082
|
member_count = 0
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1083
|
+
|
|
1084
|
+
# Compile properties (list of dicts with 'name', 'type', 'default_value')
|
|
1085
|
+
for prop in (node.properties or []):
|
|
1086
|
+
prop_name = prop.get('name', '') if isinstance(prop, dict) else getattr(prop, 'name', '')
|
|
1087
|
+
default_val = prop.get('default_value', None) if isinstance(prop, dict) else getattr(prop, 'default_value', None)
|
|
1088
|
+
|
|
1089
|
+
if default_val is not None:
|
|
1090
|
+
self._compile_node(default_val)
|
|
1091
|
+
else:
|
|
1092
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
1093
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(prop_name))
|
|
1094
|
+
member_count += 1
|
|
1095
|
+
|
|
1096
|
+
# Compile methods (list of ActionStatement)
|
|
1097
|
+
for method in (node.methods or []):
|
|
1098
|
+
self._compile_node(method)
|
|
1099
|
+
method_name = method.name.value if hasattr(method.name, 'value') else str(method.name)
|
|
1100
|
+
self._emit(Opcode.LOAD_NAME, self._add_constant(method_name))
|
|
1101
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(method_name))
|
|
1102
|
+
member_count += 1
|
|
963
1103
|
|
|
964
1104
|
self._emit(Opcode.DEFINE_ENTITY, member_count)
|
|
965
1105
|
self._emit(Opcode.STORE_NAME, name_idx)
|
|
966
1106
|
|
|
1107
|
+
def _compile_ExportStatement(self, node):
|
|
1108
|
+
"""Compile export statement - marks names for module export.
|
|
1109
|
+
|
|
1110
|
+
Export is a declaration/annotation; the exported names are already
|
|
1111
|
+
defined by their own declarations, so this just emits EXPORT opcodes
|
|
1112
|
+
to register them in the module's export table.
|
|
1113
|
+
"""
|
|
1114
|
+
for name_node in (node.names or []):
|
|
1115
|
+
name = name_node.value if hasattr(name_node, 'value') else str(name_node)
|
|
1116
|
+
name_idx = self._add_constant(name)
|
|
1117
|
+
self._emit(Opcode.LOAD_NAME, name_idx)
|
|
1118
|
+
self._emit(Opcode.EXPORT, name_idx)
|
|
1119
|
+
|
|
967
1120
|
def _compile_DataStatement(self, node):
|
|
968
1121
|
"""Compile Data/Dataclass definition"""
|
|
969
1122
|
# Data objects are simpler entities
|
|
@@ -1146,6 +1299,334 @@ class BytecodeCompiler:
|
|
|
1146
1299
|
# Alias MatchExpression to logic (it's similar enough for basic cases)
|
|
1147
1300
|
_compile_MatchExpression = _compile_PatternStatement
|
|
1148
1301
|
|
|
1302
|
+
# ==================== Debug / Verify / Watch ====================
|
|
1303
|
+
|
|
1304
|
+
def _compile_DebugStatement(self, node):
|
|
1305
|
+
"""Compile debug statement — prints the value (conditional on condition if present)."""
|
|
1306
|
+
if node.condition:
|
|
1307
|
+
skip_label = self._make_label()
|
|
1308
|
+
self._compile_node(node.condition)
|
|
1309
|
+
self._emit(Opcode.JUMP_IF_FALSE, skip_label)
|
|
1310
|
+
self._compile_node(node.value)
|
|
1311
|
+
self._emit(Opcode.PRINT)
|
|
1312
|
+
end_label = self._make_label()
|
|
1313
|
+
self._emit(Opcode.JUMP, end_label)
|
|
1314
|
+
self._mark_label(skip_label)
|
|
1315
|
+
self._mark_label(end_label)
|
|
1316
|
+
else:
|
|
1317
|
+
self._compile_node(node.value)
|
|
1318
|
+
self._emit(Opcode.PRINT)
|
|
1319
|
+
|
|
1320
|
+
def _compile_VerifyStatement(self, node):
|
|
1321
|
+
"""Compile verify statement.
|
|
1322
|
+
|
|
1323
|
+
Simple assertion form: verify condition, "message"
|
|
1324
|
+
Compiles to: if (!condition) { require(false, message) }
|
|
1325
|
+
Complex forms: delegate to the evaluator via VM-builtin call.
|
|
1326
|
+
"""
|
|
1327
|
+
if node.condition and not node.mode:
|
|
1328
|
+
# Simple assertion: verify cond, msg
|
|
1329
|
+
self._compile_node(node.condition)
|
|
1330
|
+
ok_label = self._make_label()
|
|
1331
|
+
self._emit(Opcode.JUMP_IF_TRUE, ok_label)
|
|
1332
|
+
# Verification failed — emit require(false, message)
|
|
1333
|
+
false_idx = self._add_constant(False)
|
|
1334
|
+
self._emit(Opcode.LOAD_CONST, false_idx)
|
|
1335
|
+
if node.message:
|
|
1336
|
+
self._compile_node(node.message)
|
|
1337
|
+
else:
|
|
1338
|
+
msg_idx = self._add_constant("Verification failed")
|
|
1339
|
+
self._emit(Opcode.LOAD_CONST, msg_idx)
|
|
1340
|
+
self._emit(Opcode.REQUIRE)
|
|
1341
|
+
self._mark_label(ok_label)
|
|
1342
|
+
else:
|
|
1343
|
+
# Complex form — delegate to higher-level evaluator
|
|
1344
|
+
self._emit_vm_builtin_call("__verify__", node)
|
|
1345
|
+
|
|
1346
|
+
def _compile_WatchStatement(self, node):
|
|
1347
|
+
"""Compile watch statement — reactive state management.
|
|
1348
|
+
|
|
1349
|
+
In a bytecode context, watch compiles the reaction body inline.
|
|
1350
|
+
Full reactivity tracking requires runtime support; here we compile the
|
|
1351
|
+
body so it can at least execute once (eager evaluation).
|
|
1352
|
+
"""
|
|
1353
|
+
if node.watched_expr:
|
|
1354
|
+
self._compile_node(node.watched_expr)
|
|
1355
|
+
self._emit(Opcode.POP)
|
|
1356
|
+
self._compile_node(node.reaction)
|
|
1357
|
+
|
|
1358
|
+
def _compile_NullishExpression(self, node):
|
|
1359
|
+
"""Compile nullish coalescing: left ?? right"""
|
|
1360
|
+
self._compile_node(node.left)
|
|
1361
|
+
self._emit(Opcode.DUP)
|
|
1362
|
+
# If not null, skip to end
|
|
1363
|
+
end_label = self._make_label()
|
|
1364
|
+
null_idx = self._add_constant(None)
|
|
1365
|
+
self._emit(Opcode.LOAD_CONST, null_idx)
|
|
1366
|
+
self._emit(Opcode.NEQ)
|
|
1367
|
+
self._emit(Opcode.JUMP_IF_TRUE, end_label)
|
|
1368
|
+
# Left was null, pop it and use right
|
|
1369
|
+
self._emit(Opcode.POP)
|
|
1370
|
+
self._compile_node(node.right)
|
|
1371
|
+
self._mark_label(end_label)
|
|
1372
|
+
|
|
1373
|
+
def _compile_ThrowStatement(self, node):
|
|
1374
|
+
"""Compile throw statement"""
|
|
1375
|
+
expr = getattr(node, 'message', None) or getattr(node, 'expression', None)
|
|
1376
|
+
if expr:
|
|
1377
|
+
self._compile_node(expr)
|
|
1378
|
+
else:
|
|
1379
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant("Unknown error"))
|
|
1380
|
+
self._emit(Opcode.THROW)
|
|
1381
|
+
|
|
1382
|
+
def _compile_EnumStatement(self, node):
|
|
1383
|
+
"""Compile enum declaration"""
|
|
1384
|
+
self._emit_vm_builtin_call("__define_enum__", node)
|
|
1385
|
+
|
|
1386
|
+
def _compile_StringInterpolationExpression(self, node):
|
|
1387
|
+
"""Compile string interpolation: `hello {name}`
|
|
1388
|
+
|
|
1389
|
+
Compiles each part and concatenates with ADD.
|
|
1390
|
+
"""
|
|
1391
|
+
parts = node.parts if hasattr(node, 'parts') else []
|
|
1392
|
+
if not parts:
|
|
1393
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(""))
|
|
1394
|
+
return
|
|
1395
|
+
|
|
1396
|
+
self._compile_node(parts[0])
|
|
1397
|
+
for part in parts[1:]:
|
|
1398
|
+
self._compile_node(part)
|
|
1399
|
+
self._emit(Opcode.ADD)
|
|
1400
|
+
|
|
1401
|
+
# ==================== ActionLiteral / AsyncExpression / ProtectStatement ====================
|
|
1402
|
+
|
|
1403
|
+
def _compile_ActionLiteral(self, node):
|
|
1404
|
+
"""Compile action literal (anonymous function expression).
|
|
1405
|
+
|
|
1406
|
+
ActionLiteral is semantically identical to LambdaExpression — an
|
|
1407
|
+
anonymous function with parameters and a body. It pushes a function
|
|
1408
|
+
descriptor onto the stack so it can be passed as an argument or
|
|
1409
|
+
assigned to a variable.
|
|
1410
|
+
"""
|
|
1411
|
+
from .bytecode import Bytecode
|
|
1412
|
+
|
|
1413
|
+
func_compiler = self.__class__()
|
|
1414
|
+
func_compiler._compile_node(node.body)
|
|
1415
|
+
|
|
1416
|
+
# Implicit return null if execution flows off the end
|
|
1417
|
+
null_idx = func_compiler._add_constant(None)
|
|
1418
|
+
func_compiler._emit(Opcode.LOAD_CONST, null_idx)
|
|
1419
|
+
func_compiler._emit(Opcode.RETURN)
|
|
1420
|
+
|
|
1421
|
+
func_compiler._resolve_labels()
|
|
1422
|
+
func_bytecode = Bytecode(func_compiler.instructions, func_compiler.constants)
|
|
1423
|
+
|
|
1424
|
+
params = [p.value if hasattr(p, 'value') else str(p) for p in node.parameters]
|
|
1425
|
+
func_desc = {
|
|
1426
|
+
"bytecode": func_bytecode,
|
|
1427
|
+
"params": params,
|
|
1428
|
+
"is_async": False,
|
|
1429
|
+
"name": "<action>",
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
func_idx = self._add_constant(func_desc)
|
|
1433
|
+
self._emit(Opcode.LOAD_CONST, func_idx)
|
|
1434
|
+
|
|
1435
|
+
def _compile_AsyncExpression(self, node):
|
|
1436
|
+
"""Compile async expression: ``async <expr>``
|
|
1437
|
+
|
|
1438
|
+
Compiles the inner expression, then emits SPAWN to schedule it
|
|
1439
|
+
asynchronously. The resulting task handle is left on the stack.
|
|
1440
|
+
"""
|
|
1441
|
+
self._compile_node(node.expression)
|
|
1442
|
+
self._emit(Opcode.SPAWN)
|
|
1443
|
+
|
|
1444
|
+
def _compile_ProtectStatement(self, node):
|
|
1445
|
+
"""Compile protect() security guardrail.
|
|
1446
|
+
|
|
1447
|
+
protect(target, rules) is a high-level security directive.
|
|
1448
|
+
We compile target and rules, then call the VM builtin __protect__.
|
|
1449
|
+
If the builtin is not registered at runtime the VM will silently
|
|
1450
|
+
skip (same behaviour as NativeStatement etc.).
|
|
1451
|
+
"""
|
|
1452
|
+
# Compile target
|
|
1453
|
+
self._compile_node(node.target)
|
|
1454
|
+
# Compile rules
|
|
1455
|
+
self._compile_node(node.rules)
|
|
1456
|
+
# Call builtin
|
|
1457
|
+
name_idx = self._add_constant("__protect__")
|
|
1458
|
+
self._emit(Opcode.CALL_NAME, (name_idx, 2))
|
|
1459
|
+
self._emit(Opcode.POP)
|
|
1460
|
+
|
|
1461
|
+
# ==================== Additional commonly-used nodes ====================
|
|
1462
|
+
|
|
1463
|
+
def _compile_SandboxStatement(self, node):
|
|
1464
|
+
"""Compile sandbox statement — wraps body in an isolated scope."""
|
|
1465
|
+
self._compile_node(node.body)
|
|
1466
|
+
|
|
1467
|
+
def _compile_MiddlewareStatement(self, node):
|
|
1468
|
+
"""Compile middleware registration."""
|
|
1469
|
+
self._emit_vm_builtin_call("__middleware__", node)
|
|
1470
|
+
|
|
1471
|
+
def _compile_AuthStatement(self, node):
|
|
1472
|
+
"""Compile auth statement."""
|
|
1473
|
+
self._emit_vm_builtin_call("__auth__", node)
|
|
1474
|
+
|
|
1475
|
+
def _compile_ThrottleStatement(self, node):
|
|
1476
|
+
"""Compile throttle statement."""
|
|
1477
|
+
self._emit_vm_builtin_call("__throttle__", node)
|
|
1478
|
+
|
|
1479
|
+
def _compile_CacheStatement(self, node):
|
|
1480
|
+
"""Compile cache statement."""
|
|
1481
|
+
self._emit_vm_builtin_call("__cache__", node)
|
|
1482
|
+
|
|
1483
|
+
def _compile_SealStatement(self, node):
|
|
1484
|
+
"""Compile seal statement."""
|
|
1485
|
+
self._emit_vm_builtin_call("__seal__", node)
|
|
1486
|
+
|
|
1487
|
+
def _compile_StreamStatement(self, node):
|
|
1488
|
+
"""Compile stream statement."""
|
|
1489
|
+
self._emit_vm_builtin_call("__stream__", node)
|
|
1490
|
+
|
|
1491
|
+
def _compile_LogStatement(self, node):
|
|
1492
|
+
"""Compile log statement — same as print."""
|
|
1493
|
+
if hasattr(node, 'message'):
|
|
1494
|
+
self._compile_node(node.message)
|
|
1495
|
+
elif hasattr(node, 'value'):
|
|
1496
|
+
self._compile_node(node.value)
|
|
1497
|
+
elif hasattr(node, 'expression'):
|
|
1498
|
+
self._compile_node(node.expression)
|
|
1499
|
+
else:
|
|
1500
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(""))
|
|
1501
|
+
self._emit(Opcode.PRINT)
|
|
1502
|
+
|
|
1503
|
+
def _compile_ImmutableStatement(self, node):
|
|
1504
|
+
"""Compile immutable declaration — treat as const."""
|
|
1505
|
+
self._compile_LetStatement(node)
|
|
1506
|
+
|
|
1507
|
+
def _compile_ValidateStatement(self, node):
|
|
1508
|
+
"""Compile validate statement."""
|
|
1509
|
+
self._emit_vm_builtin_call("__validate__", node)
|
|
1510
|
+
|
|
1511
|
+
def _compile_SanitizeStatement(self, node):
|
|
1512
|
+
"""Compile sanitize statement."""
|
|
1513
|
+
self._emit_vm_builtin_call("__sanitize__", node)
|
|
1514
|
+
|
|
1515
|
+
def _compile_InjectStatement(self, node):
|
|
1516
|
+
"""Compile inject statement (dependency injection)."""
|
|
1517
|
+
self._emit_vm_builtin_call("__inject__", node)
|
|
1518
|
+
|
|
1519
|
+
def _compile_InterfaceStatement(self, node):
|
|
1520
|
+
"""Compile interface statement — define type contract."""
|
|
1521
|
+
self._emit_vm_builtin_call("__interface__", node)
|
|
1522
|
+
|
|
1523
|
+
def _compile_TypeAliasStatement(self, node):
|
|
1524
|
+
"""Compile type alias — no-op at runtime."""
|
|
1525
|
+
pass
|
|
1526
|
+
|
|
1527
|
+
def _compile_ModuleStatement(self, node):
|
|
1528
|
+
"""Compile module statement."""
|
|
1529
|
+
if hasattr(node, 'body'):
|
|
1530
|
+
self._compile_node(node.body)
|
|
1531
|
+
|
|
1532
|
+
def _compile_PackageStatement(self, node):
|
|
1533
|
+
"""Compile package statement."""
|
|
1534
|
+
if hasattr(node, 'body'):
|
|
1535
|
+
self._compile_node(node.body)
|
|
1536
|
+
|
|
1537
|
+
def _compile_TrailStatement(self, node):
|
|
1538
|
+
"""Compile trail/audit-trail statement."""
|
|
1539
|
+
self._emit_vm_builtin_call("__trail__", node)
|
|
1540
|
+
|
|
1541
|
+
def _compile_EmbeddedCodeStatement(self, node):
|
|
1542
|
+
"""Compile embedded code — no-op in VM (Python-only)."""
|
|
1543
|
+
pass
|
|
1544
|
+
|
|
1545
|
+
def _compile_EmbeddedLiteral(self, node):
|
|
1546
|
+
"""Compile embedded literal — push as string constant."""
|
|
1547
|
+
code = getattr(node, 'code', '') or getattr(node, 'value', '') or ''
|
|
1548
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(code))
|
|
1549
|
+
|
|
1550
|
+
def _compile_ExternalDeclaration(self, node):
|
|
1551
|
+
"""Compile external declaration — no-op (type hint only)."""
|
|
1552
|
+
pass
|
|
1553
|
+
|
|
1554
|
+
def _compile_ImportLogStatement(self, node):
|
|
1555
|
+
"""Compile import log statement — no-op at compile time."""
|
|
1556
|
+
pass
|
|
1557
|
+
|
|
1558
|
+
def _compile_ExactlyStatement(self, node):
|
|
1559
|
+
"""Compile exactly statement (type-exact check)."""
|
|
1560
|
+
self._emit_vm_builtin_call("__exactly__", node)
|
|
1561
|
+
|
|
1562
|
+
def _compile_FindExpression(self, node):
|
|
1563
|
+
"""Compile find expression."""
|
|
1564
|
+
self._emit_vm_builtin_call("__find__", node, discard_result=False)
|
|
1565
|
+
|
|
1566
|
+
def _compile_LoadExpression(self, node):
|
|
1567
|
+
"""Compile load expression."""
|
|
1568
|
+
self._emit_vm_builtin_call("__load__", node, discard_result=False)
|
|
1569
|
+
|
|
1570
|
+
def _compile_DestructurePattern(self, node):
|
|
1571
|
+
"""Compile destructure pattern — push null (placeholder)."""
|
|
1572
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
1573
|
+
|
|
1574
|
+
def _compile_AtomicStatement(self, node):
|
|
1575
|
+
"""Compile atomic statement — executes body/expr as a single unit.
|
|
1576
|
+
|
|
1577
|
+
In the current VM, atomic simply compiles the inner body or expression
|
|
1578
|
+
directly. True atomicity would require runtime locking which the VM
|
|
1579
|
+
can add later.
|
|
1580
|
+
"""
|
|
1581
|
+
if node.body:
|
|
1582
|
+
self._compile_node(node.body)
|
|
1583
|
+
elif node.expr:
|
|
1584
|
+
self._compile_node(node.expr)
|
|
1585
|
+
self._emit(Opcode.POP)
|
|
1586
|
+
|
|
1587
|
+
# ==================== Channel / Concurrency statements ====================
|
|
1588
|
+
|
|
1589
|
+
def _compile_ChannelStatement(self, node):
|
|
1590
|
+
"""Compile channel declaration: channel<T>[capacity] name
|
|
1591
|
+
|
|
1592
|
+
Emits a call to the ``__create_channel__`` VM builtin which creates a
|
|
1593
|
+
:class:`Channel` object and stores it in the environment.
|
|
1594
|
+
"""
|
|
1595
|
+
# Push channel name
|
|
1596
|
+
ch_name = node.name.value if hasattr(node.name, 'value') else str(node.name)
|
|
1597
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(ch_name))
|
|
1598
|
+
|
|
1599
|
+
# Push element type (string or None)
|
|
1600
|
+
elem_type = None
|
|
1601
|
+
if hasattr(node, 'element_type') and node.element_type is not None:
|
|
1602
|
+
elem_type = node.element_type.value if hasattr(node.element_type, 'value') else str(node.element_type)
|
|
1603
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(elem_type))
|
|
1604
|
+
|
|
1605
|
+
# Push capacity
|
|
1606
|
+
if hasattr(node, 'capacity') and node.capacity is not None:
|
|
1607
|
+
self._compile_node(node.capacity)
|
|
1608
|
+
else:
|
|
1609
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(0))
|
|
1610
|
+
|
|
1611
|
+
# Call __create_channel__(name, element_type, capacity)
|
|
1612
|
+
fn_idx = self._add_constant("__create_channel__")
|
|
1613
|
+
self._emit(Opcode.CALL_NAME, (fn_idx, 3))
|
|
1614
|
+
self._emit(Opcode.POP) # discard result (channel stored internally)
|
|
1615
|
+
|
|
1616
|
+
def _compile_SendStatement(self, node):
|
|
1617
|
+
"""Compile send statement: send(channel, value)"""
|
|
1618
|
+
self._compile_node(node.channel_expr)
|
|
1619
|
+
self._compile_node(node.value_expr)
|
|
1620
|
+
fn_idx = self._add_constant("send")
|
|
1621
|
+
self._emit(Opcode.CALL_NAME, (fn_idx, 2))
|
|
1622
|
+
self._emit(Opcode.POP)
|
|
1623
|
+
|
|
1624
|
+
def _compile_ReceiveStatement(self, node):
|
|
1625
|
+
"""Compile receive statement: value = receive(channel)"""
|
|
1626
|
+
self._compile_node(node.channel_expr)
|
|
1627
|
+
fn_idx = self._add_constant("receive")
|
|
1628
|
+
self._emit(Opcode.CALL_NAME, (fn_idx, 1))
|
|
1629
|
+
|
|
1149
1630
|
# ==================== Fallback for unsupported nodes ====================
|
|
1150
1631
|
|
|
1151
1632
|
def __getattr__(self, name):
|