zexus 1.6.8 → 1.7.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 (177) hide show
  1. package/README.md +12 -5
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/capability_system.py +184 -9
  45. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  46. package/src/zexus/cli/main.py +383 -34
  47. package/src/zexus/cli/zpm.py +1 -1
  48. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  53. package/src/zexus/compiler/bytecode.py +124 -7
  54. package/src/zexus/compiler/compat_runtime.py +6 -2
  55. package/src/zexus/compiler/lexer.py +16 -5
  56. package/src/zexus/compiler/parser.py +108 -7
  57. package/src/zexus/compiler/semantic.py +18 -19
  58. package/src/zexus/compiler/zexus_ast.py +26 -1
  59. package/src/zexus/concurrency_system.py +79 -0
  60. package/src/zexus/config.py +54 -0
  61. package/src/zexus/crypto_bridge.py +244 -8
  62. package/src/zexus/dap/__init__.py +10 -0
  63. package/src/zexus/dap/__main__.py +4 -0
  64. package/src/zexus/dap/dap_server.py +391 -0
  65. package/src/zexus/dap/debug_engine.py +298 -0
  66. package/src/zexus/environment.py +112 -9
  67. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  70. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  71. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  72. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  73. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  74. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  75. package/src/zexus/evaluator/bytecode_compiler.py +457 -37
  76. package/src/zexus/evaluator/core.py +644 -50
  77. package/src/zexus/evaluator/expressions.py +358 -62
  78. package/src/zexus/evaluator/functions.py +458 -20
  79. package/src/zexus/evaluator/resource_limiter.py +4 -4
  80. package/src/zexus/evaluator/statements.py +774 -122
  81. package/src/zexus/evaluator/unified_execution.py +573 -72
  82. package/src/zexus/evaluator/utils.py +14 -2
  83. package/src/zexus/evaluator_original.py +1 -1
  84. package/src/zexus/event_loop.py +186 -0
  85. package/src/zexus/lexer.py +742 -458
  86. package/src/zexus/lsp/__init__.py +1 -1
  87. package/src/zexus/lsp/definition_provider.py +163 -9
  88. package/src/zexus/lsp/server.py +22 -8
  89. package/src/zexus/lsp/symbol_provider.py +182 -9
  90. package/src/zexus/module_cache.py +239 -9
  91. package/src/zexus/module_manager.py +129 -1
  92. package/src/zexus/object.py +76 -6
  93. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  94. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  95. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  96. package/src/zexus/parser/parser.py +1349 -408
  97. package/src/zexus/parser/strategy_context.py +755 -58
  98. package/src/zexus/parser/strategy_structural.py +121 -21
  99. package/src/zexus/persistence.py +15 -1
  100. package/src/zexus/renderer/__init__.py +61 -0
  101. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  103. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  104. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  105. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  106. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  107. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  108. package/src/zexus/renderer/backend.py +261 -0
  109. package/src/zexus/renderer/canvas.py +78 -0
  110. package/src/zexus/renderer/color_system.py +201 -0
  111. package/src/zexus/renderer/graphics.py +31 -0
  112. package/src/zexus/renderer/layout.py +222 -0
  113. package/src/zexus/renderer/main_renderer.py +66 -0
  114. package/src/zexus/renderer/painter.py +30 -0
  115. package/src/zexus/renderer/tk_backend.py +208 -0
  116. package/src/zexus/renderer/web_backend.py +260 -0
  117. package/src/zexus/runtime/__init__.py +10 -2
  118. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  120. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  121. package/src/zexus/runtime/file_flags.py +137 -0
  122. package/src/zexus/runtime/load_manager.py +368 -0
  123. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  125. package/src/zexus/security.py +424 -34
  126. package/src/zexus/stdlib/fs.py +23 -18
  127. package/src/zexus/stdlib/http.py +289 -186
  128. package/src/zexus/stdlib/sockets.py +207 -163
  129. package/src/zexus/stdlib/websockets.py +282 -0
  130. package/src/zexus/stdlib_integration.py +369 -2
  131. package/src/zexus/strategy_recovery.py +6 -3
  132. package/src/zexus/type_checker.py +423 -0
  133. package/src/zexus/virtual_filesystem.py +189 -2
  134. package/src/zexus/vm/__init__.py +113 -3
  135. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  136. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  137. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  138. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  139. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  140. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  141. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  142. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  143. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  144. package/src/zexus/vm/async_optimizer.py +80 -6
  145. package/src/zexus/vm/binary_bytecode.py +659 -0
  146. package/src/zexus/vm/bytecode.py +59 -11
  147. package/src/zexus/vm/bytecode_converter.py +26 -12
  148. package/src/zexus/vm/cabi.c +1985 -0
  149. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  150. package/src/zexus/vm/cabi.h +127 -0
  151. package/src/zexus/vm/cache.py +561 -17
  152. package/src/zexus/vm/compiler.py +818 -51
  153. package/src/zexus/vm/fastops.c +15743 -0
  154. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  155. package/src/zexus/vm/fastops.pyx +288 -0
  156. package/src/zexus/vm/gas_metering.py +50 -9
  157. package/src/zexus/vm/jit.py +364 -20
  158. package/src/zexus/vm/native_jit_backend.py +1816 -0
  159. package/src/zexus/vm/native_runtime.cpp +1388 -0
  160. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  161. package/src/zexus/vm/optimizer.py +161 -11
  162. package/src/zexus/vm/parallel_vm.py +140 -45
  163. package/src/zexus/vm/peephole_optimizer.py +82 -4
  164. package/src/zexus/vm/profiler.py +38 -18
  165. package/src/zexus/vm/register_allocator.py +16 -5
  166. package/src/zexus/vm/register_vm.py +8 -5
  167. package/src/zexus/vm/vm.py +3581 -531
  168. package/src/zexus/vm/wasm_compiler.py +658 -0
  169. package/src/zexus/zexus_ast.py +137 -11
  170. package/src/zexus/zexus_token.py +16 -5
  171. package/src/zexus/zpm/installer.py +55 -15
  172. package/src/zexus/zpm/package_manager.py +1 -1
  173. package/src/zexus/zpm/registry.py +257 -28
  174. package/src/zexus.egg-info/PKG-INFO +16 -6
  175. package/src/zexus.egg-info/SOURCES.txt +129 -17
  176. package/src/zexus.egg-info/entry_points.txt +1 -0
  177. package/src/zexus.egg-info/requires.txt +4 -0
@@ -8,7 +8,8 @@ from ..zexus_ast import (
8
8
  TryCatchStatement, UseStatement, FromStatement, ExportStatement,
9
9
  ContractStatement, EntityStatement, VerifyStatement, ProtectStatement,
10
10
  SealStatement, MiddlewareStatement, AuthStatement, ThrottleStatement, CacheStatement,
11
- ComponentStatement, ThemeStatement, DebugStatement, ExternalDeclaration, AssignmentExpression,
11
+ ComponentStatement, ThemeStatement, ColorStatement, CanvasStatement, GraphicsStatement,
12
+ AnimationStatement, ClockStatement, DebugStatement, ExternalDeclaration, AssignmentExpression,
12
13
  PrintStatement, ScreenStatement, EmbeddedCodeStatement, ExactlyStatement,
13
14
  Identifier, PropertyAccessExpression, RestrictStatement, SandboxStatement, TrailStatement,
14
15
  NativeStatement, GCStatement, InlineStatement, BufferStatement, SIMDStatement,
@@ -30,6 +31,24 @@ from ..security import (
30
31
  ProtectionPolicy, Middleware, AuthConfig, RateLimiter, CachePolicy
31
32
  )
32
33
  from .utils import is_error, debug_log, EVAL_SUMMARY, NULL, TRUE, FALSE, _resolve_awaitable, _zexus_to_python, _python_to_zexus, is_truthy
34
+ from ..config import config as zexus_config
35
+
36
+ try:
37
+ from ..renderer import (
38
+ create_named_canvas as renderer_create_named_canvas,
39
+ define_color as renderer_define_color,
40
+ register_animation as renderer_register_animation,
41
+ register_clock as renderer_register_clock,
42
+ register_graphics as renderer_register_graphics,
43
+ )
44
+ _RENDERER_AVAILABLE = True
45
+ except Exception: # pragma: no cover - renderer optional during bootstrap
46
+ renderer_create_named_canvas = None
47
+ renderer_define_color = None
48
+ renderer_register_animation = None
49
+ renderer_register_clock = None
50
+ renderer_register_graphics = None
51
+ _RENDERER_AVAILABLE = False
33
52
 
34
53
  # Break exception for loop control flow
35
54
  class BreakException:
@@ -37,9 +56,77 @@ class BreakException:
37
56
  def __repr__(self):
38
57
  return "BreakException()"
39
58
 
59
+ class ContinueException:
60
+ """Exception raised when continue statement is encountered in a loop."""
61
+ def __repr__(self):
62
+ return "ContinueException()"
63
+
64
+
40
65
  class StatementEvaluatorMixin:
41
66
  """Handles evaluation of statements, flow control, module loading, and security features."""
42
67
 
68
+ def _statement_signature(self, stmt):
69
+ """Create a stable, cheap signature for tolerant duplicate-skipping.
70
+
71
+ The tolerant parser/recovery can sometimes enqueue duplicate statements.
72
+ For long files, calling ``str(stmt)`` for every statement can become
73
+ expensive because AST __repr__ often includes nested expressions.
74
+ Prefer source location (line/column) when available.
75
+ """
76
+ stmt_type = type(stmt).__name__
77
+ line = int(getattr(stmt, 'line', 0) or 0)
78
+ column = int(getattr(stmt, 'column', 0) or 0)
79
+
80
+ if line or column:
81
+ extra = ""
82
+ try:
83
+ if isinstance(stmt, ExpressionStatement):
84
+ expr = getattr(stmt, "expression", None)
85
+ func = getattr(expr, "function", None)
86
+ if isinstance(func, Identifier) and getattr(func, "value", None):
87
+ extra = f":{func.value}"
88
+ except Exception:
89
+ extra = ""
90
+ return f"{stmt_type}@{line}:{column}{extra}"
91
+
92
+ # Fallback: retain old behavior for nodes without location metadata.
93
+ try:
94
+ return str(stmt)
95
+ except Exception:
96
+ return f"{stmt_type}@{id(stmt)}"
97
+
98
+ def _enqueue_tolerant_duplicates(self, statements):
99
+ if not statements:
100
+ return
101
+
102
+ counts = getattr(self, "_tolerant_skip_counts", None)
103
+ if counts is None:
104
+ counts = {}
105
+ self._tolerant_skip_counts = counts
106
+
107
+ local_counts = {}
108
+ for stmt in statements:
109
+ if isinstance(stmt, ExpressionStatement):
110
+ expr = getattr(stmt, "expression", None)
111
+ func = getattr(expr, "function", None)
112
+ if (
113
+ isinstance(func, Identifier)
114
+ and getattr(func, "value", None) == "revert"
115
+ ):
116
+ continue
117
+ sig = self._statement_signature(stmt)
118
+ local_counts[sig] = local_counts.get(sig, 0) + 1
119
+
120
+ duplicates_added = 0
121
+ for sig, occurrence in local_counts.items():
122
+ if occurrence > 1:
123
+ counts[sig] = counts.get(sig, 0) + (occurrence - 1)
124
+ duplicates_added += occurrence - 1
125
+
126
+ if duplicates_added:
127
+ if zexus_config.should_log('debug'):
128
+ print(f"[TOLERANT] enqueue {duplicates_added} duplicate statements")
129
+
43
130
  def ceval_program(self, statements, env):
44
131
  debug_log("eval_program", f"Processing {len(statements)} statements")
45
132
 
@@ -55,6 +142,23 @@ class StatementEvaluatorMixin:
55
142
  try:
56
143
  for i, stmt in enumerate(statements):
57
144
  debug_log(f" Statement {i+1}", type(stmt).__name__)
145
+ counts = getattr(self, "_tolerant_skip_counts", None)
146
+ if counts:
147
+ signature = self._statement_signature(stmt)
148
+ remaining = counts.get(signature, 0)
149
+ if remaining > 0:
150
+ if zexus_config.should_log('debug'):
151
+ print(f"[TOLERANT] skipping program stmt {type(stmt).__name__} sig={signature} remaining={remaining}")
152
+ if remaining == 1:
153
+ counts.pop(signature, None)
154
+ else:
155
+ counts[signature] = remaining - 1
156
+ continue
157
+ if (
158
+ getattr(self, "_pending_revert_signature", None) is not None
159
+ and not isinstance(stmt, RevertStatement)
160
+ ):
161
+ self._pending_revert_signature = None
58
162
  res = self.eval_node(stmt, env)
59
163
  res = _resolve_awaitable(res)
60
164
  EVAL_SUMMARY['evaluated_statements'] += 1
@@ -64,6 +168,14 @@ class StatementEvaluatorMixin:
64
168
  # Execute deferred cleanup before returning
65
169
  self._execute_deferred_cleanup(env, [])
66
170
  return res.value
171
+
172
+ # Check for ContinueException (Top-Level = Enable Error Recovery)
173
+ if isinstance(res, ContinueException):
174
+ self.continue_on_error = True
175
+ debug_log("ceval_program", "Enabling error recovery mode via continue")
176
+ result = NULL
177
+ continue
178
+
67
179
  if is_error(res):
68
180
  debug_log(" Error encountered", res)
69
181
  try:
@@ -76,7 +188,8 @@ class StatementEvaluatorMixin:
76
188
  # Log the error and continue execution
77
189
  error_msg = str(res)
78
190
  self.error_log.append(error_msg)
79
- print(f"[ERROR] {error_msg}")
191
+ if zexus_config.should_log('error'):
192
+ print(f"[ERROR] {error_msg}")
80
193
  debug_log(" Continuing after error", "continue_on_error=True")
81
194
  result = NULL # Continue with null result
82
195
  continue
@@ -106,11 +219,23 @@ class StatementEvaluatorMixin:
106
219
  result = NULL
107
220
  try:
108
221
  for stmt in block.statements:
222
+ counts = getattr(self, "_tolerant_skip_counts", None)
223
+ if counts:
224
+ signature = self._statement_signature(stmt)
225
+ remaining = counts.get(signature, 0)
226
+ if remaining > 0:
227
+ if zexus_config.should_log('debug'):
228
+ print(f"[TOLERANT] skipping block stmt {type(stmt).__name__} sig={signature} remaining={remaining}")
229
+ if remaining == 1:
230
+ counts.pop(signature, None)
231
+ else:
232
+ counts[signature] = remaining - 1
233
+ continue
109
234
  res = self.eval_node(stmt, env, stack_trace)
110
235
  res = _resolve_awaitable(res)
111
236
  EVAL_SUMMARY['evaluated_statements'] += 1
112
237
 
113
- if isinstance(res, (ReturnValue, BreakException, EvaluationError)):
238
+ if isinstance(res, (ReturnValue, BreakException, ContinueException, EvaluationError)):
114
239
  debug_log(" Block interrupted", res)
115
240
  if is_error(res):
116
241
  try:
@@ -123,7 +248,8 @@ class StatementEvaluatorMixin:
123
248
  # Log the error and continue execution
124
249
  error_msg = str(res)
125
250
  self.error_log.append(error_msg)
126
- print(f"[ERROR] {error_msg}")
251
+ if zexus_config.should_log('error'):
252
+ print(f"[ERROR] {error_msg}")
127
253
  debug_log(" Continuing after error in block", "continue_on_error=True")
128
254
  result = NULL # Continue with null result
129
255
  continue
@@ -142,23 +268,124 @@ class StatementEvaluatorMixin:
142
268
  self._execute_deferred_cleanup(env, stack_trace)
143
269
  # Restore stdout to previous state (scope-aware)
144
270
  self._restore_stdout(env)
145
-
271
+
272
+ # ------------------------------------------------------------------
273
+ # Renderer helpers
274
+ # ------------------------------------------------------------------
275
+ def _evaluate_renderer_block(self, block, env, stack_trace):
276
+ local_env = Environment(outer=env)
277
+ result = self.eval_block_statement(block, local_env, list(stack_trace) if stack_trace else [])
278
+
279
+ if isinstance(result, ReturnValue):
280
+ result = result.value
281
+ if is_error(result):
282
+ return result
283
+
284
+ collected = {}
285
+ for key, value in local_env.store.items():
286
+ if key.startswith("__"):
287
+ continue
288
+ collected[key] = value
289
+ return collected
290
+
291
+ def _evaluate_renderer_properties(self, prop_node, env, stack_trace):
292
+ if prop_node is None:
293
+ return None
294
+
295
+ if isinstance(prop_node, list):
296
+ values = []
297
+ for expr in prop_node:
298
+ val = self.eval_node(expr, env, stack_trace)
299
+ val = _resolve_awaitable(val)
300
+ if isinstance(val, ReturnValue):
301
+ val = val.value
302
+ if is_error(val):
303
+ return val
304
+ values.append(_zexus_to_python(val))
305
+ return values
306
+
307
+ if isinstance(prop_node, BlockStatement):
308
+ bindings = self._evaluate_renderer_block(prop_node, env, stack_trace)
309
+ if is_error(bindings):
310
+ return bindings
311
+ return {key: _zexus_to_python(val) for key, val in bindings.items()}
312
+
313
+ val = self.eval_node(prop_node, env, stack_trace)
314
+ val = _resolve_awaitable(val)
315
+ if isinstance(val, ReturnValue):
316
+ val = val.value
317
+ if is_error(val):
318
+ return val
319
+ return _zexus_to_python(val)
320
+
146
321
  def eval_expression_statement(self, node, env, stack_trace):
147
322
  # Debug: Check if expression is being evaluated
148
323
  if hasattr(node.expression, 'function') and hasattr(node.expression.function, 'value'):
149
324
  func_name = node.expression.function.value
150
325
  if func_name in ['persist_set', 'persist_get']:
151
- print(f"[EVAL_EXPR_STMT] Evaluating {func_name} call", flush=True)
326
+ debug_log("eval_expression_statement", f"Evaluating {func_name} call", level='info')
152
327
  result = self.eval_node(node.expression, env, stack_trace)
153
328
  if hasattr(node.expression, 'function') and hasattr(node.expression.function, 'value'):
154
329
  func_name = node.expression.function.value
155
330
  if func_name in ['persist_set', 'persist_get']:
156
- print(f"[EVAL_EXPR_STMT] Result from {func_name}: {result}", flush=True)
331
+ debug_log("eval_expression_statement", f"Result from {func_name}: {result}", level='info')
157
332
  return result
158
333
 
159
334
  # === VARIABLE & CONTROL FLOW ===
160
335
 
336
+ def _eval_destructure(self, pattern, value, env, stack_trace):
337
+ """Bind variables from a DestructurePattern against a runtime value.
338
+
339
+ let {a, b} = {"a": 1, "b": 2} -> env.a=1, env.b=2
340
+ let [x, y] = [10, 20] -> env.x=10, env.y=20
341
+ """
342
+ from ..zexus_ast import DestructurePattern
343
+ if pattern.kind == 'map':
344
+ # Value must be a Map or dict-like
345
+ if not hasattr(value, 'pairs'):
346
+ return EvaluationError(
347
+ f"Cannot destructure {type(value).__name__} as map — expected a Map"
348
+ )
349
+ pairs = value.pairs
350
+ for source_key, target_name in pattern.bindings:
351
+ # Map keys are stored as strings
352
+ val = pairs.get(source_key)
353
+ if val is None:
354
+ # Try with String wrapper
355
+ val = pairs.get(f'"{source_key}"')
356
+ if val is None:
357
+ val = NULL
358
+ env.set(target_name, val)
359
+ elif pattern.kind == 'list':
360
+ # Value must be a List with .elements
361
+ if not hasattr(value, 'elements'):
362
+ return EvaluationError(
363
+ f"Cannot destructure {type(value).__name__} as list — expected a List"
364
+ )
365
+ elements = value.elements
366
+ for idx, target_name in pattern.bindings:
367
+ if idx < len(elements):
368
+ env.set(target_name, elements[idx])
369
+ else:
370
+ env.set(target_name, NULL)
371
+ # Handle rest element
372
+ if pattern.rest:
373
+ rest_start = len(pattern.bindings)
374
+ from ..object import List as ListObj
375
+ env.set(pattern.rest, ListObj(elements[rest_start:]))
376
+ return NULL
377
+
161
378
  def eval_let_statement(self, node, env, stack_trace):
379
+ from ..zexus_ast import DestructurePattern
380
+
381
+ # Handle destructuring: let {a, b} = expr or let [x, y] = expr
382
+ if isinstance(node.name, DestructurePattern):
383
+ debug_log("eval_let_statement", f"let destructure ({node.name.kind})")
384
+ value = self.eval_node(node.value, env, stack_trace)
385
+ if is_error(value):
386
+ return value
387
+ return self._eval_destructure(node.name, value, env, stack_trace)
388
+
162
389
  debug_log("eval_let_statement", f"let {node.name.value}")
163
390
 
164
391
  # FIXED: Evaluate value FIRST to prevent recursion issues
@@ -213,6 +440,16 @@ class StatementEvaluatorMixin:
213
440
  return value_type in expected_types
214
441
 
215
442
  def eval_const_statement(self, node, env, stack_trace):
443
+ from ..zexus_ast import DestructurePattern
444
+
445
+ # Handle destructuring: const {a, b} = expr or const [x, y] = expr
446
+ if isinstance(node.name, DestructurePattern):
447
+ debug_log("eval_const_statement", f"const destructure ({node.name.kind})")
448
+ value = self.eval_node(node.value, env, stack_trace)
449
+ if is_error(value):
450
+ return value
451
+ return self._eval_destructure(node.name, value, env, stack_trace)
452
+
216
453
  debug_log("eval_const_statement", f"const {node.name.value}")
217
454
 
218
455
  # Evaluate value FIRST
@@ -745,10 +982,9 @@ class StatementEvaluatorMixin:
745
982
  return ReturnValue(val)
746
983
 
747
984
  def eval_continue_statement(self, node, env, stack_trace):
748
- """Enable continue-on-error mode for the evaluator."""
749
- debug_log("eval_continue_statement", "Enabling error recovery mode")
750
- self.continue_on_error = True
751
- return NULL
985
+ """Return ContinueException to signal loop continuation or error recovery."""
986
+ debug_log("eval_continue_statement", "Signaling continue")
987
+ return ContinueException()
752
988
 
753
989
  def eval_break_statement(self, node, env, stack_trace):
754
990
  """Return BreakException to signal loop exit."""
@@ -842,9 +1078,11 @@ class StatementEvaluatorMixin:
842
1078
  obj.set(prop_key, value)
843
1079
  return value
844
1080
  except Exception as e:
845
- return EvaluationError(str(e))
1081
+ obj_type = type(obj).__name__
1082
+ return EvaluationError(f"Assignment to property failed for {prop_key} on {obj_type}: {e}")
846
1083
 
847
- return EvaluationError('Assignment to property failed')
1084
+ obj_type = type(obj).__name__
1085
+ return EvaluationError(f"Assignment to property failed for {prop_key} on {obj_type}")
848
1086
 
849
1087
  # Otherwise it's an identifier assignment
850
1088
  if isinstance(node.name, Identifier):
@@ -865,25 +1103,50 @@ class StatementEvaluatorMixin:
865
1103
  return value
866
1104
 
867
1105
  debug_log("eval_assignment", f"Invalid assignment target - node.name: {node.name}, type: {type(node.name).__name__}")
868
- print(f"[ASSIGN ERROR] node.name: {node.name}, type: {type(node.name).__name__}, value: {getattr(node.name, 'value', 'N/A')}")
1106
+ if zexus_config.should_log('debug'):
1107
+ print(f"[ASSIGN ERROR] node.name: {node.name}, type: {type(node.name).__name__}, value: {getattr(node.name, 'value', 'N/A')}")
869
1108
  return EvaluationError('Invalid assignment target')
870
1109
 
871
1110
  def eval_try_catch_statement(self, node, env, stack_trace):
872
1111
  debug_log("eval_try_catch", f"error_var: {node.error_variable.value if node.error_variable else 'error'}")
873
1112
 
1113
+ # Clear any pending tolerant skips when entering a try/catch,
1114
+ # as the protected block defines a new execution context
1115
+ self._tolerant_skip_counts = {}
1116
+
1117
+ finally_block = getattr(node, 'finally_block', None)
1118
+ result = None
1119
+
874
1120
  try:
875
1121
  result = self.eval_node(node.try_block, env, stack_trace)
876
1122
  if is_error(result):
877
1123
  catch_env = Environment(outer=env)
878
1124
  var_name = node.error_variable.value if node.error_variable else "error"
879
1125
  catch_env.set(var_name, String(str(result)))
880
- return self.eval_node(node.catch_block, catch_env, stack_trace)
1126
+ if zexus_config.should_log('debug'):
1127
+ print(f"[TRY_CATCH] caught error: {result}")
1128
+ result = self.eval_node(node.catch_block, catch_env, stack_trace)
1129
+ self._tolerant_skip_counts = {}
1130
+ self._enqueue_tolerant_duplicates(getattr(node.try_block, "statements", []))
1131
+ self._enqueue_tolerant_duplicates(getattr(node.catch_block, "statements", []))
1132
+ return result
1133
+ self._tolerant_skip_counts = {}
1134
+ self._enqueue_tolerant_duplicates(getattr(node.try_block, "statements", []))
1135
+ self._enqueue_tolerant_duplicates(getattr(node.catch_block, "statements", []))
881
1136
  return result
882
1137
  except Exception as e:
883
1138
  catch_env = Environment(outer=env)
884
1139
  var_name = node.error_variable.value if node.error_variable else "error"
885
1140
  catch_env.set(var_name, String(str(e)))
886
- return self.eval_node(node.catch_block, catch_env, stack_trace)
1141
+ result = self.eval_node(node.catch_block, catch_env, stack_trace)
1142
+ self._tolerant_skip_counts = {}
1143
+ self._enqueue_tolerant_duplicates(getattr(node.try_block, "statements", []))
1144
+ self._enqueue_tolerant_duplicates(getattr(node.catch_block, "statements", []))
1145
+ return result
1146
+ finally:
1147
+ # Always execute the finally block if present
1148
+ if finally_block is not None:
1149
+ self.eval_node(finally_block, env, stack_trace)
887
1150
 
888
1151
  def eval_if_statement(self, node, env, stack_trace):
889
1152
  cond = self.eval_node(node.condition, env, stack_trace)
@@ -913,7 +1176,11 @@ class StatementEvaluatorMixin:
913
1176
  loop_id = id(node) # Unique identifier for this loop
914
1177
 
915
1178
  # Use unified executor if available
916
- if hasattr(self, 'unified_executor') and self.unified_executor:
1179
+ if (
1180
+ hasattr(self, 'unified_executor')
1181
+ and self.unified_executor
1182
+ and not getattr(env, 'disable_vm', False)
1183
+ ):
917
1184
  # Unified execution system handles everything automatically
918
1185
  try:
919
1186
  return self.unified_executor.execute_loop(
@@ -950,6 +1217,10 @@ class StatementEvaluatorMixin:
950
1217
  if isinstance(result, BreakException):
951
1218
  # Break out of loop, return NULL to continue execution in block
952
1219
  return NULL
1220
+ if isinstance(result, ContinueException):
1221
+ # Continue loop iteration
1222
+ result = NULL
1223
+ continue
953
1224
  if isinstance(result, EvaluationError):
954
1225
  return result
955
1226
  def eval_foreach_statement(self, node, env, stack_trace):
@@ -979,6 +1250,10 @@ class StatementEvaluatorMixin:
979
1250
  if isinstance(result, BreakException):
980
1251
  # Break out of loop, return NULL to continue execution in block
981
1252
  return NULL
1253
+ if isinstance(result, ContinueException):
1254
+ # Continue loop iteration
1255
+ result = NULL
1256
+ continue
982
1257
  if isinstance(result, EvaluationError):
983
1258
  return result
984
1259
 
@@ -1119,9 +1394,38 @@ class StatementEvaluatorMixin:
1119
1394
  return False
1120
1395
 
1121
1396
  return False
1397
+
1398
+ def _try_vm_module_exec(self, program, module_env, candidate_path: str):
1399
+ """Attempt to execute a module via VM bytecode for faster imports.
1400
+
1401
+ Returns compiled bytecodes if VM execution succeeds, else None.
1402
+ """
1403
+ if not getattr(self, "use_vm", False):
1404
+ return None
1405
+ bytecode_compiler = getattr(self, "bytecode_compiler", None)
1406
+ if bytecode_compiler is None:
1407
+ return None
1408
+ try:
1409
+ if getattr(self, "vm_instance", None) is None and hasattr(self, "_initialize_vm"):
1410
+ self._initialize_vm()
1411
+ except Exception:
1412
+ return None
1413
+ try:
1414
+ compiled = bytecode_compiler.compile_file(candidate_path, program, optimize=True)
1415
+ if not compiled:
1416
+ return None
1417
+ result = self._execute_bytecode_sequence(compiled, module_env, debug_mode=False)
1418
+ if result is None:
1419
+ return None
1420
+ return compiled
1421
+ except Exception:
1422
+ return None
1122
1423
 
1123
1424
  def eval_use_statement(self, node, env, stack_trace):
1124
- from ..module_cache import get_cached_module, cache_module, get_module_candidates, normalize_path, invalidate_module
1425
+ from ..module_cache import (get_cached_module, cache_module, get_module_candidates,
1426
+ normalize_path, invalidate_module,
1427
+ begin_loading, end_loading, CircularImportError,
1428
+ is_loading)
1125
1429
  from ..builtin_modules import is_builtin_module, get_builtin_module
1126
1430
  from ..stdlib_integration import is_stdlib_module, get_stdlib_module
1127
1431
 
@@ -1131,6 +1435,7 @@ class StatementEvaluatorMixin:
1131
1435
  if not file_path:
1132
1436
  return EvaluationError("use: missing file path")
1133
1437
 
1438
+ debug_enabled = zexus_config.enable_debug_logs
1134
1439
  debug_log(" UseStatement loading", file_path)
1135
1440
 
1136
1441
  # 1a. Check if this is a stdlib module (fs, http, json, datetime, crypto, blockchain)
@@ -1155,7 +1460,8 @@ class StatementEvaluatorMixin:
1155
1460
  debug_log(f" Imported '{name}' from {file_path}", value)
1156
1461
  elif alias:
1157
1462
  # Import as alias: use "stdlib/fs" as fs
1158
- env.set(alias, module_env)
1463
+ alias_name = alias.value if hasattr(alias, 'value') else str(alias)
1464
+ env.set(alias_name, module_env)
1159
1465
  else:
1160
1466
  # Import all functions into current scope
1161
1467
  for key in module_env.store.keys():
@@ -1183,10 +1489,47 @@ class StatementEvaluatorMixin:
1183
1489
  return EvaluationError(f"Builtin module '{file_path}' not available")
1184
1490
 
1185
1491
  normalized_path = normalize_path(file_path)
1186
-
1492
+
1493
+ # 1c. Circular import detection — check before cache lookup
1494
+ # because the cache may contain a partially-loaded placeholder env
1495
+ if is_loading(normalized_path):
1496
+ return EvaluationError(
1497
+ f"Circular import detected: {file_path} is already being loaded"
1498
+ )
1499
+
1187
1500
  # 2. Check Cache
1188
- module_env = get_cached_module(normalized_path)
1189
-
1501
+ module_env = None
1502
+ cached_bytecode = None
1503
+ cached_ast = None
1504
+ _cache_entry = get_cached_module(normalized_path)
1505
+ if isinstance(_cache_entry, tuple) and _cache_entry:
1506
+ module_env, cached_bytecode, cached_ast = _cache_entry
1507
+
1508
+ # 2a. Handle pre-compiled but not-yet-evaluated modules
1509
+ if module_env and getattr(module_env, '_precompiled', False):
1510
+ module_env._precompiled = False # Clear flag to prevent re-entrant eval
1511
+ if cached_bytecode is not None:
1512
+ # Execute pre-compiled bytecode into module env (fastest)
1513
+ try:
1514
+ self._execute_bytecode_sequence(
1515
+ cached_bytecode if isinstance(cached_bytecode, (list, tuple)) else [cached_bytecode],
1516
+ module_env, debug_mode=False)
1517
+ cache_module(normalized_path, module_env, cached_bytecode, cached_ast)
1518
+ debug_log(" Pre-compiled module bytecode executed:", file_path)
1519
+ except Exception:
1520
+ # Bytecode execution failed — fall through to AST eval
1521
+ module_env._precompiled = True
1522
+ module_env = None
1523
+ elif cached_ast is not None:
1524
+ # Fall back to evaluating pre-parsed AST
1525
+ try:
1526
+ self.eval_node(cached_ast, module_env)
1527
+ cache_module(normalized_path, module_env, None, cached_ast)
1528
+ debug_log(" Pre-compiled module AST evaluated:", file_path)
1529
+ except Exception:
1530
+ module_env._precompiled = True
1531
+ module_env = None
1532
+
1190
1533
  # 3. Load if not cached
1191
1534
  if not module_env:
1192
1535
  # Get the importing file's path for relative resolution
@@ -1197,52 +1540,109 @@ class StatementEvaluatorMixin:
1197
1540
  importer_file = __file_obj.value
1198
1541
  elif isinstance(__file_obj, str):
1199
1542
  importer_file = __file_obj
1200
-
1543
+
1201
1544
  candidates = get_module_candidates(file_path, importer_file)
1202
- module_env = Environment()
1203
- loaded = False
1545
+ for candidate in candidates:
1546
+ try:
1547
+ cached = get_cached_module(normalize_path(candidate))
1548
+ if cached:
1549
+ _env, _bc, _ast = cached
1550
+ if getattr(_env, '_precompiled', False):
1551
+ # Pre-compiled but not evaluated — try executing
1552
+ _env._precompiled = False
1553
+ if _bc is not None:
1554
+ try:
1555
+ self._execute_bytecode_sequence(
1556
+ _bc if isinstance(_bc, (list, tuple)) else [_bc],
1557
+ _env, debug_mode=False)
1558
+ cache_module(normalize_path(candidate), _env, _bc, _ast)
1559
+ module_env = _env
1560
+ break
1561
+ except Exception:
1562
+ _env._precompiled = True
1563
+ elif _ast is not None:
1564
+ try:
1565
+ self.eval_node(_ast, _env)
1566
+ cache_module(normalize_path(candidate), _env, None, _ast)
1567
+ module_env = _env
1568
+ break
1569
+ except Exception:
1570
+ _env._precompiled = True
1571
+ else:
1572
+ module_env = _env
1573
+ break
1574
+ except Exception:
1575
+ continue
1576
+
1577
+ loaded = module_env is not None
1578
+ if not module_env:
1579
+ module_env = Environment()
1204
1580
  parse_errors = []
1205
-
1581
+
1582
+ # Circular import detection — register this path as in-progress
1583
+ try:
1584
+ begin_loading(normalized_path)
1585
+ except CircularImportError as e:
1586
+ return EvaluationError(str(e))
1587
+
1206
1588
  # Circular dependency placeholder
1207
1589
  try:
1208
1590
  cache_module(normalized_path, module_env)
1209
1591
  except Exception:
1210
1592
  pass
1211
-
1212
- for candidate in candidates:
1213
- try:
1214
- if not os.path.exists(candidate):
1215
- continue
1216
-
1217
- debug_log(" Found module file", candidate)
1218
- with open(candidate, 'r', encoding='utf-8') as f:
1219
- code = f.read()
1220
-
1221
- from ..lexer import Lexer
1222
- from ..parser import Parser
1223
-
1224
- lexer = Lexer(code)
1225
- parser = Parser(lexer)
1226
- program = parser.parse_program()
1227
-
1228
- if getattr(parser, 'errors', None):
1229
- parse_errors.append((candidate, parser.errors))
1230
- continue
1231
-
1232
- # Set __file__ in module environment so it can do relative imports
1233
- module_env.set("__file__", String(os.path.abspath(candidate)))
1234
- # Set __MODULE__ to the module path (not "__main__" since it's imported)
1235
- module_env.set("__MODULE__", String(file_path))
1236
-
1237
- # Recursive evaluation
1238
- self.eval_node(program, module_env)
1239
-
1240
- # Update cache with fully loaded env
1241
- cache_module(normalized_path, module_env)
1242
- loaded = True
1243
- break
1244
- except Exception as e:
1245
- parse_errors.append((candidate, str(e)))
1593
+
1594
+ try:
1595
+ if not loaded:
1596
+ for candidate in candidates:
1597
+ try:
1598
+ if not os.path.exists(candidate):
1599
+ continue
1600
+
1601
+ debug_log(" Found module file", candidate)
1602
+ # Use VFS cache for module reads (hot path)
1603
+ try:
1604
+ from .integration import get_integration
1605
+ code = get_integration().vfs_manager.cached_read(candidate)
1606
+ except Exception:
1607
+ with open(candidate, 'r', encoding='utf-8') as f:
1608
+ code = f.read()
1609
+
1610
+ from ..lexer import Lexer
1611
+ from ..parser import Parser
1612
+
1613
+ lexer = Lexer(code)
1614
+ parser = Parser(lexer)
1615
+ program = parser.parse_program()
1616
+
1617
+ if getattr(parser, 'errors', None):
1618
+ parse_errors.append((candidate, parser.errors))
1619
+ continue
1620
+
1621
+ # Set __file__ in module environment so it can do relative imports
1622
+ module_env.set("__file__", String(os.path.abspath(candidate)))
1623
+ # Set __MODULE__ to the module path (not "__main__" since it's imported)
1624
+ module_env.set("__MODULE__", String(file_path))
1625
+
1626
+ compiled = self._try_vm_module_exec(program, module_env, os.path.abspath(candidate))
1627
+ if compiled:
1628
+ cache_module(normalized_path, module_env, compiled, program)
1629
+ cache_module(normalize_path(candidate), module_env, compiled, program)
1630
+ loaded = True
1631
+ break
1632
+
1633
+ # Recursive evaluation (interpreter fallback)
1634
+ self.eval_node(program, module_env)
1635
+
1636
+ # Update cache with fully loaded env
1637
+ cache_module(normalized_path, module_env, None, program)
1638
+ cache_module(normalize_path(candidate), module_env, None, program)
1639
+ loaded = True
1640
+ break
1641
+ except Exception as e:
1642
+ parse_errors.append((candidate, str(e)))
1643
+ finally:
1644
+ # Always unmark, even if loading threw
1645
+ end_loading(normalized_path)
1246
1646
 
1247
1647
  if not loaded:
1248
1648
  try:
@@ -1298,12 +1698,14 @@ class StatementEvaluatorMixin:
1298
1698
 
1299
1699
  elif alias:
1300
1700
  # Handle: use "./file.zx" as alias
1301
- env.set(alias, module_env)
1701
+ alias_name = alias.value if hasattr(alias, 'value') else str(alias)
1702
+ env.set(alias_name, module_env)
1302
1703
  else:
1303
1704
  # Handle: use "./file.zx" (import all exports)
1304
1705
  try:
1305
1706
  exports = module_env.get_exports()
1306
- print(f"[DEBUG USE] Importing from module, exports: {list(exports.keys())}")
1707
+ if debug_enabled:
1708
+ print(f"[DEBUG USE] Importing from module, exports: {list(exports.keys())}")
1307
1709
  __file_obj = env.get("__file__")
1308
1710
  importer_file = None
1309
1711
  if __file_obj:
@@ -1313,10 +1715,12 @@ class StatementEvaluatorMixin:
1313
1715
  if importer_file:
1314
1716
  if not self._check_import_permission(value, importer_file):
1315
1717
  return EvaluationError(f"Permission denied for export {name}")
1316
- print(f"[DEBUG USE] Setting {name} = {value}")
1718
+ if debug_enabled:
1719
+ print(f"[DEBUG USE] Setting {name} = {value}")
1317
1720
  env.set(name, value)
1318
1721
  except Exception as e:
1319
- print(f"[DEBUG USE] Exception during export import: {e}")
1722
+ if debug_enabled:
1723
+ print(f"[DEBUG USE] Exception during export import: {e}")
1320
1724
  # Fallback: expose module as filename object
1321
1725
  module_name = os.path.basename(file_path)
1322
1726
  env.set(module_name, module_env)
@@ -1325,7 +1729,9 @@ class StatementEvaluatorMixin:
1325
1729
 
1326
1730
  def eval_from_statement(self, node, env, stack_trace):
1327
1731
  """Full implementation of FromStatement."""
1328
- from ..module_cache import get_cached_module, cache_module, get_module_candidates, normalize_path, invalidate_module
1732
+ from ..module_cache import (get_cached_module, cache_module, get_module_candidates,
1733
+ normalize_path, invalidate_module,
1734
+ begin_loading, end_loading, is_loading)
1329
1735
 
1330
1736
  # 1. Resolve Path
1331
1737
  file_path = node.file_path
@@ -1333,7 +1739,16 @@ class StatementEvaluatorMixin:
1333
1739
  return EvaluationError("from: missing file path")
1334
1740
 
1335
1741
  normalized_path = normalize_path(file_path)
1742
+
1743
+ # Circular import detection
1744
+ if is_loading(normalized_path):
1745
+ return EvaluationError(
1746
+ f"Circular import detected: {file_path} is already being loaded"
1747
+ )
1748
+
1336
1749
  module_env = get_cached_module(normalized_path)
1750
+ if isinstance(module_env, tuple):
1751
+ module_env = module_env[0] if module_env else None
1337
1752
 
1338
1753
  # 2. Load Logic (Explicitly repeated to ensure isolation)
1339
1754
  if not module_env:
@@ -1349,41 +1764,55 @@ class StatementEvaluatorMixin:
1349
1764
  candidates = get_module_candidates(file_path, importer_file)
1350
1765
  module_env = Environment()
1351
1766
  loaded = False
1767
+
1768
+ try:
1769
+ begin_loading(normalized_path)
1770
+ except Exception:
1771
+ return EvaluationError(f"Circular import detected while loading {file_path}")
1352
1772
 
1353
1773
  try:
1354
1774
  cache_module(normalized_path, module_env)
1355
1775
  except Exception:
1356
1776
  pass
1357
1777
 
1358
- for candidate in candidates:
1359
- try:
1360
- if not os.path.exists(candidate):
1361
- continue
1362
-
1363
- with open(candidate, 'r', encoding='utf-8') as f:
1364
- code = f.read()
1365
-
1366
- from ..lexer import Lexer
1367
- from ..parser import Parser
1368
-
1369
- lexer = Lexer(code)
1370
- parser = Parser(lexer)
1371
- program = parser.parse_program()
1372
-
1373
- if getattr(parser, 'errors', None):
1778
+ try:
1779
+ for candidate in candidates:
1780
+ try:
1781
+ if not os.path.exists(candidate):
1782
+ continue
1783
+
1784
+ with open(candidate, 'r', encoding='utf-8') as f:
1785
+ code = f.read()
1786
+
1787
+ from ..lexer import Lexer
1788
+ from ..parser import Parser
1789
+
1790
+ lexer = Lexer(code)
1791
+ parser = Parser(lexer)
1792
+ program = parser.parse_program()
1793
+
1794
+ if getattr(parser, 'errors', None):
1795
+ continue
1796
+
1797
+ # Set __file__ in module environment so it can do relative imports
1798
+ module_env.set("__file__", String(os.path.abspath(candidate)))
1799
+ # Set __MODULE__ to the module path (not "__main__" since it's imported)
1800
+ module_env.set("__MODULE__", String(file_path))
1801
+
1802
+ compiled = self._try_vm_module_exec(program, module_env, os.path.abspath(candidate))
1803
+ if compiled:
1804
+ cache_module(normalized_path, module_env, compiled, program)
1805
+ loaded = True
1806
+ break
1807
+
1808
+ self.eval_node(program, module_env)
1809
+ cache_module(normalized_path, module_env, None, program)
1810
+ loaded = True
1811
+ break
1812
+ except Exception:
1374
1813
  continue
1375
-
1376
- # Set __file__ in module environment so it can do relative imports
1377
- module_env.set("__file__", String(os.path.abspath(candidate)))
1378
- # Set __MODULE__ to the module path (not "__main__" since it's imported)
1379
- module_env.set("__MODULE__", String(file_path))
1380
-
1381
- self.eval_node(program, module_env)
1382
- cache_module(normalized_path, module_env)
1383
- loaded = True
1384
- break
1385
- except Exception:
1386
- continue
1814
+ finally:
1815
+ end_loading(normalized_path)
1387
1816
 
1388
1817
  if not loaded:
1389
1818
  try:
@@ -1635,7 +2064,8 @@ class StatementEvaluatorMixin:
1635
2064
 
1636
2065
  Syntax: sandbox { code }
1637
2066
 
1638
- Creates a new isolated environment and executes code within it.
2067
+ Creates a new isolated environment with VFS-backed file access
2068
+ and restricted capability policy, then executes code within it.
1639
2069
  """
1640
2070
 
1641
2071
  # Create isolated environment (child of current)
@@ -1645,6 +2075,26 @@ class StatementEvaluatorMixin:
1645
2075
  # Allow caller to specify a policy on the node (future enhancement)
1646
2076
  sandbox_policy = getattr(node, 'policy', None) or 'default'
1647
2077
  sandbox_env.set('__sandbox_policy__', sandbox_policy)
2078
+
2079
+ # --- VFS integration: create a per-sandbox VFS with temp-only write ---
2080
+ sandbox_id = f"sandbox_{id(node)}_{id(env)}"
2081
+ try:
2082
+ from .integration import get_integration
2083
+ integration = get_integration()
2084
+ from ..virtual_filesystem import SandboxBuilder, FileAccessMode
2085
+ builder = SandboxBuilder(integration.vfs_manager, sandbox_id)
2086
+ builder.with_temp_access() # /tmp read-write
2087
+ # Mount CWD as read-only so sandbox can read source files
2088
+ import os
2089
+ cwd = os.getcwd()
2090
+ if os.path.isdir(cwd):
2091
+ builder.add_mount("/workspace", cwd, FileAccessMode.READ)
2092
+ sandbox_fs = builder.build()
2093
+ sandbox_env.set('__sandbox_vfs__', sandbox_fs)
2094
+ sandbox_env.set('__sandbox_id__', sandbox_id)
2095
+ except Exception:
2096
+ pass
2097
+
1648
2098
  # Ensure default sandbox policy exists
1649
2099
  try:
1650
2100
  sec = get_security_context()
@@ -1664,10 +2114,16 @@ class StatementEvaluatorMixin:
1664
2114
 
1665
2115
  result = self.eval_node(node.body, sandbox_env, stack_trace)
1666
2116
 
2117
+ # --- Cleanup VFS sandbox ---
2118
+ try:
2119
+ integration = get_integration()
2120
+ integration.vfs_manager.delete_sandbox(sandbox_id)
2121
+ except Exception:
2122
+ pass
2123
+
1667
2124
  # Register sandbox run for observability
1668
2125
  try:
1669
2126
  ctx = get_security_context()
1670
- # store a minimal summary (stringified result) for now
1671
2127
  result_summary = None
1672
2128
  try:
1673
2129
  result_summary = str(result)
@@ -2525,6 +2981,174 @@ class StatementEvaluatorMixin:
2525
2981
  obj = EmbeddedCode(node.name.value, node.language, node.code)
2526
2982
  env.set(node.name.value, obj)
2527
2983
  return NULL
2984
+
2985
+ def eval_color_statement(self, node, env, stack_trace):
2986
+ if node.value is None:
2987
+ return EvaluationError(f"color {node.name.value}: missing definition")
2988
+
2989
+ if isinstance(node.value, BlockStatement):
2990
+ bindings = self._evaluate_renderer_block(node.value, env, stack_trace)
2991
+ if is_error(bindings):
2992
+ return bindings
2993
+ python_spec = {key: _zexus_to_python(val) for key, val in bindings.items()}
2994
+ else:
2995
+ spec_val = self.eval_node(node.value, env, stack_trace)
2996
+ spec_val = _resolve_awaitable(spec_val)
2997
+ if isinstance(spec_val, ReturnValue):
2998
+ spec_val = spec_val.value
2999
+ if is_error(spec_val):
3000
+ return spec_val
3001
+ python_spec = _zexus_to_python(spec_val)
3002
+
3003
+ colour_repr = python_spec
3004
+ if _RENDERER_AVAILABLE and renderer_define_color:
3005
+ try:
3006
+ colour_obj = renderer_define_color(node.name.value, python_spec)
3007
+ colour_repr = str(colour_obj)
3008
+ except Exception as exc:
3009
+ return EvaluationError(f"color {node.name.value}: {exc}")
3010
+ else:
3011
+ registry = getattr(self, 'render_registry', None)
3012
+ if isinstance(registry, dict):
3013
+ registry.setdefault('colours', {})[node.name.value] = python_spec
3014
+
3015
+ env.set(node.name.value, _python_to_zexus(colour_repr))
3016
+ return NULL
3017
+
3018
+ def eval_canvas_statement(self, node, env, stack_trace):
3019
+ prop_data = self._evaluate_renderer_properties(node.properties, env, stack_trace)
3020
+ if is_error(prop_data):
3021
+ return prop_data
3022
+
3023
+ width = 80
3024
+ height = 24
3025
+
3026
+ if isinstance(prop_data, list):
3027
+ if len(prop_data) > 0:
3028
+ width = prop_data[0]
3029
+ if len(prop_data) > 1:
3030
+ height = prop_data[1]
3031
+ elif isinstance(prop_data, dict):
3032
+ width = prop_data.get('width', prop_data.get('w', width))
3033
+ height = prop_data.get('height', prop_data.get('h', height))
3034
+ elif prop_data is not None:
3035
+ width = prop_data
3036
+
3037
+ try:
3038
+ width = int(width)
3039
+ height = int(height)
3040
+ except (ValueError, TypeError):
3041
+ return EvaluationError(f"canvas {node.name.value}: width/height must be numeric")
3042
+
3043
+ canvas_id = None
3044
+ if _RENDERER_AVAILABLE and renderer_create_named_canvas:
3045
+ try:
3046
+ canvas_id = renderer_create_named_canvas(node.name.value, width, height)
3047
+ except Exception as exc:
3048
+ return EvaluationError(f"canvas {node.name.value}: {exc}")
3049
+ else:
3050
+ registry = getattr(self, 'render_registry', None)
3051
+ if isinstance(registry, dict):
3052
+ canvases = registry.setdefault('canvases', {})
3053
+ alias_map = registry.setdefault('canvas_aliases', {})
3054
+ canvas_id = f"canvas_{len(canvases) + 1}"
3055
+ canvases[canvas_id] = {
3056
+ 'width': width,
3057
+ 'height': height,
3058
+ 'draw_ops': [],
3059
+ }
3060
+ alias_map[node.name.value] = canvas_id
3061
+ else:
3062
+ canvas_id = f"{node.name.value}_canvas"
3063
+
3064
+ canvas_obj = _python_to_zexus(canvas_id)
3065
+ env.set(node.name.value, canvas_obj)
3066
+
3067
+ if node.body:
3068
+ local_env = Environment(outer=env)
3069
+ local_env.set(node.name.value, canvas_obj)
3070
+ local_env.set('canvas', canvas_obj)
3071
+ result = self.eval_block_statement(node.body, local_env, stack_trace)
3072
+ if isinstance(result, ReturnValue):
3073
+ return result
3074
+ if is_error(result):
3075
+ return result
3076
+
3077
+ return NULL
3078
+
3079
+ def eval_graphics_statement(self, node, env, stack_trace):
3080
+ payload = {}
3081
+ if node.body:
3082
+ bindings = self._evaluate_renderer_block(node.body, env, stack_trace)
3083
+ if is_error(bindings):
3084
+ return bindings
3085
+ payload = {key: _zexus_to_python(val) for key, val in bindings.items()}
3086
+
3087
+ if _RENDERER_AVAILABLE and renderer_register_graphics:
3088
+ try:
3089
+ renderer_register_graphics(node.name.value, payload)
3090
+ except Exception as exc:
3091
+ return EvaluationError(f"graphics {node.name.value}: {exc}")
3092
+ else:
3093
+ registry = getattr(self, 'render_registry', None)
3094
+ if isinstance(registry, dict):
3095
+ registry.setdefault('graphics', {})[node.name.value] = payload
3096
+
3097
+ env.set(node.name.value, _python_to_zexus(payload))
3098
+ return NULL
3099
+
3100
+ def eval_animation_statement(self, node, env, stack_trace):
3101
+ animation_data = {}
3102
+
3103
+ props = self._evaluate_renderer_properties(node.properties, env, stack_trace)
3104
+ if is_error(props):
3105
+ return props
3106
+ if props is not None:
3107
+ animation_data['properties'] = props
3108
+
3109
+ if node.body:
3110
+ bindings = self._evaluate_renderer_block(node.body, env, stack_trace)
3111
+ if is_error(bindings):
3112
+ return bindings
3113
+ animation_data.update({key: _zexus_to_python(val) for key, val in bindings.items()})
3114
+
3115
+ if _RENDERER_AVAILABLE and renderer_register_animation:
3116
+ try:
3117
+ renderer_register_animation(node.name.value, animation_data)
3118
+ except Exception as exc:
3119
+ return EvaluationError(f"animation {node.name.value}: {exc}")
3120
+ else:
3121
+ registry = getattr(self, 'render_registry', None)
3122
+ if isinstance(registry, dict):
3123
+ registry.setdefault('animations', {})[node.name.value] = animation_data
3124
+
3125
+ env.set(node.name.value, _python_to_zexus(animation_data))
3126
+ return NULL
3127
+
3128
+ def eval_clock_statement(self, node, env, stack_trace):
3129
+ config = self._evaluate_renderer_properties(node.properties, env, stack_trace)
3130
+ if is_error(config):
3131
+ return config
3132
+
3133
+ if isinstance(config, dict):
3134
+ clock_data = dict(config)
3135
+ elif config is None:
3136
+ clock_data = {}
3137
+ else:
3138
+ clock_data = {'value': config}
3139
+
3140
+ if _RENDERER_AVAILABLE and renderer_register_clock:
3141
+ try:
3142
+ renderer_register_clock(node.name.value, clock_data)
3143
+ except Exception as exc:
3144
+ return EvaluationError(f"clock {node.name.value}: {exc}")
3145
+ else:
3146
+ registry = getattr(self, 'render_registry', None)
3147
+ if isinstance(registry, dict):
3148
+ registry.setdefault('clocks', {})[node.name.value] = clock_data
3149
+
3150
+ env.set(node.name.value, _python_to_zexus(clock_data))
3151
+ return NULL
2528
3152
 
2529
3153
  def eval_component_statement(self, node, env, stack_trace):
2530
3154
  props = None
@@ -2611,7 +3235,15 @@ class StatementEvaluatorMixin:
2611
3235
  return self.eval_node(node.body, env, stack_trace)
2612
3236
 
2613
3237
  def eval_action_statement(self, node, env, stack_trace):
2614
- action = Action(node.parameters, node.body, env)
3238
+ capture_env = env.clone_for_closure() if hasattr(env, "clone_for_closure") else env
3239
+ action = Action(node.parameters, node.body, capture_env)
3240
+
3241
+ # Ensure recursive references resolve within closures
3242
+ try:
3243
+ if capture_env is not env and hasattr(capture_env, "set"):
3244
+ capture_env.set(node.name.value, action)
3245
+ except Exception:
3246
+ pass
2615
3247
 
2616
3248
  # Check for direct is_async attribute (from UltimateParser)
2617
3249
  if hasattr(node, 'is_async') and node.is_async:
@@ -2644,20 +3276,26 @@ class StatementEvaluatorMixin:
2644
3276
 
2645
3277
  def eval_function_statement(self, node, env, stack_trace):
2646
3278
  """Evaluate function statement - identical to action statement in Zexus"""
2647
- print(f"[EVAL_FUNC] Starting eval_function_statement for: {node.name.value}", flush=True)
2648
- action = Action(node.parameters, node.body, env)
2649
- print(f"[EVAL_FUNC] Created Action object", flush=True)
3279
+ debug_log("eval_function_statement", f"Start {node.name.value}")
3280
+ capture_env = env.clone_for_closure() if hasattr(env, "clone_for_closure") else env
3281
+ action = Action(node.parameters, node.body, capture_env)
3282
+ try:
3283
+ if capture_env is not env and hasattr(capture_env, "set"):
3284
+ capture_env.set(node.name.value, action)
3285
+ except Exception:
3286
+ pass
3287
+ debug_log("eval_function_statement", "Created Action object")
2650
3288
 
2651
3289
  # Apply modifiers if present
2652
3290
  modifiers = getattr(node, 'modifiers', [])
2653
- print(f"[EVAL_FUNC] Modifiers: {modifiers}", flush=True)
3291
+ debug_log("eval_function_statement", f"Modifiers: {modifiers}")
2654
3292
  if modifiers:
2655
3293
  # Set modifier flags on the action object
2656
3294
  if 'inline' in modifiers:
2657
3295
  action.is_inlined = True
2658
3296
  if 'async' in modifiers:
2659
3297
  action.is_async = True
2660
- print(f"[EVAL_FUNC] Set is_async=True", flush=True)
3298
+ debug_log("eval_function_statement", "Set is_async=True")
2661
3299
  if 'secure' in modifiers:
2662
3300
  action.is_secure = True
2663
3301
  if 'pure' in modifiers:
@@ -2672,9 +3310,9 @@ class StatementEvaluatorMixin:
2672
3310
  except Exception:
2673
3311
  pass
2674
3312
 
2675
- print(f"[EVAL_FUNC] About to set in environment: {node.name.value}", flush=True)
3313
+ debug_log("eval_function_statement", f"Binding {node.name.value}")
2676
3314
  env.set(node.name.value, action)
2677
- print(f"[EVAL_FUNC] Successfully set in environment", flush=True)
3315
+ debug_log("eval_function_statement", "Binding complete")
2678
3316
  return NULL
2679
3317
 
2680
3318
  # === PERFORMANCE OPTIMIZATION STATEMENTS ===
@@ -3758,10 +4396,12 @@ class StatementEvaluatorMixin:
3758
4396
  if not is_truthy(condition):
3759
4397
  # Execute tolerance block if provided (for conditional allowances)
3760
4398
  if node.tolerance_block:
3761
- print(f"⚡ TOLERANCE BLOCK: type={type(node.tolerance_block).__name__}")
4399
+ if zexus_config.should_log('debug'):
4400
+ print(f"⚡ TOLERANCE BLOCK: type={type(node.tolerance_block).__name__}")
3762
4401
  debug_log("eval_require_statement", "Condition failed - executing tolerance logic")
3763
4402
  tolerance_result = self.eval_node(node.tolerance_block, env, stack_trace)
3764
- print(f"⚡ TOLERANCE RESULT: type={type(tolerance_result).__name__}")
4403
+ if zexus_config.should_log('debug'):
4404
+ print(f"⚡ TOLERANCE RESULT: type={type(tolerance_result).__name__}")
3765
4405
 
3766
4406
  # Check if tolerance logic allows proceeding
3767
4407
  if is_error(tolerance_result):
@@ -3770,18 +4410,22 @@ class StatementEvaluatorMixin:
3770
4410
  # Unwrap ReturnValue if present
3771
4411
  from ..object import ReturnValue
3772
4412
  if isinstance(tolerance_result, ReturnValue):
3773
- print(f"⚡ UNWRAPPING ReturnValue")
4413
+ if zexus_config.should_log('debug'):
4414
+ print(f"⚡ UNWRAPPING ReturnValue")
3774
4415
  tolerance_result = tolerance_result.value
3775
- print(f"⚡ UNWRAPPED VALUE: {tolerance_result}")
4416
+ if zexus_config.should_log('debug'):
4417
+ print(f"⚡ UNWRAPPED VALUE: {tolerance_result}")
3776
4418
 
3777
4419
  # If tolerance block returns true/truthy, allow it
3778
4420
  if is_truthy(tolerance_result):
3779
- print(f"⚡ TOLERANCE APPROVED")
4421
+ if zexus_config.should_log('debug'):
4422
+ print(f"⚡ TOLERANCE APPROVED")
3780
4423
  debug_log("eval_require_statement", "Tolerance logic approved - allowing requirement")
3781
4424
  return NULL
3782
4425
 
3783
4426
  # If tolerance block returns false, requirement still fails
3784
- print(f"⚡ TOLERANCE REJECTED")
4427
+ if zexus_config.should_log('debug'):
4428
+ print(f"⚡ TOLERANCE REJECTED")
3785
4429
  debug_log("eval_require_statement", "Tolerance logic rejected - requirement fails")
3786
4430
  # Fall through to error below
3787
4431
 
@@ -3902,25 +4546,32 @@ class StatementEvaluatorMixin:
3902
4546
  debug_log("_eval_require_resource", f"{req_type} requirement satisfied")
3903
4547
  return NULL
3904
4548
 
3905
- def eval_revert_statement(self, node, env, stack_trace):
3906
- """Evaluate revert statement - rollback transaction.
3907
-
3908
- revert();
3909
- revert("Unauthorized");
3910
- """
4549
+ def _perform_revert(self, reason_expr, env, stack_trace):
3911
4550
  debug_log("eval_revert_statement", "Reverting transaction")
3912
-
3913
- # Evaluate revert reason if provided
4551
+
3914
4552
  reason = "Transaction reverted"
3915
- if node.reason:
3916
- reason_val = self.eval_node(node.reason, env, stack_trace)
4553
+ if reason_expr:
4554
+ reason_val = self.eval_node(reason_expr, env, stack_trace)
3917
4555
  if isinstance(reason_val, String):
3918
4556
  reason = reason_val.value
3919
4557
  elif not is_error(reason_val):
3920
4558
  reason = str(reason_val.inspect() if hasattr(reason_val, 'inspect') else reason_val)
3921
-
4559
+
3922
4560
  debug_log("eval_revert_statement", f"REVERT: {reason}")
3923
4561
  return EvaluationError(f"Transaction reverted: {reason}", stack_trace=stack_trace)
4562
+
4563
+ def eval_revert_statement(self, node, env, stack_trace):
4564
+ """Evaluate revert statement - rollback transaction."""
4565
+
4566
+ signature = self._compute_revert_signature(getattr(node, "reason", None))
4567
+ pending = getattr(self, "_pending_revert_signature", None)
4568
+ if pending is not None and pending == signature:
4569
+ debug_log("eval_revert_statement", "Skipping duplicate revert statement")
4570
+ self._pending_revert_signature = None
4571
+ return NULL
4572
+
4573
+ self._pending_revert_signature = None
4574
+ return self._perform_revert(getattr(node, "reason", None), env, stack_trace)
3924
4575
 
3925
4576
  def eval_limit_statement(self, node, env, stack_trace):
3926
4577
  """Evaluate limit statement - set gas limit.
@@ -4247,7 +4898,8 @@ class StatementEvaluatorMixin:
4247
4898
 
4248
4899
  # Print event for debugging (optional)
4249
4900
  args_str = ", ".join(str(arg.inspect() if hasattr(arg, 'inspect') else arg) for arg in args)
4250
- print(f"📢 Event: {event_name}({args_str})")
4901
+ if zexus_config.should_log('info'):
4902
+ print(f"📢 Event: {event_name}({args_str})")
4251
4903
 
4252
4904
  return NULL
4253
4905