zexus 1.8.0 ā 1.8.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 +34 -6
- package/bin/zexus +12 -2
- package/bin/zpics +12 -2
- package/bin/zpm +12 -2
- package/bin/zx +12 -2
- package/bin/zx-deploy +12 -2
- package/bin/zx-dev +12 -2
- package/bin/zx-run +12 -2
- package/package.json +2 -1
- package/rust_core/Cargo.lock +603 -0
- package/rust_core/Cargo.toml +26 -0
- package/rust_core/README.md +15 -0
- package/rust_core/pyproject.toml +25 -0
- package/rust_core/src/binary_bytecode.rs +543 -0
- package/rust_core/src/contract_vm.rs +643 -0
- package/rust_core/src/executor.rs +847 -0
- package/rust_core/src/hasher.rs +90 -0
- package/rust_core/src/lib.rs +71 -0
- package/rust_core/src/merkle.rs +128 -0
- package/rust_core/src/rust_vm.rs +2313 -0
- package/rust_core/src/signature.rs +79 -0
- package/rust_core/src/state_adapter.rs +281 -0
- package/rust_core/src/validator.rs +116 -0
- package/scripts/postinstall.js +204 -21
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/evaluator/bytecode_compiler.py +150 -52
- package/src/zexus/evaluator/core.py +151 -809
- package/src/zexus/evaluator/expressions.py +27 -22
- package/src/zexus/evaluator/functions.py +171 -126
- package/src/zexus/evaluator/statements.py +55 -112
- package/src/zexus/module_cache.py +20 -9
- package/src/zexus/object.py +330 -38
- package/src/zexus/parser/parser.py +103 -23
- package/src/zexus/parser/strategy_context.py +318 -6
- package/src/zexus/parser/strategy_structural.py +2 -2
- package/src/zexus/persistence.py +46 -17
- package/src/zexus/security.py +140 -234
- package/src/zexus/type_checker.py +44 -5
- package/src/zexus/vm/binary_bytecode.py +7 -3
- package/src/zexus/vm/bytecode.py +6 -0
- package/src/zexus/vm/cache.py +24 -46
- package/src/zexus/vm/compiler.py +549 -68
- package/src/zexus/vm/memory_pool.py +21 -9
- package/src/zexus/vm/vm.py +609 -95
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +56 -12
- package/src/zexus.egg-info/SOURCES.txt +14 -0
- package/src/zexus.egg-info/entry_points.txt +5 -1
- package/src/zexus.egg-info/requires.txt +26 -0
package/scripts/postinstall.js
CHANGED
|
@@ -3,42 +3,225 @@
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
6
7
|
|
|
7
8
|
console.log('\nš Installing Zexus Programming Language...\n');
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
function hasCommand(cmd) {
|
|
15
|
+
try {
|
|
16
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
function run(cmd, opts = {}) {
|
|
24
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: 'inherit', ...opts });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runQuiet(cmd) {
|
|
28
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isWin = os.platform() === 'win32';
|
|
32
|
+
const pip = isWin ? 'pip' : 'pip3';
|
|
33
|
+
const python = isWin ? 'python' : 'python3';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// 1. Python ā check or install
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
let pythonAvailable = false;
|
|
40
|
+
|
|
41
|
+
if (hasCommand(python)) {
|
|
42
|
+
try {
|
|
43
|
+
const ver = runQuiet(`${python} --version`).trim();
|
|
44
|
+
console.log(`ā Found ${ver}`);
|
|
45
|
+
// Verify >= 3.8
|
|
46
|
+
runQuiet(`${python} -c "import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"`);
|
|
47
|
+
console.log('ā Python version is 3.8 or higher');
|
|
48
|
+
pythonAvailable = true;
|
|
49
|
+
} catch {
|
|
50
|
+
console.error('ā Python was found but version is below 3.8.');
|
|
51
|
+
console.error(' Please upgrade to Python 3.8+: https://www.python.org/downloads/');
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
console.warn('ā ļø python3 not found on PATH.');
|
|
55
|
+
// Attempt auto-install on common platforms
|
|
56
|
+
if (os.platform() === 'linux') {
|
|
57
|
+
console.log(' Attempting to install python3 via apt...');
|
|
58
|
+
try {
|
|
59
|
+
run('sudo apt-get update -qq && sudo apt-get install -y -qq python3 python3-pip python3-venv');
|
|
60
|
+
pythonAvailable = true;
|
|
61
|
+
console.log('ā Python 3 installed via apt');
|
|
62
|
+
} catch {
|
|
63
|
+
console.warn(' Could not auto-install Python. Please install manually:');
|
|
64
|
+
console.warn(' https://www.python.org/downloads/');
|
|
65
|
+
}
|
|
66
|
+
} else if (os.platform() === 'darwin') {
|
|
67
|
+
if (hasCommand('brew')) {
|
|
68
|
+
console.log(' Attempting to install python3 via Homebrew...');
|
|
69
|
+
try {
|
|
70
|
+
run('brew install python@3');
|
|
71
|
+
pythonAvailable = true;
|
|
72
|
+
console.log('ā Python 3 installed via Homebrew');
|
|
73
|
+
} catch {
|
|
74
|
+
console.warn(' Could not auto-install Python. Please install manually:');
|
|
75
|
+
console.warn(' https://www.python.org/downloads/');
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
console.warn(' Please install Python 3.8+: https://www.python.org/downloads/');
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
console.warn(' Please install Python 3.8+: https://www.python.org/downloads/');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!pythonAvailable) {
|
|
86
|
+
console.error('\nā Python 3.8+ is required but could not be found or installed.');
|
|
87
|
+
console.error(' Install it from: https://www.python.org/downloads/');
|
|
88
|
+
console.error(' Then re-run: npm rebuild zexus');
|
|
25
89
|
process.exit(1);
|
|
26
90
|
}
|
|
27
91
|
|
|
28
|
-
//
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// 2. pip ā ensure available
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
if (!hasCommand(pip) && !hasCommand('pip')) {
|
|
97
|
+
console.log(' pip not found ā bootstrapping...');
|
|
98
|
+
try {
|
|
99
|
+
run(`${python} -m ensurepip --upgrade`);
|
|
100
|
+
} catch {
|
|
101
|
+
try {
|
|
102
|
+
run(`${python} -m pip install --upgrade pip`);
|
|
103
|
+
} catch {
|
|
104
|
+
console.warn('ā ļø Could not bootstrap pip. Python package install may fail.');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// 3. Install Zexus Python package
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
29
113
|
console.log('\nš¦ Installing Zexus Python package...');
|
|
114
|
+
|
|
115
|
+
// Check if zexus is already installed and up-to-date
|
|
116
|
+
let zexusInstalled = false;
|
|
30
117
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
118
|
+
runQuiet(`${python} -c "import zexus"`);
|
|
119
|
+
zexusInstalled = true;
|
|
120
|
+
console.log('ā Zexus Python package already installed');
|
|
121
|
+
} catch {
|
|
122
|
+
// Not installed yet
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!zexusInstalled) {
|
|
126
|
+
try {
|
|
127
|
+
run(`${python} -m pip install --user "zexus[full]"`);
|
|
128
|
+
console.log('ā Zexus Python package installed successfully');
|
|
129
|
+
} catch {
|
|
130
|
+
// Retry without --user (some environments like venvs don't need it)
|
|
131
|
+
try {
|
|
132
|
+
run(`${python} -m pip install "zexus[full]"`);
|
|
133
|
+
console.log('ā Zexus Python package installed successfully');
|
|
134
|
+
} catch {
|
|
135
|
+
console.error('ā Failed to install Zexus Python package.');
|
|
136
|
+
console.error(` Please run manually: ${python} -m pip install "zexus[full]"`);
|
|
137
|
+
// Don't exit ā commands that don't need Python may still work
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// 4. Rust toolchain ā check or install, then build rust_core
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
147
|
+
const cargoToml = path.join(pkgRoot, 'rust_core', 'Cargo.toml');
|
|
148
|
+
|
|
149
|
+
if (fs.existsSync(cargoToml)) {
|
|
150
|
+
let cargoReady = hasCommand('cargo');
|
|
151
|
+
|
|
152
|
+
if (!cargoReady) {
|
|
153
|
+
console.log('\nš¦ Rust toolchain (cargo) not found.');
|
|
154
|
+
console.log(' Attempting to install Rust via rustup...');
|
|
155
|
+
try {
|
|
156
|
+
if (isWin) {
|
|
157
|
+
// On Windows, download and run rustup-init
|
|
158
|
+
console.log(' Please install Rust manually: https://rustup.rs');
|
|
159
|
+
} else {
|
|
160
|
+
// Unix: install rustup non-interactively
|
|
161
|
+
run('curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y');
|
|
162
|
+
// Source the cargo env so it's available in this session
|
|
163
|
+
const cargoEnv = path.join(os.homedir(), '.cargo', 'env');
|
|
164
|
+
if (fs.existsSync(cargoEnv)) {
|
|
165
|
+
// Read the cargo bin path and add to PATH for child processes
|
|
166
|
+
const cargoBin = path.join(os.homedir(), '.cargo', 'bin');
|
|
167
|
+
process.env.PATH = `${cargoBin}${path.delimiter}${process.env.PATH}`;
|
|
168
|
+
}
|
|
169
|
+
cargoReady = hasCommand('cargo');
|
|
170
|
+
if (cargoReady) {
|
|
171
|
+
console.log('ā Rust toolchain installed via rustup');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.warn('ā ļø Could not auto-install Rust. Continuing with pure-Python VM.');
|
|
176
|
+
console.warn(' To install manually: https://rustup.rs');
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
console.log('\nā Rust toolchain detected');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (cargoReady) {
|
|
183
|
+
// Check if zexus_core is already importable
|
|
184
|
+
let rustVmInstalled = false;
|
|
185
|
+
try {
|
|
186
|
+
runQuiet(`${python} -c "import zexus_core"`);
|
|
187
|
+
rustVmInstalled = true;
|
|
188
|
+
console.log('ā Rust VM extension (zexus_core) already installed');
|
|
189
|
+
} catch {
|
|
190
|
+
// Need to build
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!rustVmInstalled) {
|
|
194
|
+
console.log('šØ Building Rust VM extension (zexus_core)...');
|
|
195
|
+
try {
|
|
196
|
+
// Ensure maturin is available
|
|
197
|
+
try {
|
|
198
|
+
runQuiet(`${python} -m maturin --version`);
|
|
199
|
+
} catch {
|
|
200
|
+
console.log(' Installing maturin build tool...');
|
|
201
|
+
run(`${python} -m pip install --user --upgrade maturin`);
|
|
202
|
+
}
|
|
203
|
+
run(`${python} -m maturin develop -m "${cargoToml}" --release`);
|
|
204
|
+
runQuiet(`${python} -c "import zexus_core"`);
|
|
205
|
+
console.log('ā Rust VM extension built and installed');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.warn('\nā ļø Rust VM build failed; continuing with pure-Python VM.');
|
|
208
|
+
console.warn(' To retry manually:');
|
|
209
|
+
console.warn(` ${python} -m pip install maturin && ${python} -m maturin develop -m rust_core/Cargo.toml --release`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
console.log('\nā¹ļø rust_core/Cargo.toml not bundled; skipping Rust VM build.');
|
|
37
215
|
}
|
|
38
216
|
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Done
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
|
|
39
221
|
console.log('\nā
Zexus installed successfully!\n');
|
|
40
222
|
console.log('Get started:');
|
|
41
223
|
console.log(' zexus --help # Show help');
|
|
42
224
|
console.log(' zx --version # Check version');
|
|
225
|
+
console.log(' zx run file.zx # Run a Zexus file');
|
|
43
226
|
console.log(' zexus examples/ # Explore examples\n');
|
|
44
227
|
console.log('Documentation: https://github.com/Zaidux/zexus-interpreter\n');
|
package/src/zexus/__init__.py
CHANGED
package/src/zexus/cli/main.py
CHANGED
|
@@ -156,7 +156,7 @@ def show_all_commands():
|
|
|
156
156
|
console.print("\n[bold green]š” Tip:[/bold green] Use 'zx <command> --help' for detailed command options\n")
|
|
157
157
|
|
|
158
158
|
@click.group(invoke_without_command=True)
|
|
159
|
-
@click.version_option(version="1.8.
|
|
159
|
+
@click.version_option(version="1.8.2", prog_name="Zexus")
|
|
160
160
|
@click.option('--syntax-style', type=click.Choice(['universal', 'tolerable', 'auto']),
|
|
161
161
|
default='auto', help='Syntax style to use (universal=strict, tolerable=flexible)')
|
|
162
162
|
@click.option('--advanced-parsing', is_flag=True, default=True,
|
package/src/zexus/cli/zpm.py
CHANGED
|
@@ -41,13 +41,26 @@ _SHARED_CACHE: Optional[BytecodeCache] = None
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def get_shared_cache() -> BytecodeCache:
|
|
44
|
-
"""Get the global shared bytecode cache
|
|
44
|
+
"""Get the global shared bytecode cache.
|
|
45
|
+
|
|
46
|
+
Note: The compiler stores AST objects (e.g., LoadExpression/FindExpression) inside
|
|
47
|
+
bytecode constants for VM keyword builtins.
|
|
48
|
+
|
|
49
|
+
Persisting these bytecodes to disk can cause order-dependent behavior during tests
|
|
50
|
+
because the same codebase is imported under multiple module namespaces (e.g.
|
|
51
|
+
`zexus.*` and `src.zexus.*`), producing distinct AST classes that are not
|
|
52
|
+
interchangeable at runtime.
|
|
53
|
+
"""
|
|
45
54
|
global _SHARED_CACHE
|
|
46
55
|
if _SHARED_CACHE is None:
|
|
56
|
+
# Default to in-memory only under pytest to avoid cross-test contamination.
|
|
57
|
+
is_pytest = bool(os.environ.get("PYTEST_CURRENT_TEST"))
|
|
58
|
+
persist_env = os.environ.get("ZEXUS_BYTECODE_CACHE_PERSISTENT")
|
|
59
|
+
persist_requested = bool(persist_env) and persist_env.lower() not in ("0", "false", "off")
|
|
47
60
|
_SHARED_CACHE = BytecodeCache(
|
|
48
61
|
max_size=2000,
|
|
49
62
|
max_memory_mb=100,
|
|
50
|
-
persistent=
|
|
63
|
+
persistent=(persist_requested and not is_pytest),
|
|
51
64
|
debug=False
|
|
52
65
|
)
|
|
53
66
|
return _SHARED_CACHE
|
|
@@ -161,11 +174,6 @@ class EvaluatorBytecodeCompiler:
|
|
|
161
174
|
cached = self.cache.get(node)
|
|
162
175
|
if cached:
|
|
163
176
|
return cached
|
|
164
|
-
|
|
165
|
-
# Check pattern cache (similar code structure)
|
|
166
|
-
pattern_cached = self.cache.get_by_pattern(node)
|
|
167
|
-
if pattern_cached:
|
|
168
|
-
return pattern_cached
|
|
169
177
|
|
|
170
178
|
# Cache miss - compile
|
|
171
179
|
self.builder = BytecodeBuilder()
|
|
@@ -184,10 +192,12 @@ class EvaluatorBytecodeCompiler:
|
|
|
184
192
|
|
|
185
193
|
bytecode.set_metadata('created_by', 'evaluator')
|
|
186
194
|
|
|
187
|
-
# Store in
|
|
195
|
+
# Store in cache
|
|
196
|
+
# NOTE: Pattern-cache reuse is intentionally disabled here because the current
|
|
197
|
+
# pattern hashing ignores literal/name fields, which can return bytecode with
|
|
198
|
+
# incorrect embedded constants/identifiers for āsimilarā nodes.
|
|
188
199
|
if use_cache and self.cache:
|
|
189
200
|
self.cache.put(node, bytecode)
|
|
190
|
-
self.cache.put_by_pattern(node, bytecode)
|
|
191
201
|
|
|
192
202
|
return bytecode
|
|
193
203
|
|
|
@@ -261,15 +271,51 @@ class EvaluatorBytecodeCompiler:
|
|
|
261
271
|
|
|
262
272
|
def _compile_LetStatement(self, node: zexus_ast.LetStatement):
|
|
263
273
|
"""Compile let statement"""
|
|
264
|
-
#
|
|
274
|
+
# Support destructuring patterns: let {a, b: x} = expr; let [x, y, ..rest] = expr
|
|
275
|
+
if isinstance(getattr(node, "name", None), zexus_ast.DestructurePattern):
|
|
276
|
+
self._compile_node(node.value)
|
|
277
|
+
pattern = node.name
|
|
278
|
+
if pattern.kind == 'map':
|
|
279
|
+
for source_key, target_name in pattern.bindings:
|
|
280
|
+
self.builder.emit("DUP")
|
|
281
|
+
self.builder.emit_constant(source_key)
|
|
282
|
+
self.builder.emit("INDEX")
|
|
283
|
+
self.builder.emit_store(target_name)
|
|
284
|
+
self.builder.emit("POP")
|
|
285
|
+
return
|
|
286
|
+
if pattern.kind == 'list':
|
|
287
|
+
for idx, target_name in pattern.bindings:
|
|
288
|
+
self.builder.emit("DUP")
|
|
289
|
+
self.builder.emit_constant(idx)
|
|
290
|
+
self.builder.emit("INDEX")
|
|
291
|
+
self.builder.emit_store(target_name)
|
|
292
|
+
if pattern.rest:
|
|
293
|
+
rest_start = len(pattern.bindings)
|
|
294
|
+
self.builder.emit("DUP")
|
|
295
|
+
self.builder.emit_constant(rest_start)
|
|
296
|
+
self.builder.emit_constant(None)
|
|
297
|
+
self.builder.emit("SLICE")
|
|
298
|
+
self.builder.emit_store(pattern.rest)
|
|
299
|
+
self.builder.emit("POP")
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
self.errors.append(f"Unsupported destructure pattern kind: {pattern.kind}")
|
|
303
|
+
self.builder.emit("POP")
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
# Regular identifier assignment
|
|
265
307
|
self._compile_node(node.value)
|
|
266
|
-
# Store it
|
|
267
308
|
name = str(node.name.value).strip()
|
|
268
309
|
self.builder.emit_store(name)
|
|
269
310
|
|
|
270
311
|
def _compile_ConstStatement(self, node: zexus_ast.ConstStatement):
|
|
271
312
|
"""Compile const statement"""
|
|
272
|
-
#
|
|
313
|
+
# Const supports the same destructuring patterns as let.
|
|
314
|
+
if isinstance(getattr(node, "name", None), zexus_ast.DestructurePattern):
|
|
315
|
+
# Reuse let destructuring behavior
|
|
316
|
+
fake_let = node
|
|
317
|
+
return self._compile_LetStatement(fake_let)
|
|
318
|
+
|
|
273
319
|
self._compile_node(node.value)
|
|
274
320
|
name = str(node.name.value).strip()
|
|
275
321
|
self.builder.emit_store(name)
|
|
@@ -283,10 +329,18 @@ class EvaluatorBytecodeCompiler:
|
|
|
283
329
|
self.builder.emit("RETURN")
|
|
284
330
|
|
|
285
331
|
def _compile_ContinueStatement(self, node: zexus_ast.ContinueStatement):
|
|
286
|
-
"""Compile continue statement
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
332
|
+
"""Compile continue statement.
|
|
333
|
+
|
|
334
|
+
Behavior (mirrors VM compiler):
|
|
335
|
+
- Inside a loop: jump to the loop's continue target
|
|
336
|
+
- Outside a loop: enable continue-on-error mode
|
|
337
|
+
"""
|
|
338
|
+
if self._loop_stack:
|
|
339
|
+
continue_label = self._loop_stack[-1].get('continue')
|
|
340
|
+
if continue_label:
|
|
341
|
+
self.builder.emit_jump(continue_label)
|
|
342
|
+
return
|
|
343
|
+
self.builder.emit("ENABLE_ERROR_MODE")
|
|
290
344
|
|
|
291
345
|
def _compile_BreakStatement(self, node: zexus_ast.BreakStatement):
|
|
292
346
|
"""Compile break statement by jumping to loop end label"""
|
|
@@ -350,6 +404,53 @@ class EvaluatorBytecodeCompiler:
|
|
|
350
404
|
|
|
351
405
|
# While statements don't return values by default
|
|
352
406
|
self.builder.emit_constant(None)
|
|
407
|
+
|
|
408
|
+
def _compile_ForStatement(self, node):
|
|
409
|
+
"""Compile classic for loop (initializer; condition; increment) { body }.
|
|
410
|
+
|
|
411
|
+
Note: The core parser currently focuses on for-each loops, but other
|
|
412
|
+
parsing strategies / legacy ASTs may still produce ForStatement nodes.
|
|
413
|
+
This keeps the evaluator bytecode compiler aligned with the VM compiler.
|
|
414
|
+
"""
|
|
415
|
+
start_label = f"for_start_{id(node)}"
|
|
416
|
+
end_label = f"for_end_{id(node)}"
|
|
417
|
+
continue_label = start_label if getattr(node, "increment", None) is None else f"for_continue_{id(node)}"
|
|
418
|
+
|
|
419
|
+
# Initializer
|
|
420
|
+
initializer = getattr(node, "initializer", None)
|
|
421
|
+
if initializer is not None:
|
|
422
|
+
self._compile_node(initializer)
|
|
423
|
+
|
|
424
|
+
# Track loop labels for break/continue
|
|
425
|
+
self._loop_stack.append({'start': start_label, 'end': end_label, 'continue': continue_label})
|
|
426
|
+
|
|
427
|
+
# Condition
|
|
428
|
+
self.builder.mark_label(start_label)
|
|
429
|
+
condition = getattr(node, "condition", None)
|
|
430
|
+
if condition is not None:
|
|
431
|
+
self._compile_node(condition)
|
|
432
|
+
self.builder.emit_jump_if_false(end_label)
|
|
433
|
+
|
|
434
|
+
# Body
|
|
435
|
+
body = getattr(node, "body", None)
|
|
436
|
+
if body is not None:
|
|
437
|
+
self._compile_node(body)
|
|
438
|
+
|
|
439
|
+
# Increment
|
|
440
|
+
increment = getattr(node, "increment", None)
|
|
441
|
+
if increment is not None:
|
|
442
|
+
self.builder.mark_label(continue_label)
|
|
443
|
+
self._compile_node(increment)
|
|
444
|
+
self.builder.emit("POP")
|
|
445
|
+
|
|
446
|
+
# Jump back to condition
|
|
447
|
+
self.builder.emit_jump(start_label)
|
|
448
|
+
|
|
449
|
+
# End
|
|
450
|
+
self.builder.mark_label(end_label)
|
|
451
|
+
self._loop_stack.pop()
|
|
452
|
+
|
|
453
|
+
self.builder.emit_constant(None)
|
|
353
454
|
|
|
354
455
|
def _compile_ForEachStatement(self, node: zexus_ast.ForEachStatement):
|
|
355
456
|
"""Compile for-each loop"""
|
|
@@ -458,14 +559,18 @@ class EvaluatorBytecodeCompiler:
|
|
|
458
559
|
|
|
459
560
|
def _compile_PrintStatement(self, node: zexus_ast.PrintStatement):
|
|
460
561
|
"""Compile print statement"""
|
|
461
|
-
#
|
|
562
|
+
# PrintStatement can have value (single) or values (multiple)
|
|
563
|
+
if hasattr(node, 'values') and node.values:
|
|
564
|
+
for val in node.values:
|
|
565
|
+
self._compile_node(val)
|
|
566
|
+
self.builder.emit("PRINT")
|
|
567
|
+
return
|
|
568
|
+
|
|
462
569
|
if hasattr(node, 'value') and node.value:
|
|
463
570
|
self._compile_node(node.value)
|
|
464
571
|
else:
|
|
465
|
-
# Empty print
|
|
466
572
|
self.builder.emit_constant("")
|
|
467
|
-
|
|
468
|
-
# Emit PRINT opcode
|
|
573
|
+
|
|
469
574
|
self.builder.emit("PRINT")
|
|
470
575
|
|
|
471
576
|
def _compile_UseStatement(self, node: zexus_ast.UseStatement):
|
|
@@ -547,6 +652,24 @@ class EvaluatorBytecodeCompiler:
|
|
|
547
652
|
def _compile_PatternStatement(self, node: zexus_ast.PatternStatement):
|
|
548
653
|
"""Compile pattern matching directives to VM builtins."""
|
|
549
654
|
self._emit_vm_builtin_call("__vm_pattern_statement__", node, discard_result=False)
|
|
655
|
+
|
|
656
|
+
def _compile_ExportStatement(self, node):
|
|
657
|
+
"""Compile export statement - no-op for bytecode.
|
|
658
|
+
|
|
659
|
+
Export is a declaration/annotation; the names being exported
|
|
660
|
+
are already defined by their own declarations (contract, action, etc.).
|
|
661
|
+
The evaluator handles export registration at module level.
|
|
662
|
+
"""
|
|
663
|
+
# Export names are registered by the evaluator, not bytecode
|
|
664
|
+
pass
|
|
665
|
+
|
|
666
|
+
def _compile_EmitStatement(self, node):
|
|
667
|
+
"""Compile emit statement to VM builtin call."""
|
|
668
|
+
self._emit_vm_builtin_call("__vm_emit_statement__", node, discard_result=False)
|
|
669
|
+
|
|
670
|
+
def _compile_ProtocolStatement(self, node):
|
|
671
|
+
"""Compile protocol statement to VM builtin call."""
|
|
672
|
+
self._emit_vm_builtin_call("__vm_protocol_statement__", node, discard_result=False)
|
|
550
673
|
|
|
551
674
|
# === Blockchain Statement Compilation ===
|
|
552
675
|
|
|
@@ -582,7 +705,7 @@ class EvaluatorBytecodeCompiler:
|
|
|
582
705
|
|
|
583
706
|
# Jump if true (condition passes)
|
|
584
707
|
pass_label = f"require_pass_{id(node)}"
|
|
585
|
-
self.builder.
|
|
708
|
+
self.builder.emit_jump_if_true(pass_label)
|
|
586
709
|
|
|
587
710
|
# Condition failed - revert
|
|
588
711
|
if hasattr(node, 'message') and node.message:
|
|
@@ -697,34 +820,8 @@ class EvaluatorBytecodeCompiler:
|
|
|
697
820
|
# Build map from stack
|
|
698
821
|
self.builder.emit("BUILD_MAP", len(node.pairs))
|
|
699
822
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if self.builder is None:
|
|
703
|
-
return
|
|
704
|
-
|
|
705
|
-
# Compile object
|
|
706
|
-
self._compile_node(node.object)
|
|
707
|
-
|
|
708
|
-
if node.computed:
|
|
709
|
-
# Bracket notation: obj[expr]
|
|
710
|
-
self._compile_node(node.property)
|
|
711
|
-
self._builder_emit("INDEX")
|
|
712
|
-
else:
|
|
713
|
-
# Dot notation: obj.prop
|
|
714
|
-
if hasattr(node.property, 'value'):
|
|
715
|
-
name = node.property.value
|
|
716
|
-
else:
|
|
717
|
-
name = str(node.property)
|
|
718
|
-
|
|
719
|
-
# Push property name as constant
|
|
720
|
-
self.builder.emit_constant(name)
|
|
721
|
-
self._builder_emit("GET_ATTR")
|
|
722
|
-
|
|
723
|
-
def _builder_emit(self, opcode):
|
|
724
|
-
"""Helper to emit opcode string or enum"""
|
|
725
|
-
# If builder has emit, use it. Some builders might use emit(opcode, operand).
|
|
726
|
-
# Wrapper to handle potential string usage if enum not fully refreshed
|
|
727
|
-
self.builder.emit(opcode)
|
|
823
|
+
# LI12: _compile_PropertyAccessExpression is defined later in this file.
|
|
824
|
+
# The earlier duplicate implementation was dead code (overridden).
|
|
728
825
|
|
|
729
826
|
def _compile_InfixExpression(self, node: zexus_ast.InfixExpression):
|
|
730
827
|
"""Compile infix expression"""
|
|
@@ -1165,7 +1262,8 @@ class EvaluatorBytecodeCompiler:
|
|
|
1165
1262
|
elif op == 'OR':
|
|
1166
1263
|
return BooleanObj(bool(v1) or bool(v2))
|
|
1167
1264
|
|
|
1168
|
-
except:
|
|
1265
|
+
except Exception:
|
|
1266
|
+
# SECURITY (H11): Narrowed from bare except: to Exception
|
|
1169
1267
|
pass
|
|
1170
1268
|
|
|
1171
1269
|
return None
|
|
@@ -1278,7 +1376,7 @@ class EvaluatorBytecodeCompiler:
|
|
|
1278
1376
|
# List of supported node types
|
|
1279
1377
|
supported = {
|
|
1280
1378
|
'Program', 'ExpressionStatement', 'LetStatement', 'ConstStatement',
|
|
1281
|
-
'ReturnStatement', 'ContinueStatement', 'IfStatement', 'WhileStatement', 'ForEachStatement',
|
|
1379
|
+
'ReturnStatement', 'ContinueStatement', 'IfStatement', 'WhileStatement', 'ForStatement', 'ForEachStatement',
|
|
1282
1380
|
'BlockStatement', 'ActionStatement', 'FunctionStatement', 'PrintStatement',
|
|
1283
1381
|
'Identifier', 'IntegerLiteral', 'FloatLiteral',
|
|
1284
1382
|
'StringLiteral', 'Boolean', 'ListLiteral', 'MapLiteral', 'NullLiteral',
|
|
@@ -1386,7 +1484,7 @@ def should_use_vm_for_node(node) -> bool:
|
|
|
1386
1484
|
node_type = type(node).__name__
|
|
1387
1485
|
|
|
1388
1486
|
# Always use VM for loops
|
|
1389
|
-
if node_type in ['WhileStatement', 'ForEachStatement']:
|
|
1487
|
+
if node_type in ['WhileStatement', 'ForStatement', 'ForEachStatement']:
|
|
1390
1488
|
return True
|
|
1391
1489
|
|
|
1392
1490
|
# Use VM for complex functions
|