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
@@ -0,0 +1,298 @@
1
+ """
2
+ Debug Engine — breakpoint / stepping state machine.
3
+
4
+ The engine is attached to the evaluator and consulted before every AST
5
+ node is dispatched. When a stop condition is met the engine parks the
6
+ execution thread on an ``Event`` and waits for the DAP front-end to
7
+ resume it.
8
+
9
+ Thread model
10
+ ~~~~~~~~~~~~
11
+ * **Execution thread** — runs the Zexus program via the evaluator.
12
+ Calls ``DebugEngine.check()`` before each node.
13
+ * **DAP I/O thread** — reads DAP JSON messages, mutates engine state,
14
+ and signals the execution thread to continue.
15
+
16
+ Locking: a single ``threading.Condition`` guards all mutable state.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import threading
22
+ from dataclasses import dataclass, field
23
+ from enum import Enum, auto
24
+ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
25
+
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Public types
29
+ # ---------------------------------------------------------------------------
30
+
31
+ class StopReason(Enum):
32
+ BREAKPOINT = auto()
33
+ STEP = auto()
34
+ PAUSE = auto()
35
+ ENTRY = auto()
36
+ EXCEPTION = auto()
37
+
38
+
39
+ class StepMode(Enum):
40
+ CONTINUE = auto()
41
+ STEP_OVER = auto()
42
+ STEP_INTO = auto()
43
+ STEP_OUT = auto()
44
+
45
+
46
+ @dataclass
47
+ class Breakpoint:
48
+ id: int
49
+ file: str
50
+ line: int
51
+ condition: Optional[str] = None
52
+ hit_count: int = 0
53
+ enabled: bool = True
54
+
55
+
56
+ @dataclass
57
+ class StackFrame:
58
+ id: int
59
+ name: str
60
+ file: str
61
+ line: int
62
+ column: int = 0
63
+ variables: Dict[str, Any] = field(default_factory=dict)
64
+
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Engine
68
+ # ---------------------------------------------------------------------------
69
+
70
+ class DebugEngine:
71
+ """Breakpoint / stepping state machine consulted by the evaluator."""
72
+
73
+ def __init__(self) -> None:
74
+ self._lock = threading.Condition()
75
+ self._paused = False
76
+ self._stop_reason: Optional[StopReason] = None
77
+ self._step_mode: StepMode = StepMode.CONTINUE
78
+ self._step_depth: int = 0 # call depth at step start
79
+ self._stop_on_entry: bool = False
80
+
81
+ # Breakpoints: file -> {line -> Breakpoint}
82
+ self._breakpoints: Dict[str, Dict[int, Breakpoint]] = {}
83
+ self._bp_id_counter: int = 0
84
+
85
+ # Current execution state (updated by evaluator hook)
86
+ self._current_file: str = ""
87
+ self._current_line: int = 0
88
+ self._call_depth: int = 0
89
+ self._call_stack: List[StackFrame] = []
90
+ self._frame_id_counter: int = 0
91
+
92
+ # Callback fired when execution stops (DAP server sends stopped event)
93
+ self.on_stopped: Optional[Callable[[StopReason, str], None]] = None
94
+ # Callback fired when execution completes
95
+ self.on_terminated: Optional[Callable[[], None]] = None
96
+
97
+ self._terminated = False
98
+
99
+ # -- Evaluator hook (called from eval_node) ----------------------------
100
+
101
+ def check(self, file: str, line: int, env: Any = None) -> None:
102
+ """Called by the evaluator **before** dispatching each AST node.
103
+
104
+ If a stop condition is met the calling thread parks here until the
105
+ DAP server calls ``continue_execution()`` or a step variant.
106
+ """
107
+ if self._terminated:
108
+ return
109
+
110
+ with self._lock:
111
+ self._current_file = file
112
+ self._current_line = line
113
+
114
+ # Update top frame
115
+ if self._call_stack:
116
+ self._call_stack[-1].line = line
117
+ if env:
118
+ self._call_stack[-1].variables = self._extract_vars(env)
119
+
120
+ reason = self._should_stop(file, line)
121
+ if reason is None:
122
+ return
123
+
124
+ self._paused = True
125
+ self._stop_reason = reason
126
+
127
+ # Notify DAP server (outside lock)
128
+ if self.on_stopped:
129
+ self.on_stopped(reason, f"{file}:{line}")
130
+
131
+ # Park execution thread
132
+ with self._lock:
133
+ while self._paused and not self._terminated:
134
+ self._lock.wait(timeout=0.5)
135
+
136
+ def notify_call(self, name: str, file: str, line: int, env: Any = None):
137
+ """Push a new frame when entering a function/action."""
138
+ with self._lock:
139
+ self._call_depth += 1
140
+ self._frame_id_counter += 1
141
+ frame = StackFrame(
142
+ id=self._frame_id_counter,
143
+ name=name,
144
+ file=file,
145
+ line=line,
146
+ variables=self._extract_vars(env) if env else {},
147
+ )
148
+ self._call_stack.append(frame)
149
+
150
+ def notify_return(self):
151
+ """Pop a frame when leaving a function/action."""
152
+ with self._lock:
153
+ self._call_depth -= 1
154
+ if self._call_stack:
155
+ self._call_stack.pop()
156
+
157
+ def notify_terminated(self):
158
+ """Signal that program execution finished."""
159
+ with self._lock:
160
+ self._terminated = True
161
+ self._paused = False
162
+ self._lock.notify_all()
163
+ if self.on_terminated:
164
+ self.on_terminated()
165
+
166
+ # -- DAP server control (called from I/O thread) -----------------------
167
+
168
+ def continue_execution(self):
169
+ with self._lock:
170
+ self._step_mode = StepMode.CONTINUE
171
+ self._paused = False
172
+ self._lock.notify_all()
173
+
174
+ def step_over(self):
175
+ with self._lock:
176
+ self._step_mode = StepMode.STEP_OVER
177
+ self._step_depth = self._call_depth
178
+ self._paused = False
179
+ self._lock.notify_all()
180
+
181
+ def step_into(self):
182
+ with self._lock:
183
+ self._step_mode = StepMode.STEP_INTO
184
+ self._paused = False
185
+ self._lock.notify_all()
186
+
187
+ def step_out(self):
188
+ with self._lock:
189
+ self._step_mode = StepMode.STEP_OUT
190
+ self._step_depth = self._call_depth
191
+ self._paused = False
192
+ self._lock.notify_all()
193
+
194
+ def pause(self):
195
+ """Request the execution to pause at the next node."""
196
+ with self._lock:
197
+ self._step_mode = StepMode.STEP_INTO # stop on next node
198
+
199
+ def terminate(self):
200
+ with self._lock:
201
+ self._terminated = True
202
+ self._paused = False
203
+ self._lock.notify_all()
204
+
205
+ # -- Breakpoint management ---------------------------------------------
206
+
207
+ def set_breakpoints(self, file: str, lines: List[int]) -> List[Breakpoint]:
208
+ """Replace breakpoints for *file* with the given *lines*."""
209
+ bps: Dict[int, Breakpoint] = {}
210
+ result = []
211
+ for ln in lines:
212
+ self._bp_id_counter += 1
213
+ bp = Breakpoint(id=self._bp_id_counter, file=file, line=ln)
214
+ bps[ln] = bp
215
+ result.append(bp)
216
+ with self._lock:
217
+ self._breakpoints[file] = bps
218
+ return result
219
+
220
+ def clear_breakpoints(self, file: str):
221
+ with self._lock:
222
+ self._breakpoints.pop(file, None)
223
+
224
+ # -- Inspection --------------------------------------------------------
225
+
226
+ @property
227
+ def is_paused(self) -> bool:
228
+ return self._paused
229
+
230
+ def get_stack_trace(self) -> List[StackFrame]:
231
+ with self._lock:
232
+ return list(reversed(self._call_stack))
233
+
234
+ def get_variables(self, frame_id: int) -> Dict[str, Any]:
235
+ with self._lock:
236
+ for f in self._call_stack:
237
+ if f.id == frame_id:
238
+ return dict(f.variables)
239
+ return {}
240
+
241
+ def set_stop_on_entry(self, stop: bool):
242
+ self._stop_on_entry = stop
243
+
244
+ # -- Internal ----------------------------------------------------------
245
+
246
+ def _should_stop(self, file: str, line: int) -> Optional[StopReason]:
247
+ """Determine if execution should stop at this location.
248
+
249
+ Called with ``_lock`` held.
250
+ """
251
+ # Stop on entry (first node)
252
+ if self._stop_on_entry:
253
+ self._stop_on_entry = False
254
+ return StopReason.ENTRY
255
+
256
+ # Breakpoint hit
257
+ file_bps = self._breakpoints.get(file)
258
+ if file_bps:
259
+ bp = file_bps.get(line)
260
+ if bp and bp.enabled:
261
+ bp.hit_count += 1
262
+ return StopReason.BREAKPOINT
263
+
264
+ # Stepping logic
265
+ if self._step_mode == StepMode.STEP_INTO:
266
+ return StopReason.STEP
267
+ if self._step_mode == StepMode.STEP_OVER:
268
+ if self._call_depth <= self._step_depth:
269
+ return StopReason.STEP
270
+ if self._step_mode == StepMode.STEP_OUT:
271
+ if self._call_depth < self._step_depth:
272
+ return StopReason.STEP
273
+
274
+ return None
275
+
276
+ @staticmethod
277
+ def _extract_vars(env: Any) -> Dict[str, Any]:
278
+ """Pull variable names from a Zexus Environment."""
279
+ result: Dict[str, Any] = {}
280
+ store = getattr(env, "store", None) or getattr(env, "_store", None)
281
+ if isinstance(store, dict):
282
+ for k, v in store.items():
283
+ try:
284
+ result[k] = _format_value(v)
285
+ except Exception:
286
+ result[k] = str(v)
287
+ return result
288
+
289
+
290
+ def _format_value(v: Any) -> str:
291
+ """Format a Zexus runtime value for display in the debugger."""
292
+ if v is None:
293
+ return "null"
294
+ if hasattr(v, "inspect"):
295
+ return v.inspect()
296
+ if hasattr(v, "value"):
297
+ return str(v.value)
298
+ return str(v)
@@ -1,4 +1,32 @@
1
- # environment.py
1
+ """Environment helpers with export-aware values access."""
2
+
3
+ from .config import config as zexus_config
4
+
5
+
6
+ class _EnvironmentValuesProxy:
7
+ """Proxy that behaves like both callable view and export lookup."""
8
+
9
+ def __init__(self, env):
10
+ self._env = env
11
+
12
+ def __contains__(self, name):
13
+ return name in self._env.exports
14
+
15
+ def __iter__(self):
16
+ return iter(self._env.exports)
17
+
18
+ def __len__(self):
19
+ return len(self._env.exports)
20
+
21
+ def __call__(self):
22
+ return self._env.store.values()
23
+
24
+ def keys(self):
25
+ return self._env.exports.keys()
26
+
27
+ def items(self):
28
+ return self._env.exports.items()
29
+
2
30
 
3
31
  class Environment:
4
32
  def __init__(self, outer=None):
@@ -7,13 +35,75 @@ class Environment:
7
35
  self.exports = {}
8
36
  self.modules = {}
9
37
  self._debug = False
38
+ self._values_proxy = _EnvironmentValuesProxy(self)
39
+
40
+ # ---- Mapping protocol helpers -------------------------------------------------
41
+
42
+ def __contains__(self, name):
43
+ if name in self.store:
44
+ return True
45
+
46
+ if "." in name:
47
+ module_name, var_name = name.split(".", 1)
48
+ module = self.modules.get(module_name)
49
+ if module and var_name in module:
50
+ return True
10
51
 
11
- def get(self, name):
52
+ if self.outer is not None and hasattr(self.outer, '__contains__'):
53
+ try:
54
+ return name in self.outer
55
+ except TypeError:
56
+ pass
57
+ return False
58
+
59
+ def __getitem__(self, name):
60
+ return self.get(name)
61
+
62
+ def __setitem__(self, name, value):
63
+ self.set(name, value)
64
+
65
+ def __iter__(self):
66
+ return iter(self.store)
67
+
68
+ def __len__(self):
69
+ return len(self.store)
70
+
71
+ def keys(self):
72
+ return self.store.keys()
73
+
74
+ def items(self):
75
+ return self.store.items()
76
+
77
+ @property
78
+ def values(self):
79
+ return self._values_proxy
80
+
81
+ def copy(self):
82
+ return dict(self.store)
83
+
84
+ def update(self, other):
85
+ if other is None:
86
+ return
87
+ if hasattr(other, 'items'):
88
+ for key, value in other.items():
89
+ self.set(key, value)
90
+ else:
91
+ for key in other:
92
+ self.set(key, other[key])
93
+
94
+ def setdefault(self, name, default=None):
95
+ if name in self:
96
+ return self.get(name)
97
+ self.set(name, default)
98
+ return default
99
+
100
+ # ---- Core environment operations ---------------------------------------------
101
+
102
+ def get(self, name, default=None):
12
103
  """Get a value from the environment"""
13
- # Check local store
14
- value = self.store.get(name)
15
- if value is not None:
16
- return value
104
+ # Check local store (allow storing None explicitly)
105
+ if name in self.store:
106
+ return self.store[name]
17
107
 
18
108
  # Check modules
19
109
  if "." in name:
@@ -24,9 +114,18 @@ class Environment:
24
114
 
25
115
  # Check outer scope
26
116
  if self.outer:
27
- return self.outer.get(name)
28
-
29
- return None
117
+ getter = getattr(self.outer, 'get', None)
118
+ if callable(getter):
119
+ try:
120
+ return getter(name, default)
121
+ except TypeError:
122
+ return getter(name)
123
+ try:
124
+ return self.outer[name]
125
+ except (KeyError, TypeError, AttributeError):
126
+ pass
127
+
128
+ return default
30
129
 
31
130
  def set(self, name, value):
32
131
  """Set a value in the environment (creates new variable)"""
@@ -41,6 +140,8 @@ class Environment:
41
140
  module.set(var_name, value)
42
141
  self.modules[module_name] = module
43
142
  else:
143
+ if name == 'tx' and zexus_config.should_log('debug'):
144
+ print(f"DEBUG env.set tx -> {value}")
44
145
  self.store[name] = value
45
146
 
46
147
  def assign(self, name, value):
@@ -52,6 +153,8 @@ class Environment:
52
153
  """
53
154
  # Check if variable exists in current scope
54
155
  if name in self.store:
156
+ if name == 'tx' and zexus_config.should_log('debug'):
157
+ print(f"DEBUG env.assign tx -> {value}")
55
158
  self.store[name] = value
56
159
  return
57
160