zexus 1.6.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 (227) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +2513 -0
  3. package/bin/zexus +2 -0
  4. package/bin/zpics +2 -0
  5. package/bin/zpm +2 -0
  6. package/bin/zx +2 -0
  7. package/bin/zx-deploy +2 -0
  8. package/bin/zx-dev +2 -0
  9. package/bin/zx-run +2 -0
  10. package/package.json +66 -0
  11. package/scripts/README.md +24 -0
  12. package/scripts/postinstall.js +44 -0
  13. package/shared_config.json +24 -0
  14. package/src/README.md +1525 -0
  15. package/src/tests/run_zexus_tests.py +117 -0
  16. package/src/tests/test_all_phases.zx +346 -0
  17. package/src/tests/test_blockchain_features.zx +306 -0
  18. package/src/tests/test_complexity_features.zx +321 -0
  19. package/src/tests/test_core_integration.py +185 -0
  20. package/src/tests/test_phase10_ecosystem.zx +177 -0
  21. package/src/tests/test_phase1_modifiers.zx +87 -0
  22. package/src/tests/test_phase2_plugins.zx +80 -0
  23. package/src/tests/test_phase3_security.zx +97 -0
  24. package/src/tests/test_phase4_vfs.zx +116 -0
  25. package/src/tests/test_phase5_types.zx +117 -0
  26. package/src/tests/test_phase6_metaprogramming.zx +125 -0
  27. package/src/tests/test_phase7_optimization.zx +132 -0
  28. package/src/tests/test_phase9_advanced_types.zx +157 -0
  29. package/src/tests/test_security_features.py +419 -0
  30. package/src/tests/test_security_features.zx +276 -0
  31. package/src/tests/test_simple_zx.zx +1 -0
  32. package/src/tests/test_verification_simple.zx +69 -0
  33. package/src/zexus/__init__.py +28 -0
  34. package/src/zexus/__main__.py +5 -0
  35. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
  37. package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
  38. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  39. package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
  40. package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
  41. package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
  42. package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
  43. package/src/zexus/__pycache__/ecosystem.cpython-312.pyc +0 -0
  44. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  45. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  46. package/src/zexus/__pycache__/hybrid_orchestrator.cpython-312.pyc +0 -0
  47. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  48. package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
  49. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  50. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  51. package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
  52. package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
  53. package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
  54. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  55. package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
  56. package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
  57. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  58. package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
  59. package/src/zexus/__pycache__/virtual_filesystem.cpython-312.pyc +0 -0
  60. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  61. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  62. package/src/zexus/advanced_types.py +401 -0
  63. package/src/zexus/blockchain/__init__.py +40 -0
  64. package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
  66. package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
  67. package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
  68. package/src/zexus/blockchain/crypto.py +463 -0
  69. package/src/zexus/blockchain/ledger.py +255 -0
  70. package/src/zexus/blockchain/transaction.py +267 -0
  71. package/src/zexus/builtin_modules.py +284 -0
  72. package/src/zexus/builtin_plugins.py +317 -0
  73. package/src/zexus/capability_system.py +372 -0
  74. package/src/zexus/cli/__init__.py +2 -0
  75. package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  76. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  77. package/src/zexus/cli/main.py +707 -0
  78. package/src/zexus/cli/zpm.py +203 -0
  79. package/src/zexus/compare_interpreter_compiler.py +146 -0
  80. package/src/zexus/compiler/__init__.py +169 -0
  81. package/src/zexus/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
  82. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  83. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  84. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  85. package/src/zexus/compiler/bytecode.py +266 -0
  86. package/src/zexus/compiler/compat_runtime.py +277 -0
  87. package/src/zexus/compiler/lexer.py +257 -0
  88. package/src/zexus/compiler/parser.py +779 -0
  89. package/src/zexus/compiler/semantic.py +118 -0
  90. package/src/zexus/compiler/zexus_ast.py +454 -0
  91. package/src/zexus/complexity_system.py +575 -0
  92. package/src/zexus/concurrency_system.py +493 -0
  93. package/src/zexus/config.py +201 -0
  94. package/src/zexus/crypto_bridge.py +19 -0
  95. package/src/zexus/dependency_injection.py +423 -0
  96. package/src/zexus/ecosystem.py +434 -0
  97. package/src/zexus/environment.py +101 -0
  98. package/src/zexus/environment_manager.py +119 -0
  99. package/src/zexus/error_reporter.py +314 -0
  100. package/src/zexus/evaluator/__init__.py +12 -0
  101. package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  103. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  104. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  105. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  106. package/src/zexus/evaluator/__pycache__/integration.cpython-312.pyc +0 -0
  107. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  108. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  109. package/src/zexus/evaluator/bytecode_compiler.py +700 -0
  110. package/src/zexus/evaluator/core.py +891 -0
  111. package/src/zexus/evaluator/expressions.py +827 -0
  112. package/src/zexus/evaluator/functions.py +3989 -0
  113. package/src/zexus/evaluator/integration.py +396 -0
  114. package/src/zexus/evaluator/statements.py +4303 -0
  115. package/src/zexus/evaluator/utils.py +126 -0
  116. package/src/zexus/evaluator_original.py +2041 -0
  117. package/src/zexus/external_bridge.py +16 -0
  118. package/src/zexus/find_affected_imports.sh +155 -0
  119. package/src/zexus/hybrid_orchestrator.py +152 -0
  120. package/src/zexus/input_validation.py +259 -0
  121. package/src/zexus/lexer.py +571 -0
  122. package/src/zexus/logging.py +89 -0
  123. package/src/zexus/lsp/__init__.py +9 -0
  124. package/src/zexus/lsp/completion_provider.py +207 -0
  125. package/src/zexus/lsp/definition_provider.py +22 -0
  126. package/src/zexus/lsp/hover_provider.py +71 -0
  127. package/src/zexus/lsp/server.py +269 -0
  128. package/src/zexus/lsp/symbol_provider.py +31 -0
  129. package/src/zexus/metaprogramming.py +321 -0
  130. package/src/zexus/module_cache.py +89 -0
  131. package/src/zexus/module_manager.py +107 -0
  132. package/src/zexus/object.py +973 -0
  133. package/src/zexus/optimization.py +424 -0
  134. package/src/zexus/parser/__init__.py +31 -0
  135. package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
  136. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  137. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  138. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  139. package/src/zexus/parser/integration.py +86 -0
  140. package/src/zexus/parser/parser.py +3977 -0
  141. package/src/zexus/parser/strategy_context.py +7254 -0
  142. package/src/zexus/parser/strategy_structural.py +1033 -0
  143. package/src/zexus/persistence.py +391 -0
  144. package/src/zexus/plugin_system.py +290 -0
  145. package/src/zexus/policy_engine.py +365 -0
  146. package/src/zexus/profiler/__init__.py +5 -0
  147. package/src/zexus/profiler/profiler.py +233 -0
  148. package/src/zexus/purity_system.py +398 -0
  149. package/src/zexus/runtime/__init__.py +20 -0
  150. package/src/zexus/runtime/async_runtime.py +324 -0
  151. package/src/zexus/search_old_imports.sh +65 -0
  152. package/src/zexus/security.py +1407 -0
  153. package/src/zexus/stack_trace.py +233 -0
  154. package/src/zexus/stdlib/__init__.py +27 -0
  155. package/src/zexus/stdlib/blockchain.py +341 -0
  156. package/src/zexus/stdlib/compression.py +167 -0
  157. package/src/zexus/stdlib/crypto.py +124 -0
  158. package/src/zexus/stdlib/datetime.py +163 -0
  159. package/src/zexus/stdlib/db_mongo.py +199 -0
  160. package/src/zexus/stdlib/db_mysql.py +162 -0
  161. package/src/zexus/stdlib/db_postgres.py +163 -0
  162. package/src/zexus/stdlib/db_sqlite.py +133 -0
  163. package/src/zexus/stdlib/encoding.py +230 -0
  164. package/src/zexus/stdlib/fs.py +195 -0
  165. package/src/zexus/stdlib/http.py +219 -0
  166. package/src/zexus/stdlib/http_server.py +248 -0
  167. package/src/zexus/stdlib/json_module.py +61 -0
  168. package/src/zexus/stdlib/math.py +360 -0
  169. package/src/zexus/stdlib/os_module.py +265 -0
  170. package/src/zexus/stdlib/regex.py +148 -0
  171. package/src/zexus/stdlib/sockets.py +253 -0
  172. package/src/zexus/stdlib/test_framework.zx +208 -0
  173. package/src/zexus/stdlib/test_runner.zx +119 -0
  174. package/src/zexus/stdlib_integration.py +341 -0
  175. package/src/zexus/strategy_recovery.py +256 -0
  176. package/src/zexus/syntax_validator.py +356 -0
  177. package/src/zexus/testing/zpics.py +407 -0
  178. package/src/zexus/testing/zpics_runtime.py +369 -0
  179. package/src/zexus/type_system.py +374 -0
  180. package/src/zexus/validation_system.py +569 -0
  181. package/src/zexus/virtual_filesystem.py +355 -0
  182. package/src/zexus/vm/__init__.py +8 -0
  183. package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
  184. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  185. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  186. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  187. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  188. package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
  189. package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
  190. package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
  191. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  192. package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
  193. package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
  194. package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
  195. package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
  196. package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
  197. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  198. package/src/zexus/vm/async_optimizer.py +420 -0
  199. package/src/zexus/vm/bytecode.py +428 -0
  200. package/src/zexus/vm/bytecode_converter.py +297 -0
  201. package/src/zexus/vm/cache.py +532 -0
  202. package/src/zexus/vm/jit.py +720 -0
  203. package/src/zexus/vm/memory_manager.py +520 -0
  204. package/src/zexus/vm/memory_pool.py +511 -0
  205. package/src/zexus/vm/optimizer.py +478 -0
  206. package/src/zexus/vm/parallel_vm.py +899 -0
  207. package/src/zexus/vm/peephole_optimizer.py +452 -0
  208. package/src/zexus/vm/profiler.py +527 -0
  209. package/src/zexus/vm/register_allocator.py +462 -0
  210. package/src/zexus/vm/register_vm.py +520 -0
  211. package/src/zexus/vm/ssa_converter.py +757 -0
  212. package/src/zexus/vm/vm.py +1392 -0
  213. package/src/zexus/zexus_ast.py +1782 -0
  214. package/src/zexus/zexus_token.py +253 -0
  215. package/src/zexus/zpm/__init__.py +15 -0
  216. package/src/zexus/zpm/installer.py +116 -0
  217. package/src/zexus/zpm/package_manager.py +208 -0
  218. package/src/zexus/zpm/publisher.py +98 -0
  219. package/src/zexus/zpm/registry.py +110 -0
  220. package/src/zexus.egg-info/PKG-INFO +2235 -0
  221. package/src/zexus.egg-info/SOURCES.txt +876 -0
  222. package/src/zexus.egg-info/dependency_links.txt +1 -0
  223. package/src/zexus.egg-info/entry_points.txt +3 -0
  224. package/src/zexus.egg-info/not-zip-safe +1 -0
  225. package/src/zexus.egg-info/requires.txt +14 -0
  226. package/src/zexus.egg-info/top_level.txt +2 -0
  227. package/zexus.json +14 -0
@@ -0,0 +1,973 @@
1
+ # src/zexus/object.py
2
+ import time
3
+ import random
4
+ import json
5
+ import os
6
+ import sys
7
+ from threading import Lock
8
+
9
+ class Object:
10
+ def inspect(self):
11
+ raise NotImplementedError("Subclasses must implement this method")
12
+
13
+ # === EXISTING TYPES ===
14
+ class Integer(Object):
15
+ def __init__(self, value): self.value = value
16
+ def inspect(self): return str(self.value)
17
+ def type(self): return "INTEGER"
18
+
19
+ class Float(Object):
20
+ def __init__(self, value): self.value = value
21
+ def inspect(self): return str(self.value)
22
+ def type(self): return "FLOAT"
23
+
24
+ class Boolean(Object):
25
+ def __init__(self, value): self.value = value
26
+ def inspect(self): return "true" if self.value else "false"
27
+ def type(self): return "BOOLEAN"
28
+
29
+ class Null(Object):
30
+ def inspect(self): return "null"
31
+ def type(self): return "NULL"
32
+
33
+ class String(Object):
34
+ def __init__(self, value): self.value = value
35
+ def inspect(self): return self.value
36
+ def type(self): return "STRING"
37
+ def __str__(self): return self.value
38
+ def __eq__(self, other):
39
+ """Enable String objects to be used as dict keys"""
40
+ if isinstance(other, String):
41
+ return self.value == other.value
42
+ return False
43
+ def __hash__(self):
44
+ """Enable String objects to be used as dict keys"""
45
+ return hash(self.value)
46
+
47
+ class List(Object):
48
+ def __init__(self, elements): self.elements = elements
49
+ def inspect(self):
50
+ elements_str = ", ".join([el.inspect() for el in self.elements])
51
+ return f"[{elements_str}]"
52
+ def type(self): return "LIST"
53
+
54
+ def get(self, index):
55
+ """Get element by index"""
56
+ try:
57
+ # Handle Integer object or raw int
58
+ idx = index.value if hasattr(index, 'value') else index
59
+ idx = int(idx)
60
+ if 0 <= idx < len(self.elements):
61
+ return self.elements[idx]
62
+ return NULL
63
+ except Exception:
64
+ return NULL
65
+
66
+ def append(self, item):
67
+ """Append item to list in-place (mutating operation)"""
68
+ self.elements.append(item)
69
+ return self # Return self for method chaining
70
+
71
+ def extend(self, other_list):
72
+ """Extend list with another list in-place"""
73
+ if isinstance(other_list, List):
74
+ self.elements.extend(other_list.elements)
75
+ return self
76
+
77
+ class Map(Object):
78
+ def __init__(self, pairs):
79
+ self.pairs = pairs
80
+
81
+ def type(self): return "MAP"
82
+ def inspect(self):
83
+ pairs = []
84
+ for key, value in self.pairs.items():
85
+ key_str = key.inspect() if hasattr(key, 'inspect') else str(key)
86
+ value_str = value.inspect() if hasattr(value, 'inspect') else str(value)
87
+ pairs.append(f"{key_str}: {value_str}")
88
+ return "{" + ", ".join(pairs) + "}"
89
+
90
+ def get(self, key):
91
+ """Get value by key (compatible with string keys)"""
92
+ return self.pairs.get(key)
93
+
94
+ def set(self, key, value):
95
+ """Set value for key, blocking modification if key is sealed."""
96
+ existing = self.pairs.get(key)
97
+ if existing is not None and existing.__class__.__name__ == 'SealedObject':
98
+ raise EvaluationError(f"Cannot modify sealed map key: {key}")
99
+ self.pairs[key] = value
100
+
101
+ class EmbeddedCode(Object):
102
+ def __init__(self, name, language, code):
103
+ self.name = name
104
+ self.language = language
105
+ self.code = code
106
+ def inspect(self): return f"<embedded {self.language} code: {self.name}>"
107
+ def type(self): return "EMBEDDED_CODE"
108
+
109
+ class ReturnValue(Object):
110
+ def __init__(self, value): self.value = value
111
+ def inspect(self): return self.value.inspect()
112
+ def type(self): return "RETURN_VALUE"
113
+
114
+ class Action(Object):
115
+ def __init__(self, parameters, body, env):
116
+ self.parameters, self.body, self.env = parameters, body, env
117
+ def inspect(self):
118
+ params = ", ".join([p.value for p in self.parameters])
119
+ return f"action({params}) {{\n ...\n}}"
120
+ def type(self): return "ACTION"
121
+
122
+ class LambdaFunction(Object):
123
+ def __init__(self, parameters, body, env):
124
+ self.parameters = parameters
125
+ self.body = body
126
+ self.env = env
127
+ def inspect(self):
128
+ params = ", ".join([p.value for p in self.parameters])
129
+ return f"lambda({params})"
130
+ def type(self): return "LAMBDA_FUNCTION"
131
+ class Modifier(Object):
132
+ """Function modifier for access control and validation"""
133
+ def __init__(self, name, parameters, body, env):
134
+ self.name = name
135
+ self.parameters = parameters
136
+ self.body = body
137
+ self.env = env
138
+ def inspect(self):
139
+ params = ", ".join([p.value for p in self.parameters]) if self.parameters else ""
140
+ return f"modifier {self.name}({params})"
141
+ def type(self): return "MODIFIER"
142
+
143
+
144
+ class Builtin(Object):
145
+ def __init__(self, fn, name=""):
146
+ self.fn = fn
147
+ self.name = name
148
+ def inspect(self): return f"<built-in function: {self.name}>"
149
+ def type(self): return "BUILTIN"
150
+
151
+ # === ASYNC/AWAIT OBJECTS ===
152
+
153
+ class Promise(Object):
154
+ """
155
+ Promise object representing an async operation
156
+ States: PENDING, FULFILLED, REJECTED
157
+ """
158
+ PENDING = "PENDING"
159
+ FULFILLED = "FULFILLED"
160
+ REJECTED = "REJECTED"
161
+
162
+ def __init__(self, executor=None, env=None, stack_trace=None):
163
+ self.state = Promise.PENDING
164
+ self.value = None
165
+ self.error = None
166
+ self.then_callbacks = []
167
+ self.catch_callbacks = []
168
+ self.finally_callbacks = []
169
+
170
+ # Async context propagation
171
+ self.env = env # Environment at promise creation
172
+ self.stack_trace = stack_trace or [] # Stack trace context
173
+
174
+ # If executor provided, run it immediately
175
+ if executor:
176
+ try:
177
+ executor(self._resolve, self._reject)
178
+ except Exception as e:
179
+ self._reject(e)
180
+
181
+ def _resolve(self, value):
182
+ """Resolve the promise with a value"""
183
+ if self.state != Promise.PENDING:
184
+ return
185
+ self.state = Promise.FULFILLED
186
+ self.value = value
187
+
188
+ # Execute then callbacks
189
+ for callback in self.then_callbacks:
190
+ try:
191
+ callback(value)
192
+ except Exception:
193
+ pass
194
+
195
+ # Execute finally callbacks
196
+ for callback in self.finally_callbacks:
197
+ try:
198
+ callback()
199
+ except Exception:
200
+ pass
201
+
202
+ def _reject(self, error):
203
+ """Reject the promise with an error"""
204
+ if self.state != Promise.PENDING:
205
+ return
206
+ self.state = Promise.REJECTED
207
+ self.error = error
208
+
209
+ # Execute catch callbacks
210
+ for callback in self.catch_callbacks:
211
+ try:
212
+ callback(error)
213
+ except Exception:
214
+ pass
215
+
216
+ # Execute finally callbacks
217
+ for callback in self.finally_callbacks:
218
+ try:
219
+ callback()
220
+ except Exception:
221
+ pass
222
+
223
+ def then(self, callback):
224
+ """Add a success callback"""
225
+ if self.state == Promise.FULFILLED:
226
+ callback(self.value)
227
+ elif self.state == Promise.PENDING:
228
+ self.then_callbacks.append(callback)
229
+ return self
230
+
231
+ def catch(self, callback):
232
+ """Add an error callback"""
233
+ if self.state == Promise.REJECTED:
234
+ callback(self.error)
235
+ elif self.state == Promise.PENDING:
236
+ self.catch_callbacks.append(callback)
237
+ return self
238
+
239
+ def finally_callback(self, callback):
240
+ """Add a finally callback (runs regardless of outcome)"""
241
+ if self.state != Promise.PENDING:
242
+ callback()
243
+ else:
244
+ self.finally_callbacks.append(callback)
245
+ return self
246
+
247
+ def is_resolved(self):
248
+ """Check if promise is resolved (fulfilled or rejected)"""
249
+ return self.state != Promise.PENDING
250
+
251
+ def get_value(self):
252
+ """Get the promise value (blocks if pending)"""
253
+ if self.state == Promise.FULFILLED:
254
+ return self.value
255
+ elif self.state == Promise.REJECTED:
256
+ raise Exception(f"Promise rejected: {self.error}")
257
+ else:
258
+ raise Exception("Promise is still pending")
259
+
260
+ def inspect(self):
261
+ if self.state == Promise.PENDING:
262
+ return "Promise { <pending> }"
263
+ elif self.state == Promise.FULFILLED:
264
+ value_str = self.value.inspect() if hasattr(self.value, 'inspect') else str(self.value)
265
+ return f"Promise {{ <fulfilled>: {value_str} }}"
266
+ else:
267
+ return f"Promise {{ <rejected>: {self.error} }}"
268
+
269
+ def type(self):
270
+ return "PROMISE"
271
+
272
+
273
+ class Coroutine(Object):
274
+ """
275
+ Coroutine object representing an async function execution
276
+ Wraps a generator/iterator for suspension and resumption
277
+ """
278
+ def __init__(self, generator, action):
279
+ self.generator = generator
280
+ self.action = action # The async action that created this coroutine
281
+ self.is_complete = False
282
+ self.result = None
283
+ self.error = None
284
+
285
+ def resume(self, value=None):
286
+ """Resume coroutine execution, returns (is_done, value/error)"""
287
+ try:
288
+ if self.is_complete:
289
+ return (True, self.result)
290
+
291
+ # Send value to generator and get next yielded value
292
+ next_value = self.generator.send(value)
293
+ return (False, next_value)
294
+ except StopIteration as e:
295
+ self.is_complete = True
296
+ self.result = e.value if hasattr(e, 'value') else None
297
+ return (True, self.result)
298
+ except Exception as e:
299
+ self.is_complete = True
300
+ self.error = e
301
+ return (True, e)
302
+
303
+ def inspect(self):
304
+ if self.is_complete:
305
+ if self.result is not None:
306
+ result_str = self.result.inspect() if hasattr(self.result, 'inspect') else str(self.result)
307
+ return f"Coroutine {{ <complete>: {result_str} }}"
308
+ return "Coroutine { <complete>: null }"
309
+ return "Coroutine { <running> }"
310
+
311
+ def type(self):
312
+ return "COROUTINE"
313
+
314
+ # === ENTITY OBJECTS ===
315
+
316
+ class EntityDefinition(Object):
317
+ def __init__(self, name, properties, parent=None):
318
+ self.name = name
319
+ self.properties = properties # List of property definitions
320
+ self.parent = parent # Optional parent entity for inheritance
321
+
322
+ def type(self):
323
+ return "ENTITY_DEF"
324
+
325
+ def inspect(self):
326
+ # Handle both dict format {prop_name: {type: ..., default_value: ...}}
327
+ # and list format [{name: ..., type: ...}]
328
+ if isinstance(self.properties, dict):
329
+ props_str = ", ".join([f"{name}: {info['type']}" for name, info in self.properties.items()])
330
+ else:
331
+ props_str = ", ".join([f"{prop['name']}: {prop['type']}" for prop in self.properties])
332
+ return f"entity {self.name} {{ {props_str} }}"
333
+
334
+ def create_instance(self, initial_values=None):
335
+ """Create an instance of this entity with optional initial values"""
336
+ return EntityInstance(self, initial_values or {})
337
+
338
+ class EntityInstance(Object):
339
+ def __init__(self, entity_def, values):
340
+ self.entity_def = entity_def
341
+ self.values = values
342
+
343
+ def type(self):
344
+ return "ENTITY_INSTANCE"
345
+
346
+ def inspect(self):
347
+ values_str = ", ".join([f"{k}: {v.inspect()}" for k, v in self.values.items()])
348
+ return f"{self.entity_def.name} {{ {values_str} }}"
349
+
350
+ def get(self, property_name):
351
+ return self.values.get(property_name, NULL)
352
+
353
+ def set(self, property_name, value):
354
+ # Check if property exists in entity definition
355
+ prop_def = next((prop for prop in self.entity_def.properties if prop['name'] == property_name), None)
356
+ if prop_def:
357
+ self.values[property_name] = value
358
+ return TRUE
359
+ return FALSE
360
+
361
+ # === UTILITY CLASSES ===
362
+
363
+ class DateTime(Object):
364
+ def __init__(self, timestamp=None):
365
+ self.timestamp = timestamp or time.time()
366
+
367
+ def inspect(self):
368
+ return f"<DateTime: {self.timestamp}>"
369
+
370
+ def type(self):
371
+ return "DATETIME"
372
+
373
+ @staticmethod
374
+ def now():
375
+ return DateTime(time.time())
376
+
377
+ def to_timestamp(self):
378
+ return Integer(int(self.timestamp))
379
+
380
+ def __str__(self):
381
+ return str(self.timestamp)
382
+
383
+ class Math(Object):
384
+ def type(self):
385
+ return "MATH_UTILITY"
386
+
387
+ def inspect(self):
388
+ return "<Math utilities>"
389
+
390
+ @staticmethod
391
+ def random_int(min_val, max_val):
392
+ return Integer(random.randint(min_val, max_val))
393
+
394
+ @staticmethod
395
+ def to_hex_string(number):
396
+ if isinstance(number, Integer):
397
+ return String(hex(number.value))
398
+ return String(hex(number))
399
+
400
+ @staticmethod
401
+ def hex_to_int(hex_string):
402
+ if isinstance(hex_string, String):
403
+ return Integer(int(hex_string.value, 16))
404
+ return Integer(int(hex_string, 16))
405
+
406
+ @staticmethod
407
+ def sqrt(number):
408
+ if isinstance(number, Integer):
409
+ return Float(number.value ** 0.5)
410
+ elif isinstance(number, Float):
411
+ return Float(number.value ** 0.5)
412
+ return Null()
413
+
414
+ class File(Object):
415
+ def type(self):
416
+ return "FILE_UTILITY"
417
+
418
+ def inspect(self):
419
+ return "<File I/O utilities>"
420
+
421
+ # === BASIC TIER ===
422
+ @staticmethod
423
+ def read_text(path):
424
+ try:
425
+ if isinstance(path, String):
426
+ path = path.value
427
+ with open(path, 'r', encoding='utf-8') as f:
428
+ return String(f.read())
429
+ except Exception as e:
430
+ return EvaluationError(f"File read error: {str(e)}")
431
+
432
+ @staticmethod
433
+ def write_text(path, content):
434
+ try:
435
+ if isinstance(path, String):
436
+ path = path.value
437
+ if isinstance(content, String):
438
+ content = content.value
439
+ with open(path, 'w', encoding='utf-8') as f:
440
+ f.write(content)
441
+ return Boolean(True)
442
+ except Exception as e:
443
+ return EvaluationError(f"File write error: {str(e)}")
444
+
445
+ @staticmethod
446
+ def exists(path):
447
+ if isinstance(path, String):
448
+ path = path.value
449
+ return Boolean(os.path.exists(path))
450
+
451
+ # === MEDIUM TIER ===
452
+ @staticmethod
453
+ def read_json(path):
454
+ try:
455
+ content = File.read_text(path)
456
+ if isinstance(content, EvaluationError):
457
+ return content
458
+ data = json.loads(content.value)
459
+ # Convert Python data to Zexus objects
460
+ return File._python_to_zexus(data)
461
+ except Exception as e:
462
+ return EvaluationError(f"JSON read error: {str(e)}")
463
+
464
+ @staticmethod
465
+ def write_json(path, data):
466
+ try:
467
+ # Always convert Zexus objects to Python objects
468
+ python_data = File._zexus_to_python(data)
469
+ json_str = json.dumps(python_data, indent=2)
470
+ return File.write_text(path, String(json_str))
471
+ except Exception as e:
472
+ return EvaluationError(f"JSON write error: {str(e)}")
473
+
474
+ @staticmethod
475
+ def append_text(path, content):
476
+ try:
477
+ if isinstance(path, String):
478
+ path = path.value
479
+ if isinstance(content, String):
480
+ content = content.value
481
+ with open(path, 'a', encoding='utf-8') as f:
482
+ f.write(content + '\n')
483
+ return Boolean(True)
484
+ except Exception as e:
485
+ return EvaluationError(f"File append error: {str(e)}")
486
+
487
+ @staticmethod
488
+ def list_directory(path):
489
+ try:
490
+ if isinstance(path, String):
491
+ path = path.value
492
+ files = os.listdir(path)
493
+ return List([String(f) for f in files])
494
+ except Exception as e:
495
+ return EvaluationError(f"Directory list error: {str(e)}")
496
+
497
+ # === ADVANCED TIER ===
498
+ @staticmethod
499
+ def read_chunk(path, offset, length):
500
+ try:
501
+ if isinstance(path, String):
502
+ path = path.value
503
+ if isinstance(offset, Integer):
504
+ offset = offset.value
505
+ if isinstance(length, Integer):
506
+ length = length.value
507
+
508
+ with open(path, 'rb') as f:
509
+ f.seek(offset)
510
+ data = f.read(length)
511
+ return String(data.hex()) # Return as hex string
512
+ except Exception as e:
513
+ return EvaluationError(f"File chunk read error: {str(e)}")
514
+
515
+ @staticmethod
516
+ def write_chunk(path, offset, data):
517
+ try:
518
+ if isinstance(path, String):
519
+ path = path.value
520
+ if isinstance(offset, Integer):
521
+ offset = offset.value
522
+ if isinstance(data, String):
523
+ data = bytes.fromhex(data.value)
524
+
525
+ with open(path, 'r+b') as f:
526
+ f.seek(offset)
527
+ f.write(data)
528
+ return Boolean(True)
529
+ except Exception as e:
530
+ return EvaluationError(f"File chunk write error: {str(e)}")
531
+
532
+ @staticmethod
533
+ def atomic_write(path, data):
534
+ """Atomic write to prevent corruption"""
535
+ try:
536
+ if isinstance(path, String):
537
+ path = path.value
538
+
539
+ # Write to temporary file first
540
+ temp_path = path + '.tmp'
541
+ result = File.write_text(temp_path, data)
542
+ if result == Boolean(True):
543
+ # Atomic rename
544
+ os.replace(temp_path, path)
545
+ return Boolean(True)
546
+ return result
547
+ except Exception as e:
548
+ return EvaluationError(f"Atomic write error: {str(e)}")
549
+
550
+ # File locking for concurrent access
551
+ _file_locks = {}
552
+ _lock = Lock()
553
+
554
+ @staticmethod
555
+ def lock_file(path):
556
+ """Lock file for exclusive access"""
557
+ try:
558
+ if isinstance(path, String):
559
+ path = path.value
560
+
561
+ with File._lock:
562
+ if path not in File._file_locks:
563
+ File._file_locks[path] = Lock()
564
+
565
+ File._file_locks[path].acquire()
566
+ return Boolean(True)
567
+ except Exception as e:
568
+ return EvaluationError(f"File lock error: {str(e)}")
569
+
570
+ @staticmethod
571
+ def unlock_file(path):
572
+ """Unlock file"""
573
+ try:
574
+ if isinstance(path, String):
575
+ path = path.value
576
+
577
+ with File._lock:
578
+ if path in File._file_locks:
579
+ File._file_locks[path].release()
580
+ return Boolean(True)
581
+ return Boolean(False)
582
+ except Exception as e:
583
+ return EvaluationError(f"File unlock error: {str(e)}")
584
+
585
+ # Helper methods for data conversion
586
+ @staticmethod
587
+ def _python_to_zexus(value):
588
+ if isinstance(value, dict):
589
+ pairs = {}
590
+ for k, v in value.items():
591
+ pairs[k] = File._python_to_zexus(v)
592
+ return Map(pairs)
593
+ elif isinstance(value, list):
594
+ return List([File._python_to_zexus(item) for item in value])
595
+ elif isinstance(value, str):
596
+ return String(value)
597
+ elif isinstance(value, int):
598
+ return Integer(value)
599
+ elif isinstance(value, float):
600
+ return Float(value)
601
+ elif isinstance(value, bool):
602
+ return Boolean(value)
603
+ else:
604
+ return String(str(value))
605
+
606
+ @staticmethod
607
+ def _zexus_to_python(value):
608
+ if isinstance(value, Map):
609
+ # Handle both String keys and regular string keys
610
+ result = {}
611
+ for k, v in value.pairs.items():
612
+ key = k.value if isinstance(k, String) else str(k)
613
+ result[key] = File._zexus_to_python(v)
614
+ return result
615
+ elif isinstance(value, List):
616
+ return [File._zexus_to_python(item) for item in value.elements]
617
+ elif isinstance(value, String):
618
+ return value.value
619
+ elif isinstance(value, Integer):
620
+ return value.value
621
+ elif isinstance(value, Float):
622
+ return value.value
623
+ elif isinstance(value, Boolean):
624
+ return value.value
625
+ elif value == Null() or value is None:
626
+ return None
627
+ elif isinstance(value, dict):
628
+ # Handle plain Python dicts (fallback)
629
+ return {str(k): File._zexus_to_python(v) for k, v in value.items()}
630
+ elif isinstance(value, list):
631
+ # Handle plain Python lists (fallback)
632
+ return [File._zexus_to_python(item) for item in value]
633
+ else:
634
+ return str(value)
635
+
636
+ # Debug utility for enhanced error tracking
637
+ class Debug(Object):
638
+ def type(self):
639
+ return "DEBUG_UTILITY"
640
+
641
+ def inspect(self):
642
+ return "<Debug utilities>"
643
+
644
+ @staticmethod
645
+ def log(message, value=None):
646
+ """Log debug information with optional value"""
647
+ if isinstance(message, String):
648
+ message = message.value
649
+
650
+ debug_msg = f"🔍 DEBUG: {message}"
651
+ if value is not None:
652
+ debug_msg += f" → {value.inspect() if hasattr(value, 'inspect') else value}"
653
+
654
+ print(debug_msg)
655
+ return value if value is not None else Boolean(True)
656
+
657
+ @staticmethod
658
+ def trace(message):
659
+ """Add stack trace to debug output"""
660
+ import traceback
661
+ if isinstance(message, String):
662
+ message = message.value
663
+
664
+ print(f"🔍 TRACE: {message}")
665
+ print("Stack trace:")
666
+ for line in traceback.format_stack()[:-1]:
667
+ print(f" {line.strip()}")
668
+ return Boolean(True)
669
+
670
+ # Global dependency collector stack for WATCH feature
671
+ _dependency_collector_stack = []
672
+
673
+ def start_collecting_dependencies():
674
+ _dependency_collector_stack.append(set())
675
+
676
+ def stop_collecting_dependencies():
677
+ if _dependency_collector_stack:
678
+ return _dependency_collector_stack.pop()
679
+ return set()
680
+
681
+ def record_dependency(env, name):
682
+ if _dependency_collector_stack:
683
+ _dependency_collector_stack[-1].add((env, name))
684
+
685
+ class Environment:
686
+ def __init__(self, outer=None, persistence_scope=None):
687
+ self.store = {}
688
+ self.const_vars = set() # Track const variables
689
+ self.outer = outer
690
+ self.exports = {}
691
+ # Debug tracking
692
+ self.debug_mode = False
693
+ # Reactive watchers: name -> list of (callback_fn, context_env)
694
+ self.watchers = {}
695
+ # Persistence support
696
+ self.persistence_scope = persistence_scope
697
+ self._persistent_storage = None
698
+ self._memory_tracker = None
699
+ self._init_persistence()
700
+
701
+ def get(self, name):
702
+ val = self.store.get(name)
703
+ if val is not None:
704
+ record_dependency(self, name)
705
+ return val
706
+
707
+ if self.outer is not None:
708
+ return self.outer.get(name)
709
+ return None
710
+
711
+ def set(self, name, val):
712
+ # Check if trying to reassign a const variable
713
+ if name in self.const_vars:
714
+ from .object import EvaluationError
715
+ raise ValueError(f"Cannot reassign const variable '{name}'")
716
+ self.store[name] = val
717
+ self.notify_watchers(name, val)
718
+ return val
719
+
720
+ def assign(self, name, val):
721
+ """Assign to an existing variable in the scope chain, or create in current if not found."""
722
+ # 1. Check current scope
723
+ if name in self.store:
724
+ if hasattr(self, 'const_vars') and name in self.const_vars:
725
+ raise ValueError(f"Cannot reassign const variable '{name}'")
726
+ self.store[name] = val
727
+ if hasattr(self, 'notify_watchers'):
728
+ self.notify_watchers(name, val)
729
+ return val
730
+
731
+ # 2. Check outer scope
732
+ if self.outer is not None:
733
+ # Let's try to find where it is defined first.
734
+ scope = self.outer
735
+ while scope is not None:
736
+ if name in scope.store:
737
+ # Check for const (defensive - some envs might not have const_vars)
738
+ if hasattr(scope, 'const_vars') and name in scope.const_vars:
739
+ raise ValueError(f"Cannot reassign const variable '{name}'")
740
+ scope.store[name] = val
741
+ if hasattr(scope, 'notify_watchers'):
742
+ scope.notify_watchers(name, val)
743
+ return val
744
+ scope = scope.outer
745
+
746
+ # Not found anywhere -> Create in CURRENT scope (local)
747
+ self.store[name] = val
748
+ if hasattr(self, 'notify_watchers'):
749
+ self.notify_watchers(name, val)
750
+ return val
751
+
752
+ # 3. No outer scope (Global) -> Create here
753
+ self.store[name] = val
754
+ if hasattr(self, 'notify_watchers'):
755
+ self.notify_watchers(name, val)
756
+ return val
757
+
758
+ def _has_variable(self, name):
759
+ """Check if a variable name exists in this scope or any outer scope."""
760
+ if name in self.store:
761
+ return True
762
+ if self.outer and hasattr(self.outer, '_has_variable'):
763
+ return self.outer._has_variable(name)
764
+ return False
765
+
766
+ def set_const(self, name, val):
767
+ """Set a constant (immutable) variable"""
768
+ # Only check CURRENT scope for const shadowing - allow shadowing in nested scopes
769
+ if name in self.store and name in self.const_vars:
770
+ from .object import EvaluationError
771
+ raise ValueError(f"Cannot reassign const variable '{name}'")
772
+ self.store[name] = val
773
+ self.const_vars.add(name)
774
+ return val
775
+
776
+ def export(self, name, value):
777
+ self.exports[name] = value
778
+ return value
779
+
780
+ def get_exports(self):
781
+ return self.exports
782
+
783
+ def enable_debug(self):
784
+ self.debug_mode = True
785
+
786
+ def disable_debug(self):
787
+ self.debug_mode = False
788
+
789
+ def add_watcher(self, name, callback):
790
+ if name not in self.watchers:
791
+ self.watchers[name] = []
792
+ self.watchers[name].append(callback)
793
+
794
+ def notify_watchers(self, name, new_val):
795
+ if name in self.watchers:
796
+ # Copy list to avoid modification during iteration issues
797
+ callbacks = self.watchers[name][:]
798
+ for cb in callbacks:
799
+ try:
800
+ cb(new_val)
801
+ except Exception as e:
802
+ print(f"Error in watcher for {name}: {e}")
803
+
804
+ # === PERSISTENCE METHODS ===
805
+
806
+ def _init_persistence(self):
807
+ """Initialize persistence system if scope is provided"""
808
+ if self.persistence_scope:
809
+ try:
810
+ # Lazy import to avoid circular dependencies
811
+ import sys
812
+ if 'zexus.persistence' in sys.modules:
813
+ from .persistence import PersistentStorage, MemoryTracker
814
+ self._persistent_storage = PersistentStorage(self.persistence_scope)
815
+ self._memory_tracker = MemoryTracker()
816
+ self._memory_tracker.start_tracking()
817
+ except (ImportError, Exception):
818
+ # Persistence module not available or error - continue without it
819
+ pass
820
+
821
+ def set_persistent(self, name, val):
822
+ """Set a persistent variable that survives program restarts"""
823
+ if self._persistent_storage:
824
+ self._persistent_storage.set(name, val)
825
+ # Also store in regular memory for access
826
+ self.store[name] = val
827
+ self.notify_watchers(name, val)
828
+ return val
829
+
830
+ def get_persistent(self, name, default=None):
831
+ """Get a persistent variable"""
832
+ if self._persistent_storage:
833
+ val = self._persistent_storage.get(name)
834
+ if val is not None:
835
+ return val
836
+ # Fallback to regular store
837
+ return self.store.get(name, default)
838
+
839
+ def delete_persistent(self, name):
840
+ """Delete a persistent variable"""
841
+ if self._persistent_storage:
842
+ self._persistent_storage.delete(name)
843
+ if name in self.store:
844
+ del self.store[name]
845
+
846
+ def get_memory_stats(self):
847
+ """Get current memory tracking statistics"""
848
+ if self._memory_tracker:
849
+ return self._memory_tracker.get_stats()
850
+ return {"tracked_objects": 0, "message": "Memory tracking not enabled"}
851
+
852
+ def enable_memory_tracking(self):
853
+ """Enable memory leak detection"""
854
+ if not self._memory_tracker:
855
+ try:
856
+ # Lazy import
857
+ import sys
858
+ if 'zexus.persistence' in sys.modules:
859
+ from .persistence import MemoryTracker
860
+ self._memory_tracker = MemoryTracker()
861
+ self._memory_tracker.start_tracking()
862
+ except (ImportError, Exception):
863
+ pass
864
+
865
+ def cleanup_persistence(self):
866
+ """Clean up persistence resources"""
867
+ if self._memory_tracker:
868
+ self._memory_tracker.stop_tracking()
869
+ if self._persistent_storage:
870
+ self._persistent_storage.close()
871
+
872
+ # Global constants
873
+ NULL = Null()
874
+ TRUE = Boolean(True)
875
+ FALSE = Boolean(False)
876
+
877
+ # File object for RAII pattern (using statement)
878
+ class FileHandle(Object):
879
+ """File object that supports cleanup via close() method"""
880
+ def __init__(self, path, mode='r'):
881
+ self.path = path
882
+ self.mode = mode
883
+ self.handle = None
884
+ self.closed = False
885
+
886
+ def open(self):
887
+ """Open the file"""
888
+ if not self.handle:
889
+ try:
890
+ self.handle = open(self.path, self.mode)
891
+ except Exception as e:
892
+ raise Exception(f"Failed to open file {self.path}: {e}")
893
+ return self
894
+
895
+ def close(self):
896
+ """Close the file (called by using statement cleanup)"""
897
+ if self.handle and not self.closed:
898
+ self.handle.close()
899
+ self.closed = True
900
+
901
+ def read(self):
902
+ """Read file contents"""
903
+ if not self.handle:
904
+ self.open()
905
+ return String(self.handle.read())
906
+
907
+ def write(self, content):
908
+ """Write content to file"""
909
+ if not self.handle:
910
+ self.open()
911
+ self.handle.write(content)
912
+ return NULL
913
+
914
+ def inspect(self):
915
+ status = "closed" if self.closed else "open"
916
+ return f"File({self.path}, {status})"
917
+
918
+ def type(self):
919
+ return "FILE"
920
+
921
+ # EvaluationError class for error handling
922
+ class EvaluationError(Object):
923
+ def __init__(self, message, line=None, column=None, stack_trace=None, filename=None, suggestion=None):
924
+ self.message = message
925
+ self.line = line
926
+ self.column = column
927
+ self.stack_trace = stack_trace or []
928
+ self.filename = filename
929
+ self.suggestion = suggestion
930
+
931
+ def inspect(self):
932
+ return f"❌ Error: {self.message}"
933
+
934
+ def type(self):
935
+ return "ERROR"
936
+
937
+ def __str__(self):
938
+ """Format as a nice error message"""
939
+ # Try to use error reporter if available
940
+ try:
941
+ from .error_reporter import get_error_reporter, ZexusError, ErrorCategory
942
+
943
+ error_reporter = get_error_reporter()
944
+
945
+ # Create a formatted error
946
+ # We use a temporary ZexusError for formatting
947
+ temp_error = ZexusError(
948
+ message=self.message,
949
+ category=ErrorCategory.USER_CODE,
950
+ filename=self.filename or "<runtime>",
951
+ line=self.line,
952
+ column=self.column,
953
+ source_line=error_reporter.get_source_line(self.filename, self.line) if self.line else None,
954
+ suggestion=self.suggestion
955
+ )
956
+
957
+ # Add stack trace if available
958
+ if self.stack_trace:
959
+ trace = "\n".join(self.stack_trace[-5:])
960
+ temp_error.message += f"\n\nStack trace:\n{trace}"
961
+
962
+ return temp_error.format_error()
963
+ except Exception:
964
+ # Fallback to simple format if error reporter not available
965
+ location = f"Line {self.line}:{self.column}" if self.line and self.column else "Unknown location"
966
+ trace = "\n".join(self.stack_trace[-3:]) if self.stack_trace else ""
967
+ trace_section = f"\n Stack:\n{trace}" if trace else ""
968
+ suggestion_section = f"\n 💡 Suggestion: {self.suggestion}" if self.suggestion else ""
969
+ return f"❌ Runtime Error at {location}\n {self.message}{suggestion_section}{trace_section}"
970
+
971
+ def __len__(self):
972
+ """Support len() on errors to prevent secondary failures"""
973
+ return len(self.message)