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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/__init__.py +7 -0
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +17 -2
- package/src/zexus/blockchain/__init__.py +411 -0
- package/src/zexus/blockchain/accelerator.py +1160 -0
- package/src/zexus/blockchain/chain.py +660 -0
- package/src/zexus/blockchain/consensus.py +821 -0
- package/src/zexus/blockchain/contract_vm.py +1019 -0
- package/src/zexus/blockchain/crypto.py +79 -14
- package/src/zexus/blockchain/events.py +526 -0
- package/src/zexus/blockchain/loadtest.py +721 -0
- package/src/zexus/blockchain/monitoring.py +350 -0
- package/src/zexus/blockchain/mpt.py +716 -0
- package/src/zexus/blockchain/multichain.py +951 -0
- package/src/zexus/blockchain/multiprocess_executor.py +338 -0
- package/src/zexus/blockchain/network.py +886 -0
- package/src/zexus/blockchain/node.py +666 -0
- package/src/zexus/blockchain/rpc.py +1203 -0
- package/src/zexus/blockchain/rust_bridge.py +421 -0
- package/src/zexus/blockchain/storage.py +423 -0
- package/src/zexus/blockchain/tokens.py +750 -0
- package/src/zexus/blockchain/upgradeable.py +1004 -0
- package/src/zexus/blockchain/verification.py +1602 -0
- package/src/zexus/blockchain/wallet.py +621 -0
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +300 -20
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/lexer.py +10 -5
- package/src/zexus/concurrency_system.py +79 -0
- package/src/zexus/config.py +54 -0
- package/src/zexus/crypto_bridge.py +244 -8
- package/src/zexus/dap/__init__.py +10 -0
- package/src/zexus/dap/__main__.py +4 -0
- package/src/zexus/dap/dap_server.py +391 -0
- package/src/zexus/dap/debug_engine.py +298 -0
- package/src/zexus/environment.py +10 -1
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +441 -37
- package/src/zexus/evaluator/core.py +560 -49
- package/src/zexus/evaluator/expressions.py +122 -49
- package/src/zexus/evaluator/functions.py +417 -16
- package/src/zexus/evaluator/statements.py +521 -118
- package/src/zexus/evaluator/unified_execution.py +573 -72
- package/src/zexus/evaluator/utils.py +14 -2
- package/src/zexus/event_loop.py +186 -0
- package/src/zexus/lexer.py +742 -486
- package/src/zexus/lsp/__init__.py +1 -1
- package/src/zexus/lsp/definition_provider.py +163 -9
- package/src/zexus/lsp/server.py +22 -8
- package/src/zexus/lsp/symbol_provider.py +182 -9
- package/src/zexus/module_cache.py +237 -9
- package/src/zexus/object.py +64 -6
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/parser.py +786 -285
- package/src/zexus/parser/strategy_context.py +407 -66
- package/src/zexus/parser/strategy_structural.py +117 -19
- package/src/zexus/persistence.py +15 -1
- package/src/zexus/renderer/__init__.py +15 -0
- package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
- package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
- package/src/zexus/renderer/tk_backend.py +208 -0
- package/src/zexus/renderer/web_backend.py +260 -0
- package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
- package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
- package/src/zexus/runtime/file_flags.py +137 -0
- package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
- package/src/zexus/security.py +424 -34
- package/src/zexus/stdlib/fs.py +23 -18
- package/src/zexus/stdlib/http.py +289 -186
- package/src/zexus/stdlib/sockets.py +207 -163
- package/src/zexus/stdlib/websockets.py +282 -0
- package/src/zexus/stdlib_integration.py +369 -2
- package/src/zexus/strategy_recovery.py +6 -3
- package/src/zexus/type_checker.py +423 -0
- package/src/zexus/virtual_filesystem.py +189 -2
- package/src/zexus/vm/__init__.py +113 -3
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +14 -1
- package/src/zexus/vm/binary_bytecode.py +659 -0
- package/src/zexus/vm/bytecode.py +28 -1
- package/src/zexus/vm/bytecode_converter.py +26 -12
- package/src/zexus/vm/cabi.c +1985 -0
- package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/cabi.h +127 -0
- package/src/zexus/vm/cache.py +557 -17
- package/src/zexus/vm/compiler.py +703 -5
- package/src/zexus/vm/fastops.c +15743 -0
- package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/fastops.pyx +288 -0
- package/src/zexus/vm/gas_metering.py +50 -9
- package/src/zexus/vm/jit.py +83 -2
- package/src/zexus/vm/native_jit_backend.py +1816 -0
- package/src/zexus/vm/native_runtime.cpp +1388 -0
- package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
- package/src/zexus/vm/optimizer.py +161 -11
- package/src/zexus/vm/parallel_vm.py +118 -42
- package/src/zexus/vm/peephole_optimizer.py +82 -4
- package/src/zexus/vm/profiler.py +38 -18
- package/src/zexus/vm/register_allocator.py +16 -5
- package/src/zexus/vm/register_vm.py +8 -5
- package/src/zexus/vm/vm.py +3411 -573
- package/src/zexus/vm/wasm_compiler.py +658 -0
- package/src/zexus/zexus_ast.py +63 -11
- package/src/zexus/zexus_token.py +13 -5
- package/src/zexus/zpm/installer.py +55 -15
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus/zpm/registry.py +257 -28
- package/src/zexus.egg-info/PKG-INFO +7 -4
- package/src/zexus.egg-info/SOURCES.txt +116 -9
- package/src/zexus.egg-info/entry_points.txt +1 -0
- package/src/zexus.egg-info/requires.txt +4 -0
|
@@ -6,22 +6,72 @@ re-parsing and re-evaluating modules that have already been loaded.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
+
import hashlib
|
|
9
10
|
import threading
|
|
10
|
-
from typing import Dict, Optional
|
|
11
|
+
from typing import Dict, Optional, Any, Tuple, Set
|
|
11
12
|
from .object import Environment
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
# Module cache stores: (environment, bytecode, ast)
|
|
15
|
+
_MODULE_CACHE: Dict[str, Tuple[Environment, Any, Any]] = {}
|
|
14
16
|
_MODULE_CACHE_LOCK = threading.Lock()
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
# Circular-import detection: set of normalized paths currently being loaded
|
|
19
|
+
_LOADING_SET: Set[str] = set()
|
|
20
|
+
_LOADING_SET_LOCK = threading.Lock()
|
|
21
|
+
|
|
22
|
+
# Contract AST cache by source hash
|
|
23
|
+
_CONTRACT_AST_CACHE: Dict[str, Any] = {}
|
|
24
|
+
_CONTRACT_AST_LOCK = threading.Lock()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CircularImportError(Exception):
|
|
28
|
+
"""Raised when a circular import dependency is detected."""
|
|
29
|
+
def __init__(self, path: str, chain: Optional[list] = None):
|
|
30
|
+
self.path = path
|
|
31
|
+
self.chain = chain or []
|
|
32
|
+
if chain:
|
|
33
|
+
cycle = " -> ".join(chain + [path])
|
|
34
|
+
msg = f"Circular import detected: {cycle}"
|
|
35
|
+
else:
|
|
36
|
+
msg = f"Circular import detected while loading: {path}"
|
|
37
|
+
super().__init__(msg)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def begin_loading(module_path: str) -> None:
|
|
41
|
+
"""Mark *module_path* as currently being loaded.
|
|
42
|
+
|
|
43
|
+
Raises ``CircularImportError`` if the module is already in the loading set
|
|
44
|
+
(i.e. a circular dependency has been encountered).
|
|
45
|
+
"""
|
|
46
|
+
norm = normalize_path(module_path)
|
|
47
|
+
with _LOADING_SET_LOCK:
|
|
48
|
+
if norm in _LOADING_SET:
|
|
49
|
+
raise CircularImportError(norm, list(_LOADING_SET))
|
|
50
|
+
_LOADING_SET.add(norm)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def end_loading(module_path: str) -> None:
|
|
54
|
+
"""Remove *module_path* from the loading set after it has finished loading."""
|
|
55
|
+
norm = normalize_path(module_path)
|
|
56
|
+
with _LOADING_SET_LOCK:
|
|
57
|
+
_LOADING_SET.discard(norm)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def is_loading(module_path: str) -> bool:
|
|
61
|
+
"""Check whether *module_path* is currently being loaded."""
|
|
62
|
+
norm = normalize_path(module_path)
|
|
63
|
+
with _LOADING_SET_LOCK:
|
|
64
|
+
return norm in _LOADING_SET
|
|
65
|
+
|
|
66
|
+
def get_cached_module(module_path: str) -> Optional[Tuple[Environment, Any, Any]]:
|
|
67
|
+
"""Get a cached module (environment, bytecode, ast) if available"""
|
|
18
68
|
with _MODULE_CACHE_LOCK:
|
|
19
69
|
return _MODULE_CACHE.get(module_path)
|
|
20
70
|
|
|
21
|
-
def cache_module(module_path: str, module_env: Environment) -> None:
|
|
22
|
-
"""Cache a loaded module environment"""
|
|
71
|
+
def cache_module(module_path: str, module_env: Environment, bytecode: Any = None, ast: Any = None) -> None:
|
|
72
|
+
"""Cache a loaded module environment with optional bytecode and AST"""
|
|
23
73
|
with _MODULE_CACHE_LOCK:
|
|
24
|
-
_MODULE_CACHE[module_path] = module_env
|
|
74
|
+
_MODULE_CACHE[module_path] = (module_env, bytecode, ast)
|
|
25
75
|
|
|
26
76
|
def clear_module_cache() -> None:
|
|
27
77
|
"""Clear the entire module cache"""
|
|
@@ -40,6 +90,16 @@ def list_cached_modules() -> list[str]:
|
|
|
40
90
|
with _MODULE_CACHE_LOCK:
|
|
41
91
|
return list(_MODULE_CACHE.keys())
|
|
42
92
|
|
|
93
|
+
def get_cached_contract_ast(source_hash: str) -> Optional[Any]:
|
|
94
|
+
"""Get a cached contract AST by source hash"""
|
|
95
|
+
with _CONTRACT_AST_LOCK:
|
|
96
|
+
return _CONTRACT_AST_CACHE.get(source_hash)
|
|
97
|
+
|
|
98
|
+
def cache_contract_ast(source_hash: str, ast: Any) -> None:
|
|
99
|
+
"""Cache a parsed contract AST"""
|
|
100
|
+
with _CONTRACT_AST_LOCK:
|
|
101
|
+
_CONTRACT_AST_CACHE[source_hash] = ast
|
|
102
|
+
|
|
43
103
|
def get_module_candidates(file_path: str, importer_file: str = None) -> list[str]:
|
|
44
104
|
"""Get candidate paths for a module, checking zpm_modules etc.
|
|
45
105
|
|
|
@@ -70,7 +130,11 @@ def get_module_candidates(file_path: str, importer_file: str = None) -> list[str
|
|
|
70
130
|
resolved_path = os.path.join(importer_dir, file_path)
|
|
71
131
|
candidates.append(resolved_path)
|
|
72
132
|
else:
|
|
73
|
-
#
|
|
133
|
+
# For bare imports (no ./ or ../ prefix), check relative to importer first
|
|
134
|
+
if importer_file:
|
|
135
|
+
importer_dir = os.path.dirname(importer_file)
|
|
136
|
+
candidates.append(os.path.join(importer_dir, file_path))
|
|
137
|
+
# Then relative to current working directory
|
|
74
138
|
candidates.append(os.path.join(os.getcwd(), file_path))
|
|
75
139
|
|
|
76
140
|
# Also check zpm_modules directory
|
|
@@ -88,4 +152,168 @@ def get_module_candidates(file_path: str, importer_file: str = None) -> list[str
|
|
|
88
152
|
|
|
89
153
|
def normalize_path(path: str) -> str:
|
|
90
154
|
"""Normalize a path for consistent cache keys"""
|
|
91
|
-
return os.path.abspath(os.path.expanduser(path))
|
|
155
|
+
return os.path.abspath(os.path.expanduser(path))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
# Module Pre-compilation
|
|
160
|
+
# ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
def _extract_import_paths(node) -> list:
|
|
163
|
+
"""Recursively walk an AST and return all import file paths."""
|
|
164
|
+
from . import zexus_ast
|
|
165
|
+
paths = []
|
|
166
|
+
if isinstance(node, zexus_ast.Program):
|
|
167
|
+
for stmt in getattr(node, 'statements', []):
|
|
168
|
+
paths.extend(_extract_import_paths(stmt))
|
|
169
|
+
elif isinstance(node, zexus_ast.UseStatement):
|
|
170
|
+
fp = node.file_path
|
|
171
|
+
if hasattr(fp, 'value'):
|
|
172
|
+
fp = fp.value
|
|
173
|
+
if isinstance(fp, str):
|
|
174
|
+
paths.append(fp)
|
|
175
|
+
elif isinstance(node, zexus_ast.FromStatement):
|
|
176
|
+
fp = node.file_path
|
|
177
|
+
if hasattr(fp, 'value'):
|
|
178
|
+
fp = fp.value
|
|
179
|
+
if isinstance(fp, str):
|
|
180
|
+
paths.append(fp)
|
|
181
|
+
elif isinstance(node, (zexus_ast.BlockStatement,)):
|
|
182
|
+
for stmt in getattr(node, 'statements', []):
|
|
183
|
+
paths.extend(_extract_import_paths(stmt))
|
|
184
|
+
elif isinstance(node, zexus_ast.IfStatement):
|
|
185
|
+
paths.extend(_extract_import_paths(node.consequence))
|
|
186
|
+
if node.alternative:
|
|
187
|
+
paths.extend(_extract_import_paths(node.alternative))
|
|
188
|
+
for cond, body in getattr(node, 'elif_parts', []):
|
|
189
|
+
paths.extend(_extract_import_paths(body))
|
|
190
|
+
elif isinstance(node, (zexus_ast.WhileStatement, zexus_ast.ForEachStatement)):
|
|
191
|
+
paths.extend(_extract_import_paths(getattr(node, 'body', None) or getattr(node, 'block', None)))
|
|
192
|
+
elif isinstance(node, zexus_ast.FunctionStatement):
|
|
193
|
+
paths.extend(_extract_import_paths(getattr(node, 'body', None)))
|
|
194
|
+
elif isinstance(node, zexus_ast.ActionStatement):
|
|
195
|
+
paths.extend(_extract_import_paths(getattr(node, 'body', None)))
|
|
196
|
+
elif isinstance(node, zexus_ast.TryCatchStatement):
|
|
197
|
+
paths.extend(_extract_import_paths(getattr(node, 'try_body', None)))
|
|
198
|
+
paths.extend(_extract_import_paths(getattr(node, 'catch_body', None)))
|
|
199
|
+
if getattr(node, 'finally_body', None):
|
|
200
|
+
paths.extend(_extract_import_paths(node.finally_body))
|
|
201
|
+
return [p for p in paths if p]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def precompile_modules(program, main_file: str, compile_bytecode: bool = True) -> dict:
|
|
205
|
+
"""Pre-compile all imported modules before execution.
|
|
206
|
+
|
|
207
|
+
Walks the AST of *program*, resolves every ``use`` / ``from`` import,
|
|
208
|
+
lexes + parses each module file, optionally compiles to bytecode, and
|
|
209
|
+
stores everything in the global ``_MODULE_CACHE``. Processes modules
|
|
210
|
+
recursively so transitive dependencies are also cached.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
program: The parsed AST (Program node) of the main file.
|
|
214
|
+
main_file: Absolute path of the main source file.
|
|
215
|
+
compile_bytecode: If True, also compile each module to VM bytecode.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
dict mapping normalised module path → (ast, bytecode_or_None).
|
|
219
|
+
"""
|
|
220
|
+
from .lexer import Lexer
|
|
221
|
+
from .parser import Parser
|
|
222
|
+
from .stdlib_integration import is_stdlib_module
|
|
223
|
+
from .builtin_modules import is_builtin_module
|
|
224
|
+
|
|
225
|
+
compiler_mod = None
|
|
226
|
+
if compile_bytecode:
|
|
227
|
+
try:
|
|
228
|
+
from .vm.compiler import compile_ast_to_bytecode
|
|
229
|
+
compiler_mod = compile_ast_to_bytecode
|
|
230
|
+
except Exception:
|
|
231
|
+
compiler_mod = None
|
|
232
|
+
|
|
233
|
+
results: Dict[str, Any] = {}
|
|
234
|
+
visited: Set[str] = set()
|
|
235
|
+
|
|
236
|
+
def _resolve_and_cache(import_path: str, importer_file: str):
|
|
237
|
+
"""Resolve a single import and cache it, then recurse."""
|
|
238
|
+
# Skip stdlib / builtin modules — they aren't file-based
|
|
239
|
+
if is_stdlib_module(import_path) or is_builtin_module(import_path):
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
candidates = get_module_candidates(import_path, importer_file)
|
|
243
|
+
resolved = None
|
|
244
|
+
for cand in candidates:
|
|
245
|
+
norm = normalize_path(cand)
|
|
246
|
+
if norm in visited:
|
|
247
|
+
import warnings
|
|
248
|
+
warnings.warn(
|
|
249
|
+
f"Circular import detected during pre-compilation: {import_path} "
|
|
250
|
+
f"(resolved to {norm})",
|
|
251
|
+
stacklevel=2,
|
|
252
|
+
)
|
|
253
|
+
return # Already processed (or in progress — avoids cycles)
|
|
254
|
+
if os.path.isfile(cand):
|
|
255
|
+
resolved = cand
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
if resolved is None:
|
|
259
|
+
return # Unresolvable — runtime will report the error
|
|
260
|
+
|
|
261
|
+
norm = normalize_path(resolved)
|
|
262
|
+
if norm in visited:
|
|
263
|
+
return
|
|
264
|
+
visited.add(norm)
|
|
265
|
+
|
|
266
|
+
# Already cached from a previous run?
|
|
267
|
+
cached = get_cached_module(norm)
|
|
268
|
+
if cached is not None:
|
|
269
|
+
results[norm] = (cached[2], cached[1]) # (ast, bytecode)
|
|
270
|
+
# Still recurse into dependencies of the cached module
|
|
271
|
+
if cached[2] is not None:
|
|
272
|
+
sub_paths = _extract_import_paths(cached[2])
|
|
273
|
+
for sp in sub_paths:
|
|
274
|
+
_resolve_and_cache(sp, resolved)
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
# Read, lex, parse
|
|
278
|
+
try:
|
|
279
|
+
with open(resolved, 'r', encoding='utf-8') as f:
|
|
280
|
+
source = f.read()
|
|
281
|
+
except (OSError, IOError):
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
lexer = Lexer(source, filename=resolved)
|
|
286
|
+
parser = Parser(lexer, 'universal', enable_advanced_strategies=True)
|
|
287
|
+
mod_ast = parser.parse_program()
|
|
288
|
+
except Exception:
|
|
289
|
+
return # Parse failure — runtime will handle
|
|
290
|
+
|
|
291
|
+
# Optionally compile to bytecode
|
|
292
|
+
mod_bytecode = None
|
|
293
|
+
if compiler_mod is not None:
|
|
294
|
+
try:
|
|
295
|
+
mod_bytecode = compiler_mod(mod_ast, optimize=True)
|
|
296
|
+
except Exception:
|
|
297
|
+
pass # Compilation failure is non-fatal; interpreter fallback
|
|
298
|
+
|
|
299
|
+
# Cache a placeholder env + ast + bytecode so runtime finds them.
|
|
300
|
+
# Mark the env as pre-compiled but not yet evaluated — the runtime
|
|
301
|
+
# will execute the bytecode/AST to populate it on first use.
|
|
302
|
+
mod_env = Environment()
|
|
303
|
+
mod_env.set("__file__", resolved)
|
|
304
|
+
mod_env.set("__MODULE__", os.path.splitext(os.path.basename(resolved))[0])
|
|
305
|
+
mod_env._precompiled = True # Marker for eval_use_statement
|
|
306
|
+
cache_module(norm, mod_env, mod_bytecode, mod_ast)
|
|
307
|
+
results[norm] = (mod_ast, mod_bytecode)
|
|
308
|
+
|
|
309
|
+
# Recurse into sub-imports
|
|
310
|
+
sub_paths = _extract_import_paths(mod_ast)
|
|
311
|
+
for sp in sub_paths:
|
|
312
|
+
_resolve_and_cache(sp, resolved)
|
|
313
|
+
|
|
314
|
+
# Kick off from the main program's imports
|
|
315
|
+
import_paths = _extract_import_paths(program)
|
|
316
|
+
for ip in import_paths:
|
|
317
|
+
_resolve_and_cache(ip, main_file)
|
|
318
|
+
|
|
319
|
+
return results
|
package/src/zexus/object.py
CHANGED
|
@@ -98,6 +98,20 @@ class List(Object):
|
|
|
98
98
|
except Exception:
|
|
99
99
|
return NULL
|
|
100
100
|
|
|
101
|
+
def set(self, index, value):
|
|
102
|
+
"""Set element at index, mirroring array-style assignment."""
|
|
103
|
+
try:
|
|
104
|
+
idx = index.value if hasattr(index, 'value') else index
|
|
105
|
+
idx = int(idx)
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
raise EvaluationError(f"Invalid index for assignment: {index}") from exc
|
|
108
|
+
|
|
109
|
+
if idx < 0 or idx >= len(self.elements):
|
|
110
|
+
raise EvaluationError(f"Index out of range for List assignment: {idx}")
|
|
111
|
+
|
|
112
|
+
self.elements[idx] = value
|
|
113
|
+
return value
|
|
114
|
+
|
|
101
115
|
def append(self, item):
|
|
102
116
|
"""Append item to list in-place (mutating operation)"""
|
|
103
117
|
self.elements.append(item)
|
|
@@ -122,16 +136,35 @@ class Map(Object):
|
|
|
122
136
|
pairs.append(f"{key_str}: {value_str}")
|
|
123
137
|
return "{" + ", ".join(pairs) + "}"
|
|
124
138
|
|
|
139
|
+
def _normalize_key(self, key):
|
|
140
|
+
if hasattr(key, 'inspect'):
|
|
141
|
+
return key.inspect()
|
|
142
|
+
return str(key)
|
|
143
|
+
|
|
125
144
|
def get(self, key):
|
|
126
|
-
"""Get value by key (compatible with string keys)"""
|
|
127
|
-
|
|
145
|
+
"""Get value by key (compatible with both string and String keys)"""
|
|
146
|
+
# Try direct lookup first (works for String-keyed and plain-keyed maps)
|
|
147
|
+
val = self.pairs.get(key)
|
|
148
|
+
if val is not None:
|
|
149
|
+
return val
|
|
150
|
+
# Try with String object key (for maps created by _python_to_zexus)
|
|
151
|
+
if isinstance(key, str):
|
|
152
|
+
str_key = String(key)
|
|
153
|
+
val = self.pairs.get(str_key)
|
|
154
|
+
if val is not None:
|
|
155
|
+
return val
|
|
156
|
+
# Try with normalized plain string key (for maps with plain string keys)
|
|
157
|
+
norm_key = self._normalize_key(key)
|
|
158
|
+
return self.pairs.get(norm_key)
|
|
128
159
|
|
|
129
160
|
def set(self, key, value):
|
|
130
161
|
"""Set value for key, blocking modification if key is sealed."""
|
|
131
|
-
|
|
162
|
+
norm_key = self._normalize_key(key)
|
|
163
|
+
existing = self.pairs.get(norm_key)
|
|
132
164
|
if existing is not None and existing.__class__.__name__ == 'SealedObject':
|
|
133
165
|
raise EvaluationError(f"Cannot modify sealed map key: {key}")
|
|
134
|
-
self.pairs[
|
|
166
|
+
self.pairs[norm_key] = value
|
|
167
|
+
return value
|
|
135
168
|
|
|
136
169
|
def keys(self):
|
|
137
170
|
"""Return array of map keys"""
|
|
@@ -1113,14 +1146,39 @@ class EvaluationError(Object):
|
|
|
1113
1146
|
|
|
1114
1147
|
# Add stack trace if available
|
|
1115
1148
|
if self.stack_trace:
|
|
1116
|
-
|
|
1149
|
+
formatted_trace = []
|
|
1150
|
+
for frame in self.stack_trace[-5:]:
|
|
1151
|
+
if isinstance(frame, str):
|
|
1152
|
+
formatted_trace.append(frame)
|
|
1153
|
+
elif isinstance(frame, tuple) and len(frame) == 2:
|
|
1154
|
+
node_type, line = frame
|
|
1155
|
+
item = f" at {node_type.__name__}"
|
|
1156
|
+
if line:
|
|
1157
|
+
item += f" (line {line})"
|
|
1158
|
+
formatted_trace.append(item)
|
|
1159
|
+
|
|
1160
|
+
trace = "\n".join(formatted_trace)
|
|
1117
1161
|
temp_error.message += f"\n\nStack trace:\n{trace}"
|
|
1118
1162
|
|
|
1119
1163
|
return temp_error.format_error()
|
|
1120
1164
|
except Exception:
|
|
1121
1165
|
# Fallback to simple format if error reporter not available
|
|
1122
1166
|
location = f"Line {self.line}:{self.column}" if self.line and self.column else "Unknown location"
|
|
1123
|
-
|
|
1167
|
+
|
|
1168
|
+
# Format simple trace
|
|
1169
|
+
formatted_trace = []
|
|
1170
|
+
if self.stack_trace:
|
|
1171
|
+
for frame in self.stack_trace[-3:]:
|
|
1172
|
+
if isinstance(frame, str):
|
|
1173
|
+
formatted_trace.append(frame)
|
|
1174
|
+
elif isinstance(frame, tuple) and len(frame) == 2:
|
|
1175
|
+
node_type, line = frame
|
|
1176
|
+
item = f" at {node_type.__name__}"
|
|
1177
|
+
if line:
|
|
1178
|
+
item += f" (line {line})"
|
|
1179
|
+
formatted_trace.append(item)
|
|
1180
|
+
|
|
1181
|
+
trace = "\n".join(formatted_trace)
|
|
1124
1182
|
trace_section = f"\n Stack:\n{trace}" if trace else ""
|
|
1125
1183
|
suggestion_section = f"\n 💡 Suggestion: {self.suggestion}" if self.suggestion else ""
|
|
1126
1184
|
return f"❌ Runtime Error at {location}\n {self.message}{suggestion_section}{trace_section}"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|