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.
Files changed (51) hide show
  1. package/README.md +34 -6
  2. package/bin/zexus +12 -2
  3. package/bin/zpics +12 -2
  4. package/bin/zpm +12 -2
  5. package/bin/zx +12 -2
  6. package/bin/zx-deploy +12 -2
  7. package/bin/zx-dev +12 -2
  8. package/bin/zx-run +12 -2
  9. package/package.json +2 -1
  10. package/rust_core/Cargo.lock +603 -0
  11. package/rust_core/Cargo.toml +26 -0
  12. package/rust_core/README.md +15 -0
  13. package/rust_core/pyproject.toml +25 -0
  14. package/rust_core/src/binary_bytecode.rs +543 -0
  15. package/rust_core/src/contract_vm.rs +643 -0
  16. package/rust_core/src/executor.rs +847 -0
  17. package/rust_core/src/hasher.rs +90 -0
  18. package/rust_core/src/lib.rs +71 -0
  19. package/rust_core/src/merkle.rs +128 -0
  20. package/rust_core/src/rust_vm.rs +2313 -0
  21. package/rust_core/src/signature.rs +79 -0
  22. package/rust_core/src/state_adapter.rs +281 -0
  23. package/rust_core/src/validator.rs +116 -0
  24. package/scripts/postinstall.js +204 -21
  25. package/src/zexus/__init__.py +1 -1
  26. package/src/zexus/cli/main.py +1 -1
  27. package/src/zexus/cli/zpm.py +1 -1
  28. package/src/zexus/evaluator/bytecode_compiler.py +150 -52
  29. package/src/zexus/evaluator/core.py +151 -809
  30. package/src/zexus/evaluator/expressions.py +27 -22
  31. package/src/zexus/evaluator/functions.py +171 -126
  32. package/src/zexus/evaluator/statements.py +55 -112
  33. package/src/zexus/module_cache.py +20 -9
  34. package/src/zexus/object.py +330 -38
  35. package/src/zexus/parser/parser.py +103 -23
  36. package/src/zexus/parser/strategy_context.py +318 -6
  37. package/src/zexus/parser/strategy_structural.py +2 -2
  38. package/src/zexus/persistence.py +46 -17
  39. package/src/zexus/security.py +140 -234
  40. package/src/zexus/type_checker.py +44 -5
  41. package/src/zexus/vm/binary_bytecode.py +7 -3
  42. package/src/zexus/vm/bytecode.py +6 -0
  43. package/src/zexus/vm/cache.py +24 -46
  44. package/src/zexus/vm/compiler.py +549 -68
  45. package/src/zexus/vm/memory_pool.py +21 -9
  46. package/src/zexus/vm/vm.py +609 -95
  47. package/src/zexus/zpm/package_manager.py +1 -1
  48. package/src/zexus.egg-info/PKG-INFO +56 -12
  49. package/src/zexus.egg-info/SOURCES.txt +14 -0
  50. package/src/zexus.egg-info/entry_points.txt +5 -1
  51. package/src/zexus.egg-info/requires.txt +26 -0
@@ -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
- hints = {
145
- "UseStatement": "Module imports",
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
- self._emit(Opcode.JUMP_IF_FALSE, else_label)
380
-
381
- # Compile consequence
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
- # Else branch
386
- self._emit(Opcode.NOP) # Label placeholder
387
- self.instructions[-1] = (Opcode.NOP, else_label) # Mark label
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
- else:
392
- # Push null for else branch
393
- null_idx = self._add_constant(None)
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
- self._compile_node(node.argument)
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 body members
1081
+ # Compile properties and methods from EntityStatement's actual attributes
943
1082
  member_count = 0
944
- if hasattr(node.body, 'statements'):
945
- for stmt in node.body.statements:
946
- stmt_type = type(stmt).__name__
947
- # Reuse state/action compilation
948
- if stmt_type in ('StateStatement', 'ActionStatement'):
949
- if stmt_type == 'StateStatement':
950
- # Value
951
- if getattr(stmt, 'initial_value', None):
952
- self._compile_node(stmt.initial_value)
953
- else:
954
- self._emit(Opcode.LOAD_CONST, self._add_constant(None))
955
- # Name
956
- self._emit(Opcode.LOAD_CONST, self._add_constant(stmt.name.value))
957
- elif stmt_type == 'ActionStatement':
958
- self._compile_node(stmt)
959
- self._emit(Opcode.LOAD_NAME, self._add_constant(stmt.name.value))
960
- self._emit(Opcode.LOAD_CONST, self._add_constant(stmt.name.value))
961
-
962
- member_count += 1
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):