zexus 1.7.1 → 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 (159) hide show
  1. package/README.md +3 -3
  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/cli/__pycache__/main.cpython-312.pyc +0 -0
  45. package/src/zexus/cli/main.py +300 -20
  46. package/src/zexus/cli/zpm.py +1 -1
  47. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  48. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/lexer.py +10 -5
  53. package/src/zexus/concurrency_system.py +79 -0
  54. package/src/zexus/config.py +54 -0
  55. package/src/zexus/crypto_bridge.py +244 -8
  56. package/src/zexus/dap/__init__.py +10 -0
  57. package/src/zexus/dap/__main__.py +4 -0
  58. package/src/zexus/dap/dap_server.py +391 -0
  59. package/src/zexus/dap/debug_engine.py +298 -0
  60. package/src/zexus/environment.py +10 -1
  61. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  62. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  63. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  64. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  65. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  66. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  67. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/bytecode_compiler.py +441 -37
  70. package/src/zexus/evaluator/core.py +560 -49
  71. package/src/zexus/evaluator/expressions.py +122 -49
  72. package/src/zexus/evaluator/functions.py +417 -16
  73. package/src/zexus/evaluator/statements.py +521 -118
  74. package/src/zexus/evaluator/unified_execution.py +573 -72
  75. package/src/zexus/evaluator/utils.py +14 -2
  76. package/src/zexus/event_loop.py +186 -0
  77. package/src/zexus/lexer.py +742 -486
  78. package/src/zexus/lsp/__init__.py +1 -1
  79. package/src/zexus/lsp/definition_provider.py +163 -9
  80. package/src/zexus/lsp/server.py +22 -8
  81. package/src/zexus/lsp/symbol_provider.py +182 -9
  82. package/src/zexus/module_cache.py +237 -9
  83. package/src/zexus/object.py +64 -6
  84. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  85. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  86. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  87. package/src/zexus/parser/parser.py +786 -285
  88. package/src/zexus/parser/strategy_context.py +407 -66
  89. package/src/zexus/parser/strategy_structural.py +117 -19
  90. package/src/zexus/persistence.py +15 -1
  91. package/src/zexus/renderer/__init__.py +15 -0
  92. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  93. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  94. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  95. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  96. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  97. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  98. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  99. package/src/zexus/renderer/tk_backend.py +208 -0
  100. package/src/zexus/renderer/web_backend.py +260 -0
  101. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  103. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  104. package/src/zexus/runtime/file_flags.py +137 -0
  105. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  107. package/src/zexus/security.py +424 -34
  108. package/src/zexus/stdlib/fs.py +23 -18
  109. package/src/zexus/stdlib/http.py +289 -186
  110. package/src/zexus/stdlib/sockets.py +207 -163
  111. package/src/zexus/stdlib/websockets.py +282 -0
  112. package/src/zexus/stdlib_integration.py +369 -2
  113. package/src/zexus/strategy_recovery.py +6 -3
  114. package/src/zexus/type_checker.py +423 -0
  115. package/src/zexus/virtual_filesystem.py +189 -2
  116. package/src/zexus/vm/__init__.py +113 -3
  117. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  118. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  119. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  120. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  121. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  122. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  123. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  124. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  125. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  126. package/src/zexus/vm/async_optimizer.py +14 -1
  127. package/src/zexus/vm/binary_bytecode.py +659 -0
  128. package/src/zexus/vm/bytecode.py +28 -1
  129. package/src/zexus/vm/bytecode_converter.py +26 -12
  130. package/src/zexus/vm/cabi.c +1985 -0
  131. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  132. package/src/zexus/vm/cabi.h +127 -0
  133. package/src/zexus/vm/cache.py +557 -17
  134. package/src/zexus/vm/compiler.py +703 -5
  135. package/src/zexus/vm/fastops.c +15743 -0
  136. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  137. package/src/zexus/vm/fastops.pyx +288 -0
  138. package/src/zexus/vm/gas_metering.py +50 -9
  139. package/src/zexus/vm/jit.py +83 -2
  140. package/src/zexus/vm/native_jit_backend.py +1816 -0
  141. package/src/zexus/vm/native_runtime.cpp +1388 -0
  142. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  143. package/src/zexus/vm/optimizer.py +161 -11
  144. package/src/zexus/vm/parallel_vm.py +118 -42
  145. package/src/zexus/vm/peephole_optimizer.py +82 -4
  146. package/src/zexus/vm/profiler.py +38 -18
  147. package/src/zexus/vm/register_allocator.py +16 -5
  148. package/src/zexus/vm/register_vm.py +8 -5
  149. package/src/zexus/vm/vm.py +3411 -573
  150. package/src/zexus/vm/wasm_compiler.py +658 -0
  151. package/src/zexus/zexus_ast.py +63 -11
  152. package/src/zexus/zexus_token.py +13 -5
  153. package/src/zexus/zpm/installer.py +55 -15
  154. package/src/zexus/zpm/package_manager.py +1 -1
  155. package/src/zexus/zpm/registry.py +257 -28
  156. package/src/zexus.egg-info/PKG-INFO +7 -4
  157. package/src/zexus.egg-info/SOURCES.txt +116 -9
  158. package/src/zexus.egg-info/entry_points.txt +1 -0
  159. package/src/zexus.egg-info/requires.txt +4 -0
@@ -31,6 +31,7 @@ from ..security import (
31
31
  ProtectionPolicy, Middleware, AuthConfig, RateLimiter, CachePolicy
32
32
  )
33
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
34
35
 
35
36
  try:
36
37
  from ..renderer import (
@@ -55,9 +56,77 @@ class BreakException:
55
56
  def __repr__(self):
56
57
  return "BreakException()"
57
58
 
59
+ class ContinueException:
60
+ """Exception raised when continue statement is encountered in a loop."""
61
+ def __repr__(self):
62
+ return "ContinueException()"
63
+
64
+
58
65
  class StatementEvaluatorMixin:
59
66
  """Handles evaluation of statements, flow control, module loading, and security features."""
60
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
+
61
130
  def ceval_program(self, statements, env):
62
131
  debug_log("eval_program", f"Processing {len(statements)} statements")
63
132
 
@@ -73,6 +142,23 @@ class StatementEvaluatorMixin:
73
142
  try:
74
143
  for i, stmt in enumerate(statements):
75
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
76
162
  res = self.eval_node(stmt, env)
77
163
  res = _resolve_awaitable(res)
78
164
  EVAL_SUMMARY['evaluated_statements'] += 1
@@ -82,6 +168,14 @@ class StatementEvaluatorMixin:
82
168
  # Execute deferred cleanup before returning
83
169
  self._execute_deferred_cleanup(env, [])
84
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
+
85
179
  if is_error(res):
86
180
  debug_log(" Error encountered", res)
87
181
  try:
@@ -94,7 +188,8 @@ class StatementEvaluatorMixin:
94
188
  # Log the error and continue execution
95
189
  error_msg = str(res)
96
190
  self.error_log.append(error_msg)
97
- print(f"[ERROR] {error_msg}")
191
+ if zexus_config.should_log('error'):
192
+ print(f"[ERROR] {error_msg}")
98
193
  debug_log(" Continuing after error", "continue_on_error=True")
99
194
  result = NULL # Continue with null result
100
195
  continue
@@ -124,11 +219,23 @@ class StatementEvaluatorMixin:
124
219
  result = NULL
125
220
  try:
126
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
127
234
  res = self.eval_node(stmt, env, stack_trace)
128
235
  res = _resolve_awaitable(res)
129
236
  EVAL_SUMMARY['evaluated_statements'] += 1
130
237
 
131
- if isinstance(res, (ReturnValue, BreakException, EvaluationError)):
238
+ if isinstance(res, (ReturnValue, BreakException, ContinueException, EvaluationError)):
132
239
  debug_log(" Block interrupted", res)
133
240
  if is_error(res):
134
241
  try:
@@ -141,7 +248,8 @@ class StatementEvaluatorMixin:
141
248
  # Log the error and continue execution
142
249
  error_msg = str(res)
143
250
  self.error_log.append(error_msg)
144
- print(f"[ERROR] {error_msg}")
251
+ if zexus_config.should_log('error'):
252
+ print(f"[ERROR] {error_msg}")
145
253
  debug_log(" Continuing after error in block", "continue_on_error=True")
146
254
  result = NULL # Continue with null result
147
255
  continue
@@ -215,17 +323,69 @@ class StatementEvaluatorMixin:
215
323
  if hasattr(node.expression, 'function') and hasattr(node.expression.function, 'value'):
216
324
  func_name = node.expression.function.value
217
325
  if func_name in ['persist_set', 'persist_get']:
218
- print(f"[EVAL_EXPR_STMT] Evaluating {func_name} call", flush=True)
326
+ debug_log("eval_expression_statement", f"Evaluating {func_name} call", level='info')
219
327
  result = self.eval_node(node.expression, env, stack_trace)
220
328
  if hasattr(node.expression, 'function') and hasattr(node.expression.function, 'value'):
221
329
  func_name = node.expression.function.value
222
330
  if func_name in ['persist_set', 'persist_get']:
223
- 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')
224
332
  return result
225
333
 
226
334
  # === VARIABLE & CONTROL FLOW ===
227
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
+
228
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
+
229
389
  debug_log("eval_let_statement", f"let {node.name.value}")
230
390
 
231
391
  # FIXED: Evaluate value FIRST to prevent recursion issues
@@ -280,6 +440,16 @@ class StatementEvaluatorMixin:
280
440
  return value_type in expected_types
281
441
 
282
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
+
283
453
  debug_log("eval_const_statement", f"const {node.name.value}")
284
454
 
285
455
  # Evaluate value FIRST
@@ -812,10 +982,9 @@ class StatementEvaluatorMixin:
812
982
  return ReturnValue(val)
813
983
 
814
984
  def eval_continue_statement(self, node, env, stack_trace):
815
- """Enable continue-on-error mode for the evaluator."""
816
- debug_log("eval_continue_statement", "Enabling error recovery mode")
817
- self.continue_on_error = True
818
- return NULL
985
+ """Return ContinueException to signal loop continuation or error recovery."""
986
+ debug_log("eval_continue_statement", "Signaling continue")
987
+ return ContinueException()
819
988
 
820
989
  def eval_break_statement(self, node, env, stack_trace):
821
990
  """Return BreakException to signal loop exit."""
@@ -909,9 +1078,11 @@ class StatementEvaluatorMixin:
909
1078
  obj.set(prop_key, value)
910
1079
  return value
911
1080
  except Exception as e:
912
- 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}")
913
1083
 
914
- 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}")
915
1086
 
916
1087
  # Otherwise it's an identifier assignment
917
1088
  if isinstance(node.name, Identifier):
@@ -932,25 +1103,50 @@ class StatementEvaluatorMixin:
932
1103
  return value
933
1104
 
934
1105
  debug_log("eval_assignment", f"Invalid assignment target - node.name: {node.name}, type: {type(node.name).__name__}")
935
- 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')}")
936
1108
  return EvaluationError('Invalid assignment target')
937
1109
 
938
1110
  def eval_try_catch_statement(self, node, env, stack_trace):
939
1111
  debug_log("eval_try_catch", f"error_var: {node.error_variable.value if node.error_variable else 'error'}")
940
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
+
941
1120
  try:
942
1121
  result = self.eval_node(node.try_block, env, stack_trace)
943
1122
  if is_error(result):
944
1123
  catch_env = Environment(outer=env)
945
1124
  var_name = node.error_variable.value if node.error_variable else "error"
946
1125
  catch_env.set(var_name, String(str(result)))
947
- 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", []))
948
1136
  return result
949
1137
  except Exception as e:
950
1138
  catch_env = Environment(outer=env)
951
1139
  var_name = node.error_variable.value if node.error_variable else "error"
952
1140
  catch_env.set(var_name, String(str(e)))
953
- 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)
954
1150
 
955
1151
  def eval_if_statement(self, node, env, stack_trace):
956
1152
  cond = self.eval_node(node.condition, env, stack_trace)
@@ -980,7 +1176,11 @@ class StatementEvaluatorMixin:
980
1176
  loop_id = id(node) # Unique identifier for this loop
981
1177
 
982
1178
  # Use unified executor if available
983
- 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
+ ):
984
1184
  # Unified execution system handles everything automatically
985
1185
  try:
986
1186
  return self.unified_executor.execute_loop(
@@ -1017,6 +1217,10 @@ class StatementEvaluatorMixin:
1017
1217
  if isinstance(result, BreakException):
1018
1218
  # Break out of loop, return NULL to continue execution in block
1019
1219
  return NULL
1220
+ if isinstance(result, ContinueException):
1221
+ # Continue loop iteration
1222
+ result = NULL
1223
+ continue
1020
1224
  if isinstance(result, EvaluationError):
1021
1225
  return result
1022
1226
  def eval_foreach_statement(self, node, env, stack_trace):
@@ -1046,6 +1250,10 @@ class StatementEvaluatorMixin:
1046
1250
  if isinstance(result, BreakException):
1047
1251
  # Break out of loop, return NULL to continue execution in block
1048
1252
  return NULL
1253
+ if isinstance(result, ContinueException):
1254
+ # Continue loop iteration
1255
+ result = NULL
1256
+ continue
1049
1257
  if isinstance(result, EvaluationError):
1050
1258
  return result
1051
1259
 
@@ -1186,9 +1394,38 @@ class StatementEvaluatorMixin:
1186
1394
  return False
1187
1395
 
1188
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
1189
1423
 
1190
1424
  def eval_use_statement(self, node, env, stack_trace):
1191
- 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)
1192
1429
  from ..builtin_modules import is_builtin_module, get_builtin_module
1193
1430
  from ..stdlib_integration import is_stdlib_module, get_stdlib_module
1194
1431
 
@@ -1198,6 +1435,7 @@ class StatementEvaluatorMixin:
1198
1435
  if not file_path:
1199
1436
  return EvaluationError("use: missing file path")
1200
1437
 
1438
+ debug_enabled = zexus_config.enable_debug_logs
1201
1439
  debug_log(" UseStatement loading", file_path)
1202
1440
 
1203
1441
  # 1a. Check if this is a stdlib module (fs, http, json, datetime, crypto, blockchain)
@@ -1222,7 +1460,8 @@ class StatementEvaluatorMixin:
1222
1460
  debug_log(f" Imported '{name}' from {file_path}", value)
1223
1461
  elif alias:
1224
1462
  # Import as alias: use "stdlib/fs" as fs
1225
- env.set(alias, module_env)
1463
+ alias_name = alias.value if hasattr(alias, 'value') else str(alias)
1464
+ env.set(alias_name, module_env)
1226
1465
  else:
1227
1466
  # Import all functions into current scope
1228
1467
  for key in module_env.store.keys():
@@ -1250,10 +1489,47 @@ class StatementEvaluatorMixin:
1250
1489
  return EvaluationError(f"Builtin module '{file_path}' not available")
1251
1490
 
1252
1491
  normalized_path = normalize_path(file_path)
1253
-
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
+
1254
1500
  # 2. Check Cache
1255
- module_env = get_cached_module(normalized_path)
1256
-
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
+
1257
1533
  # 3. Load if not cached
1258
1534
  if not module_env:
1259
1535
  # Get the importing file's path for relative resolution
@@ -1264,52 +1540,109 @@ class StatementEvaluatorMixin:
1264
1540
  importer_file = __file_obj.value
1265
1541
  elif isinstance(__file_obj, str):
1266
1542
  importer_file = __file_obj
1267
-
1543
+
1268
1544
  candidates = get_module_candidates(file_path, importer_file)
1269
- module_env = Environment()
1270
- 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()
1271
1580
  parse_errors = []
1272
-
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
+
1273
1588
  # Circular dependency placeholder
1274
1589
  try:
1275
1590
  cache_module(normalized_path, module_env)
1276
1591
  except Exception:
1277
1592
  pass
1278
-
1279
- for candidate in candidates:
1280
- try:
1281
- if not os.path.exists(candidate):
1282
- continue
1283
-
1284
- debug_log(" Found module file", candidate)
1285
- with open(candidate, 'r', encoding='utf-8') as f:
1286
- code = f.read()
1287
-
1288
- from ..lexer import Lexer
1289
- from ..parser import Parser
1290
-
1291
- lexer = Lexer(code)
1292
- parser = Parser(lexer)
1293
- program = parser.parse_program()
1294
-
1295
- if getattr(parser, 'errors', None):
1296
- parse_errors.append((candidate, parser.errors))
1297
- continue
1298
-
1299
- # Set __file__ in module environment so it can do relative imports
1300
- module_env.set("__file__", String(os.path.abspath(candidate)))
1301
- # Set __MODULE__ to the module path (not "__main__" since it's imported)
1302
- module_env.set("__MODULE__", String(file_path))
1303
-
1304
- # Recursive evaluation
1305
- self.eval_node(program, module_env)
1306
-
1307
- # Update cache with fully loaded env
1308
- cache_module(normalized_path, module_env)
1309
- loaded = True
1310
- break
1311
- except Exception as e:
1312
- 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)
1313
1646
 
1314
1647
  if not loaded:
1315
1648
  try:
@@ -1365,12 +1698,14 @@ class StatementEvaluatorMixin:
1365
1698
 
1366
1699
  elif alias:
1367
1700
  # Handle: use "./file.zx" as alias
1368
- env.set(alias, module_env)
1701
+ alias_name = alias.value if hasattr(alias, 'value') else str(alias)
1702
+ env.set(alias_name, module_env)
1369
1703
  else:
1370
1704
  # Handle: use "./file.zx" (import all exports)
1371
1705
  try:
1372
1706
  exports = module_env.get_exports()
1373
- 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())}")
1374
1709
  __file_obj = env.get("__file__")
1375
1710
  importer_file = None
1376
1711
  if __file_obj:
@@ -1380,10 +1715,12 @@ class StatementEvaluatorMixin:
1380
1715
  if importer_file:
1381
1716
  if not self._check_import_permission(value, importer_file):
1382
1717
  return EvaluationError(f"Permission denied for export {name}")
1383
- print(f"[DEBUG USE] Setting {name} = {value}")
1718
+ if debug_enabled:
1719
+ print(f"[DEBUG USE] Setting {name} = {value}")
1384
1720
  env.set(name, value)
1385
1721
  except Exception as e:
1386
- print(f"[DEBUG USE] Exception during export import: {e}")
1722
+ if debug_enabled:
1723
+ print(f"[DEBUG USE] Exception during export import: {e}")
1387
1724
  # Fallback: expose module as filename object
1388
1725
  module_name = os.path.basename(file_path)
1389
1726
  env.set(module_name, module_env)
@@ -1392,7 +1729,9 @@ class StatementEvaluatorMixin:
1392
1729
 
1393
1730
  def eval_from_statement(self, node, env, stack_trace):
1394
1731
  """Full implementation of FromStatement."""
1395
- 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)
1396
1735
 
1397
1736
  # 1. Resolve Path
1398
1737
  file_path = node.file_path
@@ -1400,7 +1739,16 @@ class StatementEvaluatorMixin:
1400
1739
  return EvaluationError("from: missing file path")
1401
1740
 
1402
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
+
1403
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
1404
1752
 
1405
1753
  # 2. Load Logic (Explicitly repeated to ensure isolation)
1406
1754
  if not module_env:
@@ -1416,41 +1764,55 @@ class StatementEvaluatorMixin:
1416
1764
  candidates = get_module_candidates(file_path, importer_file)
1417
1765
  module_env = Environment()
1418
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}")
1419
1772
 
1420
1773
  try:
1421
1774
  cache_module(normalized_path, module_env)
1422
1775
  except Exception:
1423
1776
  pass
1424
1777
 
1425
- for candidate in candidates:
1426
- try:
1427
- if not os.path.exists(candidate):
1428
- continue
1429
-
1430
- with open(candidate, 'r', encoding='utf-8') as f:
1431
- code = f.read()
1432
-
1433
- from ..lexer import Lexer
1434
- from ..parser import Parser
1435
-
1436
- lexer = Lexer(code)
1437
- parser = Parser(lexer)
1438
- program = parser.parse_program()
1439
-
1440
- 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:
1441
1813
  continue
1442
-
1443
- # Set __file__ in module environment so it can do relative imports
1444
- module_env.set("__file__", String(os.path.abspath(candidate)))
1445
- # Set __MODULE__ to the module path (not "__main__" since it's imported)
1446
- module_env.set("__MODULE__", String(file_path))
1447
-
1448
- self.eval_node(program, module_env)
1449
- cache_module(normalized_path, module_env)
1450
- loaded = True
1451
- break
1452
- except Exception:
1453
- continue
1814
+ finally:
1815
+ end_loading(normalized_path)
1454
1816
 
1455
1817
  if not loaded:
1456
1818
  try:
@@ -1702,7 +2064,8 @@ class StatementEvaluatorMixin:
1702
2064
 
1703
2065
  Syntax: sandbox { code }
1704
2066
 
1705
- 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.
1706
2069
  """
1707
2070
 
1708
2071
  # Create isolated environment (child of current)
@@ -1712,6 +2075,26 @@ class StatementEvaluatorMixin:
1712
2075
  # Allow caller to specify a policy on the node (future enhancement)
1713
2076
  sandbox_policy = getattr(node, 'policy', None) or 'default'
1714
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
+
1715
2098
  # Ensure default sandbox policy exists
1716
2099
  try:
1717
2100
  sec = get_security_context()
@@ -1731,10 +2114,16 @@ class StatementEvaluatorMixin:
1731
2114
 
1732
2115
  result = self.eval_node(node.body, sandbox_env, stack_trace)
1733
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
+
1734
2124
  # Register sandbox run for observability
1735
2125
  try:
1736
2126
  ctx = get_security_context()
1737
- # store a minimal summary (stringified result) for now
1738
2127
  result_summary = None
1739
2128
  try:
1740
2129
  result_summary = str(result)
@@ -2887,7 +3276,7 @@ class StatementEvaluatorMixin:
2887
3276
 
2888
3277
  def eval_function_statement(self, node, env, stack_trace):
2889
3278
  """Evaluate function statement - identical to action statement in Zexus"""
2890
- print(f"[EVAL_FUNC] Starting eval_function_statement for: {node.name.value}", flush=True)
3279
+ debug_log("eval_function_statement", f"Start {node.name.value}")
2891
3280
  capture_env = env.clone_for_closure() if hasattr(env, "clone_for_closure") else env
2892
3281
  action = Action(node.parameters, node.body, capture_env)
2893
3282
  try:
@@ -2895,18 +3284,18 @@ class StatementEvaluatorMixin:
2895
3284
  capture_env.set(node.name.value, action)
2896
3285
  except Exception:
2897
3286
  pass
2898
- print(f"[EVAL_FUNC] Created Action object", flush=True)
3287
+ debug_log("eval_function_statement", "Created Action object")
2899
3288
 
2900
3289
  # Apply modifiers if present
2901
3290
  modifiers = getattr(node, 'modifiers', [])
2902
- print(f"[EVAL_FUNC] Modifiers: {modifiers}", flush=True)
3291
+ debug_log("eval_function_statement", f"Modifiers: {modifiers}")
2903
3292
  if modifiers:
2904
3293
  # Set modifier flags on the action object
2905
3294
  if 'inline' in modifiers:
2906
3295
  action.is_inlined = True
2907
3296
  if 'async' in modifiers:
2908
3297
  action.is_async = True
2909
- print(f"[EVAL_FUNC] Set is_async=True", flush=True)
3298
+ debug_log("eval_function_statement", "Set is_async=True")
2910
3299
  if 'secure' in modifiers:
2911
3300
  action.is_secure = True
2912
3301
  if 'pure' in modifiers:
@@ -2921,9 +3310,9 @@ class StatementEvaluatorMixin:
2921
3310
  except Exception:
2922
3311
  pass
2923
3312
 
2924
- 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}")
2925
3314
  env.set(node.name.value, action)
2926
- print(f"[EVAL_FUNC] Successfully set in environment", flush=True)
3315
+ debug_log("eval_function_statement", "Binding complete")
2927
3316
  return NULL
2928
3317
 
2929
3318
  # === PERFORMANCE OPTIMIZATION STATEMENTS ===
@@ -4007,10 +4396,12 @@ class StatementEvaluatorMixin:
4007
4396
  if not is_truthy(condition):
4008
4397
  # Execute tolerance block if provided (for conditional allowances)
4009
4398
  if node.tolerance_block:
4010
- 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__}")
4011
4401
  debug_log("eval_require_statement", "Condition failed - executing tolerance logic")
4012
4402
  tolerance_result = self.eval_node(node.tolerance_block, env, stack_trace)
4013
- 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__}")
4014
4405
 
4015
4406
  # Check if tolerance logic allows proceeding
4016
4407
  if is_error(tolerance_result):
@@ -4019,18 +4410,22 @@ class StatementEvaluatorMixin:
4019
4410
  # Unwrap ReturnValue if present
4020
4411
  from ..object import ReturnValue
4021
4412
  if isinstance(tolerance_result, ReturnValue):
4022
- print(f"⚡ UNWRAPPING ReturnValue")
4413
+ if zexus_config.should_log('debug'):
4414
+ print(f"⚡ UNWRAPPING ReturnValue")
4023
4415
  tolerance_result = tolerance_result.value
4024
- print(f"⚡ UNWRAPPED VALUE: {tolerance_result}")
4416
+ if zexus_config.should_log('debug'):
4417
+ print(f"⚡ UNWRAPPED VALUE: {tolerance_result}")
4025
4418
 
4026
4419
  # If tolerance block returns true/truthy, allow it
4027
4420
  if is_truthy(tolerance_result):
4028
- print(f"⚡ TOLERANCE APPROVED")
4421
+ if zexus_config.should_log('debug'):
4422
+ print(f"⚡ TOLERANCE APPROVED")
4029
4423
  debug_log("eval_require_statement", "Tolerance logic approved - allowing requirement")
4030
4424
  return NULL
4031
4425
 
4032
4426
  # If tolerance block returns false, requirement still fails
4033
- print(f"⚡ TOLERANCE REJECTED")
4427
+ if zexus_config.should_log('debug'):
4428
+ print(f"⚡ TOLERANCE REJECTED")
4034
4429
  debug_log("eval_require_statement", "Tolerance logic rejected - requirement fails")
4035
4430
  # Fall through to error below
4036
4431
 
@@ -4151,25 +4546,32 @@ class StatementEvaluatorMixin:
4151
4546
  debug_log("_eval_require_resource", f"{req_type} requirement satisfied")
4152
4547
  return NULL
4153
4548
 
4154
- def eval_revert_statement(self, node, env, stack_trace):
4155
- """Evaluate revert statement - rollback transaction.
4156
-
4157
- revert();
4158
- revert("Unauthorized");
4159
- """
4549
+ def _perform_revert(self, reason_expr, env, stack_trace):
4160
4550
  debug_log("eval_revert_statement", "Reverting transaction")
4161
-
4162
- # Evaluate revert reason if provided
4551
+
4163
4552
  reason = "Transaction reverted"
4164
- if node.reason:
4165
- 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)
4166
4555
  if isinstance(reason_val, String):
4167
4556
  reason = reason_val.value
4168
4557
  elif not is_error(reason_val):
4169
4558
  reason = str(reason_val.inspect() if hasattr(reason_val, 'inspect') else reason_val)
4170
-
4559
+
4171
4560
  debug_log("eval_revert_statement", f"REVERT: {reason}")
4172
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)
4173
4575
 
4174
4576
  def eval_limit_statement(self, node, env, stack_trace):
4175
4577
  """Evaluate limit statement - set gas limit.
@@ -4496,7 +4898,8 @@ class StatementEvaluatorMixin:
4496
4898
 
4497
4899
  # Print event for debugging (optional)
4498
4900
  args_str = ", ".join(str(arg.inspect() if hasattr(arg, 'inspect') else arg) for arg in args)
4499
- print(f"📢 Event: {event_name}({args_str})")
4901
+ if zexus_config.should_log('info'):
4902
+ print(f"📢 Event: {event_name}({args_str})")
4500
4903
 
4501
4904
  return NULL
4502
4905