zexus 1.6.8 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- 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/capability_system.py +184 -9
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +383 -34
- 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/bytecode.py +124 -7
- package/src/zexus/compiler/compat_runtime.py +6 -2
- package/src/zexus/compiler/lexer.py +16 -5
- package/src/zexus/compiler/parser.py +108 -7
- package/src/zexus/compiler/semantic.py +18 -19
- package/src/zexus/compiler/zexus_ast.py +26 -1
- 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 +112 -9
- 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 +457 -37
- package/src/zexus/evaluator/core.py +644 -50
- package/src/zexus/evaluator/expressions.py +358 -62
- package/src/zexus/evaluator/functions.py +458 -20
- package/src/zexus/evaluator/resource_limiter.py +4 -4
- package/src/zexus/evaluator/statements.py +774 -122
- package/src/zexus/evaluator/unified_execution.py +573 -72
- package/src/zexus/evaluator/utils.py +14 -2
- package/src/zexus/evaluator_original.py +1 -1
- package/src/zexus/event_loop.py +186 -0
- package/src/zexus/lexer.py +742 -458
- 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 +239 -9
- package/src/zexus/module_manager.py +129 -1
- package/src/zexus/object.py +76 -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 +1349 -408
- package/src/zexus/parser/strategy_context.py +755 -58
- package/src/zexus/parser/strategy_structural.py +121 -21
- package/src/zexus/persistence.py +15 -1
- package/src/zexus/renderer/__init__.py +61 -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/backend.py +261 -0
- package/src/zexus/renderer/canvas.py +78 -0
- package/src/zexus/renderer/color_system.py +201 -0
- package/src/zexus/renderer/graphics.py +31 -0
- package/src/zexus/renderer/layout.py +222 -0
- package/src/zexus/renderer/main_renderer.py +66 -0
- package/src/zexus/renderer/painter.py +30 -0
- package/src/zexus/renderer/tk_backend.py +208 -0
- package/src/zexus/renderer/web_backend.py +260 -0
- package/src/zexus/runtime/__init__.py +10 -2
- 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/runtime/load_manager.py +368 -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 +80 -6
- package/src/zexus/vm/binary_bytecode.py +659 -0
- package/src/zexus/vm/bytecode.py +59 -11
- 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 +561 -17
- package/src/zexus/vm/compiler.py +818 -51
- 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 +364 -20
- 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 +140 -45
- 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 +3581 -531
- package/src/zexus/vm/wasm_compiler.py +658 -0
- package/src/zexus/zexus_ast.py +137 -11
- package/src/zexus/zexus_token.py +16 -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 +16 -6
- package/src/zexus.egg-info/SOURCES.txt +129 -17
- package/src/zexus.egg-info/entry_points.txt +1 -0
- package/src/zexus.egg-info/requires.txt +4 -0
package/src/zexus/vm/compiler.py
CHANGED
|
@@ -10,6 +10,20 @@ from .bytecode import Opcode, Bytecode
|
|
|
10
10
|
from .. import zexus_ast
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
class BytecodeCompilationError(Exception):
|
|
14
|
+
"""Raised when bytecode compilation cannot complete successfully."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UnsupportedNodeError(BytecodeCompilationError):
|
|
18
|
+
"""Raised when the compiler encounters an unsupported AST node."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, node_type: str, detail: Optional[str] = None):
|
|
21
|
+
message = detail or f"Node type '{node_type}' is not supported by the bytecode compiler"
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.node_type = node_type
|
|
24
|
+
self.detail = detail
|
|
25
|
+
|
|
26
|
+
|
|
13
27
|
class BytecodeCompiler:
|
|
14
28
|
"""
|
|
15
29
|
Compiles Zexus AST to VM bytecode.
|
|
@@ -28,6 +42,7 @@ class BytecodeCompiler:
|
|
|
28
42
|
self.constant_map = {} # Deduplicate constants
|
|
29
43
|
self.label_counter = 0
|
|
30
44
|
self.loop_stack = [] # For break/continue
|
|
45
|
+
self.temp_counter = 0
|
|
31
46
|
|
|
32
47
|
def compile(self, node) -> Bytecode:
|
|
33
48
|
"""Compile AST node to bytecode"""
|
|
@@ -35,21 +50,48 @@ class BytecodeCompiler:
|
|
|
35
50
|
self.instructions = []
|
|
36
51
|
self.constant_map = {}
|
|
37
52
|
self.label_counter = 0
|
|
53
|
+
self.temp_counter = 0
|
|
38
54
|
|
|
39
55
|
self._compile_node(node)
|
|
40
56
|
|
|
57
|
+
self._resolve_labels()
|
|
58
|
+
|
|
41
59
|
return Bytecode(self.instructions, self.constants)
|
|
42
60
|
|
|
61
|
+
def _resolve_labels(self):
|
|
62
|
+
"""Resolve label strings to instruction indices"""
|
|
63
|
+
labels = {}
|
|
64
|
+
|
|
65
|
+
# 1. Map labels
|
|
66
|
+
for i, (op, operand) in enumerate(self.instructions):
|
|
67
|
+
if op == Opcode.NOP and isinstance(operand, str) and operand.startswith('L'):
|
|
68
|
+
labels[operand] = i
|
|
69
|
+
|
|
70
|
+
# 2. Update jumps
|
|
71
|
+
for i, (op, operand) in enumerate(self.instructions):
|
|
72
|
+
if op in (Opcode.JUMP, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE):
|
|
73
|
+
if isinstance(operand, str) and operand in labels:
|
|
74
|
+
self.instructions[i] = (op, labels[operand])
|
|
75
|
+
|
|
43
76
|
def _add_constant(self, value) -> int:
|
|
44
77
|
"""Add constant to pool, return index"""
|
|
45
|
-
# Deduplicate constants
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
# Deduplicate only cheap, stable primitive constants.
|
|
79
|
+
# Avoid calling str(value) for large/complex values (AST nodes, dicts,
|
|
80
|
+
# bytecode specs) because that can be expensive on large programs and
|
|
81
|
+
# can balloon memory.
|
|
82
|
+
key = None
|
|
83
|
+
if value is None or isinstance(value, (bool, int, float, str)):
|
|
84
|
+
if isinstance(value, str) and len(value) > 256:
|
|
85
|
+
key = None
|
|
86
|
+
else:
|
|
87
|
+
key = (type(value), value)
|
|
88
|
+
if key is not None and key in self.constant_map:
|
|
48
89
|
return self.constant_map[key]
|
|
49
90
|
|
|
50
91
|
idx = len(self.constants)
|
|
51
92
|
self.constants.append(value)
|
|
52
|
-
|
|
93
|
+
if key is not None:
|
|
94
|
+
self.constant_map[key] = idx
|
|
53
95
|
return idx
|
|
54
96
|
|
|
55
97
|
def _emit(self, opcode: Opcode, *args):
|
|
@@ -66,6 +108,15 @@ class BytecodeCompiler:
|
|
|
66
108
|
label = f"L{self.label_counter}"
|
|
67
109
|
self.label_counter += 1
|
|
68
110
|
return label
|
|
111
|
+
|
|
112
|
+
def _make_temp_name(self, prefix: str = "tmp") -> str:
|
|
113
|
+
name = f"__{prefix}_{self.temp_counter}"
|
|
114
|
+
self.temp_counter += 1
|
|
115
|
+
return name
|
|
116
|
+
|
|
117
|
+
def _mark_label(self, label: str):
|
|
118
|
+
self._emit(Opcode.NOP)
|
|
119
|
+
self.instructions[-1] = (Opcode.NOP, label)
|
|
69
120
|
|
|
70
121
|
def _compile_node(self, node):
|
|
71
122
|
"""Dispatch to specific compiler method"""
|
|
@@ -76,10 +127,39 @@ class BytecodeCompiler:
|
|
|
76
127
|
method_name = f'_compile_{node_type}'
|
|
77
128
|
method = getattr(self, method_name, None)
|
|
78
129
|
|
|
79
|
-
if method:
|
|
130
|
+
if method is None:
|
|
131
|
+
raise UnsupportedNodeError(node_type, self._unsupported_message(node_type))
|
|
132
|
+
|
|
133
|
+
try:
|
|
80
134
|
method(node)
|
|
81
|
-
|
|
82
|
-
raise
|
|
135
|
+
except UnsupportedNodeError:
|
|
136
|
+
raise
|
|
137
|
+
except Exception as exc:
|
|
138
|
+
raise BytecodeCompilationError(
|
|
139
|
+
f"Failed to compile {node_type}: {exc}"
|
|
140
|
+
) from exc
|
|
141
|
+
|
|
142
|
+
def _unsupported_message(self, node_type: str) -> str:
|
|
143
|
+
"""Return a friendly message for unsupported nodes."""
|
|
144
|
+
hints = {
|
|
145
|
+
"UseStatement": "Module imports",
|
|
146
|
+
"FromStatement": "Module imports",
|
|
147
|
+
"EntityStatement": "Entity declarations",
|
|
148
|
+
"ContractStatement": "Contract declarations",
|
|
149
|
+
"ActionStatement": "Action/function declarations",
|
|
150
|
+
"FunctionStatement": "Function declarations",
|
|
151
|
+
"IfStatement": "If statements",
|
|
152
|
+
"WhileStatement": "While loops",
|
|
153
|
+
"ForStatement": "For loops",
|
|
154
|
+
"ForEachStatement": "For-each loops",
|
|
155
|
+
"TxStatement": "Transaction blocks",
|
|
156
|
+
"RequireStatement": "Require statements",
|
|
157
|
+
"RevertStatement": "Revert statements",
|
|
158
|
+
}
|
|
159
|
+
prefix = hints.get(node_type)
|
|
160
|
+
if prefix:
|
|
161
|
+
return f"{prefix} are not yet supported by the VM bytecode compiler"
|
|
162
|
+
return f"Node type '{node_type}' is not supported by the VM bytecode compiler"
|
|
83
163
|
|
|
84
164
|
# ==================== Program & Statements ====================
|
|
85
165
|
|
|
@@ -117,7 +197,53 @@ class BytecodeCompiler:
|
|
|
117
197
|
self._emit(Opcode.PRINT)
|
|
118
198
|
|
|
119
199
|
def _compile_LetStatement(self, node):
|
|
120
|
-
"""Compile let/const declaration"""
|
|
200
|
+
"""Compile let/const declaration, including destructuring patterns."""
|
|
201
|
+
from ..zexus_ast import DestructurePattern
|
|
202
|
+
|
|
203
|
+
if isinstance(node.name, DestructurePattern):
|
|
204
|
+
# Compile the RHS value expression — leaves it on the stack
|
|
205
|
+
self._compile_node(node.value)
|
|
206
|
+
pattern = node.name
|
|
207
|
+
|
|
208
|
+
if pattern.kind == 'map':
|
|
209
|
+
for source_key, target_name in pattern.bindings:
|
|
210
|
+
# DUP the map on the stack so we can index multiple times
|
|
211
|
+
self._emit(Opcode.DUP)
|
|
212
|
+
# Push the key string
|
|
213
|
+
key_idx = self._add_constant(source_key)
|
|
214
|
+
self._emit(Opcode.LOAD_CONST, key_idx)
|
|
215
|
+
# Index into the map: map[key]
|
|
216
|
+
self._emit(Opcode.INDEX)
|
|
217
|
+
# Store to local name
|
|
218
|
+
name_idx = self._add_constant(target_name)
|
|
219
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
220
|
+
# Pop the original map off the stack
|
|
221
|
+
self._emit(Opcode.POP)
|
|
222
|
+
elif pattern.kind == 'list':
|
|
223
|
+
for idx, target_name in pattern.bindings:
|
|
224
|
+
self._emit(Opcode.DUP)
|
|
225
|
+
idx_const = self._add_constant(idx)
|
|
226
|
+
self._emit(Opcode.LOAD_CONST, idx_const)
|
|
227
|
+
self._emit(Opcode.INDEX)
|
|
228
|
+
name_idx = self._add_constant(target_name)
|
|
229
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
230
|
+
# Handle rest element: ..rest captures remaining items
|
|
231
|
+
if pattern.rest:
|
|
232
|
+
rest_start = len(pattern.bindings)
|
|
233
|
+
# SLICE expects stack: [obj, start, end]
|
|
234
|
+
self._emit(Opcode.DUP) # list
|
|
235
|
+
start_idx = self._add_constant(rest_start)
|
|
236
|
+
self._emit(Opcode.LOAD_CONST, start_idx) # start
|
|
237
|
+
none_idx = self._add_constant(None)
|
|
238
|
+
self._emit(Opcode.LOAD_CONST, none_idx) # end (None = to end)
|
|
239
|
+
self._emit(Opcode.SLICE)
|
|
240
|
+
rest_name_idx = self._add_constant(pattern.rest)
|
|
241
|
+
self._emit(Opcode.STORE_NAME, rest_name_idx)
|
|
242
|
+
# Pop the original list off the stack
|
|
243
|
+
self._emit(Opcode.POP)
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
# Normal (non-destructuring) let/const
|
|
121
247
|
# Compile value
|
|
122
248
|
self._compile_node(node.value)
|
|
123
249
|
|
|
@@ -171,6 +297,30 @@ class BytecodeCompiler:
|
|
|
171
297
|
"""Load boolean constant"""
|
|
172
298
|
const_idx = self._add_constant(node.value)
|
|
173
299
|
self._emit(Opcode.LOAD_CONST, const_idx)
|
|
300
|
+
|
|
301
|
+
def _compile_NullLiteral(self, node):
|
|
302
|
+
"""Load null constant"""
|
|
303
|
+
const_idx = self._add_constant(None)
|
|
304
|
+
self._emit(Opcode.LOAD_CONST, const_idx)
|
|
305
|
+
|
|
306
|
+
def _compile_PropertyAccessExpression(self, node):
|
|
307
|
+
"""Compile property access"""
|
|
308
|
+
# Compile object
|
|
309
|
+
self._compile_node(node.object)
|
|
310
|
+
|
|
311
|
+
if node.computed:
|
|
312
|
+
# Bracket notation: obj[expr]
|
|
313
|
+
self._compile_node(node.property)
|
|
314
|
+
self._emit(Opcode.INDEX)
|
|
315
|
+
else:
|
|
316
|
+
# Dot notation: obj.prop
|
|
317
|
+
if hasattr(node.property, 'value'):
|
|
318
|
+
name = node.property.value
|
|
319
|
+
else:
|
|
320
|
+
name = str(node.property)
|
|
321
|
+
const_idx = self._add_constant(name)
|
|
322
|
+
self._emit(Opcode.LOAD_CONST, const_idx)
|
|
323
|
+
self._emit(Opcode.GET_ATTR)
|
|
174
324
|
|
|
175
325
|
# ==================== Binary Operations ====================
|
|
176
326
|
|
|
@@ -247,32 +397,58 @@ class BytecodeCompiler:
|
|
|
247
397
|
self._emit(Opcode.NOP)
|
|
248
398
|
self.instructions[-1] = (Opcode.NOP, end_label)
|
|
249
399
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
400
|
+
_compile_IfStatement = _compile_IfExpression
|
|
401
|
+
|
|
402
|
+
def _compile_TernaryExpression(self, node):
|
|
403
|
+
"""Compile ternary expression (cond ? true : false)"""
|
|
404
|
+
# Compile condition
|
|
405
|
+
self._compile_node(node.condition)
|
|
406
|
+
|
|
407
|
+
# Jump if false
|
|
408
|
+
else_label = self._make_label()
|
|
253
409
|
end_label = self._make_label()
|
|
254
410
|
|
|
255
|
-
|
|
256
|
-
self.loop_stack.append((start_label, end_label))
|
|
411
|
+
self._emit(Opcode.JUMP_IF_FALSE, else_label)
|
|
257
412
|
|
|
258
|
-
#
|
|
259
|
-
self.
|
|
260
|
-
self.
|
|
413
|
+
# True branch
|
|
414
|
+
self._compile_node(node.true_value)
|
|
415
|
+
self._emit(Opcode.JUMP, end_label)
|
|
261
416
|
|
|
262
|
-
#
|
|
263
|
-
self.
|
|
264
|
-
self.
|
|
417
|
+
# False branch
|
|
418
|
+
self._mark_label(else_label)
|
|
419
|
+
self._compile_node(node.false_value)
|
|
265
420
|
|
|
266
|
-
|
|
421
|
+
self._mark_label(end_label)
|
|
422
|
+
|
|
423
|
+
def _compile_WhileStatement(self, node):
|
|
424
|
+
"""Compile while loop"""
|
|
425
|
+
start_label = self._make_label()
|
|
426
|
+
end_label = self._make_label()
|
|
427
|
+
loop_info = {
|
|
428
|
+
'start': start_label,
|
|
429
|
+
'end': end_label,
|
|
430
|
+
'continue': start_label,
|
|
431
|
+
}
|
|
432
|
+
self.loop_stack.append(loop_info)
|
|
433
|
+
|
|
434
|
+
# Start label before evaluating condition
|
|
435
|
+
self._mark_label(start_label)
|
|
436
|
+
|
|
437
|
+
# Condition
|
|
438
|
+
self._compile_node(node.condition)
|
|
439
|
+
exit_jump_idx = len(self.instructions)
|
|
440
|
+
self._emit(Opcode.JUMP_IF_FALSE, None)
|
|
441
|
+
|
|
442
|
+
# Body
|
|
267
443
|
self._compile_node(node.body)
|
|
268
|
-
|
|
269
|
-
#
|
|
444
|
+
|
|
445
|
+
# Jump back to start
|
|
270
446
|
self._emit(Opcode.JUMP, start_label)
|
|
271
|
-
|
|
272
|
-
# End
|
|
273
|
-
self.
|
|
274
|
-
self.instructions[
|
|
275
|
-
|
|
447
|
+
|
|
448
|
+
# End label
|
|
449
|
+
self._mark_label(end_label)
|
|
450
|
+
self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
|
|
451
|
+
|
|
276
452
|
self.loop_stack.pop()
|
|
277
453
|
|
|
278
454
|
def _compile_ForStatement(self, node):
|
|
@@ -283,33 +459,40 @@ class BytecodeCompiler:
|
|
|
283
459
|
|
|
284
460
|
start_label = self._make_label()
|
|
285
461
|
end_label = self._make_label()
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
462
|
+
continue_label = start_label if node.increment is None else self._make_label()
|
|
463
|
+
|
|
464
|
+
self.loop_stack.append({
|
|
465
|
+
'start': start_label,
|
|
466
|
+
'end': end_label,
|
|
467
|
+
'continue': continue_label,
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
# Loop start (condition)
|
|
471
|
+
self._mark_label(start_label)
|
|
472
|
+
|
|
473
|
+
exit_jump_idx = None
|
|
294
474
|
if node.condition:
|
|
295
475
|
self._compile_node(node.condition)
|
|
296
|
-
self.
|
|
476
|
+
exit_jump_idx = len(self.instructions)
|
|
477
|
+
self._emit(Opcode.JUMP_IF_FALSE, None)
|
|
297
478
|
|
|
298
479
|
# Body
|
|
299
480
|
self._compile_node(node.body)
|
|
300
|
-
|
|
301
|
-
#
|
|
481
|
+
|
|
482
|
+
# Continue label placed before increment (if present)
|
|
302
483
|
if node.increment:
|
|
484
|
+
self._mark_label(continue_label)
|
|
303
485
|
self._compile_node(node.increment)
|
|
304
486
|
self._emit(Opcode.POP)
|
|
305
|
-
|
|
306
|
-
#
|
|
487
|
+
|
|
488
|
+
# Jump back to condition
|
|
307
489
|
self._emit(Opcode.JUMP, start_label)
|
|
308
|
-
|
|
309
|
-
#
|
|
310
|
-
self.
|
|
311
|
-
|
|
312
|
-
|
|
490
|
+
|
|
491
|
+
# Loop end label
|
|
492
|
+
self._mark_label(end_label)
|
|
493
|
+
if exit_jump_idx is not None:
|
|
494
|
+
self.instructions[exit_jump_idx] = (Opcode.JUMP_IF_FALSE, end_label)
|
|
495
|
+
|
|
313
496
|
self.loop_stack.pop()
|
|
314
497
|
|
|
315
498
|
# ==================== Function Calls ====================
|
|
@@ -360,15 +543,25 @@ class BytecodeCompiler:
|
|
|
360
543
|
count = len(node.elements)
|
|
361
544
|
self._emit(Opcode.BUILD_LIST, count)
|
|
362
545
|
|
|
546
|
+
_compile_ListLiteral = _compile_ArrayLiteral
|
|
547
|
+
|
|
363
548
|
def _compile_MapLiteral(self, node):
|
|
364
549
|
"""Compile map/dictionary literal"""
|
|
365
550
|
# Compile key-value pairs
|
|
366
|
-
|
|
551
|
+
pairs = node.pairs or []
|
|
552
|
+
|
|
553
|
+
if isinstance(pairs, dict):
|
|
554
|
+
iterable = pairs.items()
|
|
555
|
+
count = len(pairs)
|
|
556
|
+
else:
|
|
557
|
+
iterable = pairs
|
|
558
|
+
count = len(pairs)
|
|
559
|
+
|
|
560
|
+
for key, value in iterable:
|
|
367
561
|
self._compile_node(key)
|
|
368
562
|
self._compile_node(value)
|
|
369
563
|
|
|
370
564
|
# Build map
|
|
371
|
-
count = len(node.pairs)
|
|
372
565
|
self._emit(Opcode.BUILD_MAP, count)
|
|
373
566
|
|
|
374
567
|
def _compile_IndexExpression(self, node):
|
|
@@ -376,7 +569,583 @@ class BytecodeCompiler:
|
|
|
376
569
|
self._compile_node(node.left)
|
|
377
570
|
self._compile_node(node.index)
|
|
378
571
|
self._emit(Opcode.INDEX)
|
|
572
|
+
|
|
573
|
+
def _compile_PropertyAccessExpression(self, node):
|
|
574
|
+
"""Compile property access (obj.prop or obj[expr])."""
|
|
575
|
+
self._compile_node(node.object)
|
|
576
|
+
|
|
577
|
+
if getattr(node, "computed", False):
|
|
578
|
+
self._compile_node(node.property)
|
|
579
|
+
else:
|
|
580
|
+
prop_name = node.property.value if hasattr(node.property, 'value') else str(node.property)
|
|
581
|
+
prop_idx = self._add_constant(prop_name)
|
|
582
|
+
self._emit(Opcode.LOAD_CONST, prop_idx)
|
|
583
|
+
|
|
584
|
+
self._emit(Opcode.INDEX)
|
|
585
|
+
|
|
586
|
+
def _compile_SliceExpression(self, node):
|
|
587
|
+
"""Compile slice expression: obj[start:end]"""
|
|
588
|
+
self._compile_node(node.object)
|
|
589
|
+
|
|
590
|
+
if node.start is not None:
|
|
591
|
+
self._compile_node(node.start)
|
|
592
|
+
else:
|
|
593
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
594
|
+
|
|
595
|
+
if node.end is not None:
|
|
596
|
+
self._compile_node(node.end)
|
|
597
|
+
else:
|
|
598
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
599
|
+
|
|
600
|
+
self._emit(Opcode.SLICE)
|
|
601
|
+
|
|
602
|
+
# ==================== Advanced Control Flow & Declarations ====================
|
|
603
|
+
|
|
604
|
+
def _compile_ActionStatement(self, node):
|
|
605
|
+
"""Compile action/function definition"""
|
|
606
|
+
# Create a new compiler for the function body
|
|
607
|
+
func_compiler = self.__class__()
|
|
608
|
+
|
|
609
|
+
# Compile body
|
|
610
|
+
func_compiler._compile_node(node.body)
|
|
611
|
+
|
|
612
|
+
# Ensure implicit return (null) if execution flows off the end
|
|
613
|
+
null_idx = func_compiler._add_constant(None)
|
|
614
|
+
func_compiler._emit(Opcode.LOAD_CONST, null_idx)
|
|
615
|
+
func_compiler._emit(Opcode.RETURN)
|
|
616
|
+
|
|
617
|
+
# Get bytecode
|
|
618
|
+
from .bytecode import Bytecode
|
|
619
|
+
func_compiler._resolve_labels()
|
|
620
|
+
func_bytecode = Bytecode(func_compiler.instructions, func_compiler.constants)
|
|
621
|
+
|
|
622
|
+
# Prepare function descriptor
|
|
623
|
+
params = [p.value if hasattr(p, 'value') else str(p) for p in node.parameters]
|
|
624
|
+
func_desc = {
|
|
625
|
+
"bytecode": func_bytecode,
|
|
626
|
+
"params": params,
|
|
627
|
+
"is_async": getattr(node, 'is_async', False),
|
|
628
|
+
"name": node.name.value if hasattr(node.name, 'value') else str(node.name)
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
# Store using STORE_FUNC
|
|
632
|
+
# Operand: (name_idx, func_const_idx)
|
|
633
|
+
func_idx = self._add_constant(func_desc)
|
|
634
|
+
name_idx = self._add_constant(func_desc["name"])
|
|
635
|
+
|
|
636
|
+
self._emit(Opcode.STORE_FUNC, (name_idx, func_idx))
|
|
637
|
+
|
|
638
|
+
# Aliases for other function types
|
|
639
|
+
_compile_FunctionStatement = _compile_ActionStatement
|
|
640
|
+
_compile_PureFunctionStatement = _compile_ActionStatement
|
|
641
|
+
|
|
642
|
+
def _compile_ConstStatement(self, node):
|
|
643
|
+
"""Compile const declaration (same as Let for current VM)"""
|
|
644
|
+
self._compile_LetStatement(node)
|
|
645
|
+
|
|
646
|
+
def _compile_FileImportExpression(self, node):
|
|
647
|
+
"""Compile file import expression (<<)"""
|
|
648
|
+
self._compile_node(node.filepath)
|
|
649
|
+
self._emit(Opcode.READ)
|
|
650
|
+
|
|
651
|
+
def _compile_AssignmentExpression(self, node):
|
|
652
|
+
"""Compile variable or property assignment"""
|
|
653
|
+
|
|
654
|
+
# Check target type
|
|
655
|
+
target = node.name
|
|
656
|
+
|
|
657
|
+
# 1. Variable Assignment (target is Identifier or string)
|
|
658
|
+
if isinstance(target, (zexus_ast.Identifier, str)):
|
|
659
|
+
self._compile_node(node.value)
|
|
660
|
+
self._emit(Opcode.DUP)
|
|
661
|
+
name = target.value if hasattr(target, 'value') else str(target)
|
|
662
|
+
name_idx = self._add_constant(name)
|
|
663
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
664
|
+
return
|
|
665
|
+
|
|
666
|
+
# 2. Property/Index Assignment (target[key] = value, target.prop = value)
|
|
667
|
+
if hasattr(zexus_ast, 'IndexExpression') and isinstance(target, zexus_ast.IndexExpression) or \
|
|
668
|
+
isinstance(target, zexus_ast.PropertyAccessExpression):
|
|
669
|
+
# Transform to obj.set(key, value)
|
|
670
|
+
|
|
671
|
+
# 2a. Compile object
|
|
672
|
+
obj = target.object if isinstance(target, zexus_ast.PropertyAccessExpression) else target.left
|
|
673
|
+
self._compile_node(obj)
|
|
674
|
+
|
|
675
|
+
# 2b. Compile key
|
|
676
|
+
if isinstance(target, zexus_ast.PropertyAccessExpression):
|
|
677
|
+
if target.computed:
|
|
678
|
+
self._compile_node(target.property)
|
|
679
|
+
else:
|
|
680
|
+
if hasattr(target.property, 'value'):
|
|
681
|
+
prop_name = target.property.value
|
|
682
|
+
else:
|
|
683
|
+
prop_name = str(target.property)
|
|
684
|
+
idx = self._add_constant(prop_name)
|
|
685
|
+
self._emit(Opcode.LOAD_CONST, idx)
|
|
686
|
+
else: # IndexExpression
|
|
687
|
+
self._compile_node(target.index)
|
|
688
|
+
|
|
689
|
+
# 2c. Compile value
|
|
690
|
+
self._compile_node(node.value)
|
|
691
|
+
|
|
692
|
+
# 2d. Call set(key, value)
|
|
693
|
+
method_idx = self._add_constant("set")
|
|
694
|
+
self._emit(Opcode.CALL_METHOD, (method_idx, 2))
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
raise UnsupportedNodeError(type(target).__name__, "Assignment target not supported")
|
|
698
|
+
|
|
699
|
+
def _compile_UseStatement(self, node):
|
|
700
|
+
"""Compile module import via VM import helper."""
|
|
701
|
+
file_path_attr = getattr(node, 'file_path', None) or getattr(node, 'embedded_ref', None)
|
|
702
|
+
path = file_path_attr.value if hasattr(file_path_attr, 'value') else str(file_path_attr or "")
|
|
703
|
+
alias = node.alias.value if node.alias and hasattr(node.alias, 'value') else (node.alias or "")
|
|
704
|
+
|
|
705
|
+
names_list = []
|
|
706
|
+
if getattr(node, 'names', None):
|
|
707
|
+
for entry in node.names:
|
|
708
|
+
if entry is None:
|
|
709
|
+
continue
|
|
710
|
+
names_list.append(entry.value if hasattr(entry, 'value') else str(entry))
|
|
711
|
+
|
|
712
|
+
spec = {
|
|
713
|
+
"file": path,
|
|
714
|
+
"alias": alias,
|
|
715
|
+
"names": names_list,
|
|
716
|
+
"is_named": bool(getattr(node, 'is_named_import', False)) and len(names_list) > 0,
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
spec_idx = self._add_constant(spec)
|
|
720
|
+
self._emit(Opcode.LOAD_CONST, spec_idx)
|
|
721
|
+
name_idx = self._add_constant("__vm_use_module__")
|
|
722
|
+
self._emit(Opcode.CALL_NAME, (name_idx, 1))
|
|
723
|
+
self._emit(Opcode.POP)
|
|
724
|
+
|
|
725
|
+
def _compile_FromStatement(self, node):
|
|
726
|
+
"""Compile from-import statements into VM helper calls."""
|
|
727
|
+
file_path_attr = getattr(node, 'file_path', None) or getattr(node, 'embedded_ref', None)
|
|
728
|
+
file_path = file_path_attr.value if hasattr(file_path_attr, 'value') else str(file_path_attr or "")
|
|
729
|
+
entries = []
|
|
730
|
+
for entry in getattr(node, 'imports', []) or []:
|
|
731
|
+
if isinstance(entry, (list, tuple)):
|
|
732
|
+
base = entry[0] if len(entry) > 0 else None
|
|
733
|
+
alias = entry[1] if len(entry) > 1 else None
|
|
734
|
+
else:
|
|
735
|
+
base = entry
|
|
736
|
+
alias = None
|
|
737
|
+
if base is None:
|
|
738
|
+
continue
|
|
739
|
+
entries.append({
|
|
740
|
+
"name": base.value if hasattr(base, 'value') else str(base),
|
|
741
|
+
"alias": alias.value if hasattr(alias, 'value') else (str(alias) if alias else ""),
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
spec = {
|
|
745
|
+
"file": file_path,
|
|
746
|
+
"imports": entries,
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
spec_idx = self._add_constant(spec)
|
|
750
|
+
self._emit(Opcode.LOAD_CONST, spec_idx)
|
|
751
|
+
name_idx = self._add_constant("__vm_from_module__")
|
|
752
|
+
self._emit(Opcode.CALL_NAME, (name_idx, 1))
|
|
753
|
+
self._emit(Opcode.POP)
|
|
754
|
+
|
|
755
|
+
def _compile_TxStatement(self, node):
|
|
756
|
+
"""Compile transaction block with automatic revert on error.
|
|
757
|
+
|
|
758
|
+
Emits:
|
|
759
|
+
TX_BEGIN
|
|
760
|
+
SETUP_TRY catch_label
|
|
761
|
+
<body>
|
|
762
|
+
POP_TRY
|
|
763
|
+
TX_COMMIT
|
|
764
|
+
JUMP end_label
|
|
765
|
+
catch_label:
|
|
766
|
+
POP ; discard exception value
|
|
767
|
+
TX_REVERT
|
|
768
|
+
THROW ; re-raise so callers see the error
|
|
769
|
+
end_label:
|
|
770
|
+
"""
|
|
771
|
+
catch_label = self._make_label()
|
|
772
|
+
end_label = self._make_label()
|
|
773
|
+
|
|
774
|
+
self._emit(Opcode.TX_BEGIN)
|
|
775
|
+
self._emit(Opcode.SETUP_TRY, catch_label)
|
|
776
|
+
self._compile_node(node.body)
|
|
777
|
+
self._emit(Opcode.POP_TRY)
|
|
778
|
+
self._emit(Opcode.TX_COMMIT)
|
|
779
|
+
self._emit(Opcode.JUMP, end_label)
|
|
780
|
+
|
|
781
|
+
# Exception handler — revert and re-raise
|
|
782
|
+
self._mark_label(catch_label)
|
|
783
|
+
self._emit(Opcode.TX_REVERT)
|
|
784
|
+
self._emit(Opcode.THROW)
|
|
785
|
+
|
|
786
|
+
self._mark_label(end_label)
|
|
787
|
+
|
|
788
|
+
def _compile_GCStatement(self, node):
|
|
789
|
+
"""Compile GC statement"""
|
|
790
|
+
# Call builtin gc(action)
|
|
791
|
+
action = node.action.value if hasattr(node.action, 'value') else str(node.action)
|
|
792
|
+
|
|
793
|
+
# Load 'gc' function name
|
|
794
|
+
gc_idx = self._add_constant("gc")
|
|
795
|
+
|
|
796
|
+
# Load action argument
|
|
797
|
+
action_idx = self._add_constant(action)
|
|
798
|
+
self._emit(Opcode.LOAD_CONST, action_idx)
|
|
799
|
+
|
|
800
|
+
# Call gc(action)
|
|
801
|
+
self._emit(Opcode.CALL_NAME, (gc_idx, 1))
|
|
802
|
+
self._emit(Opcode.POP)
|
|
803
|
+
|
|
379
804
|
|
|
805
|
+
def _compile_ContractStatement(self, node):
|
|
806
|
+
"""Compile smart contract definition into bytecode that creates a real
|
|
807
|
+
SmartContract object at runtime.
|
|
808
|
+
|
|
809
|
+
Stack layout at DEFINE_CONTRACT execution (top → bottom):
|
|
810
|
+
contract_name
|
|
811
|
+
value_N, name_N ← last storage var
|
|
812
|
+
...
|
|
813
|
+
value_1, name_1 ← first storage var
|
|
814
|
+
|
|
815
|
+
The AST node is stored in the constants pool so the VM can create
|
|
816
|
+
Action objects (which hold the AST body + closure environment) and
|
|
817
|
+
access storage_vars metadata for SmartContract construction.
|
|
818
|
+
"""
|
|
819
|
+
contract_name = node.name.value
|
|
820
|
+
|
|
821
|
+
# Store the full AST node in constants so the VM has access to
|
|
822
|
+
# action definitions (parameters, body) and storage_vars metadata.
|
|
823
|
+
ast_idx = self._add_constant(node)
|
|
824
|
+
|
|
825
|
+
# Compile storage initial values to bytecode.
|
|
826
|
+
# Each var pushes (name, value) onto the stack.
|
|
827
|
+
storage_vars = getattr(node, 'storage_vars', [])
|
|
828
|
+
for sv in storage_vars:
|
|
829
|
+
# Push var name
|
|
830
|
+
var_name = sv.name.value if hasattr(sv, 'name') and hasattr(sv.name, 'value') else str(getattr(sv, 'name', ''))
|
|
831
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(var_name))
|
|
832
|
+
# Push initial value (or None)
|
|
833
|
+
if getattr(sv, 'initial_value', None):
|
|
834
|
+
self._compile_node(sv.initial_value)
|
|
835
|
+
else:
|
|
836
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
837
|
+
|
|
838
|
+
# Push contract name
|
|
839
|
+
name_idx = self._add_constant(contract_name)
|
|
840
|
+
self._emit(Opcode.LOAD_CONST, name_idx)
|
|
841
|
+
|
|
842
|
+
# Emit DEFINE_CONTRACT with (ast_idx, storage_count) so the VM can
|
|
843
|
+
# reconstruct the full SmartContract.
|
|
844
|
+
self._emit(Opcode.DEFINE_CONTRACT, (ast_idx, len(storage_vars)))
|
|
845
|
+
|
|
846
|
+
# Store the contract in the environment
|
|
847
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
848
|
+
|
|
849
|
+
def _compile_BreakStatement(self, node):
|
|
850
|
+
"""Compile break statement"""
|
|
851
|
+
if not self.loop_stack:
|
|
852
|
+
raise BytecodeCompilationError("Break statement outside loop")
|
|
853
|
+
self._emit(Opcode.JUMP, self.loop_stack[-1]['end'])
|
|
854
|
+
|
|
855
|
+
def _compile_ContinueStatement(self, node):
|
|
856
|
+
"""Compile continue statement
|
|
857
|
+
|
|
858
|
+
Behavior:
|
|
859
|
+
- If inside a loop: Jump to loop start (next iteration).
|
|
860
|
+
- If outside a loop: Enable 'Continue On Error' mode globally.
|
|
861
|
+
"""
|
|
862
|
+
if self.loop_stack:
|
|
863
|
+
# Inside loop: Standard control flow
|
|
864
|
+
self._emit(Opcode.JUMP, self.loop_stack[-1]['continue'])
|
|
865
|
+
else:
|
|
866
|
+
# Outside loop: Enable error resilience
|
|
867
|
+
self._emit(Opcode.ENABLE_ERROR_MODE)
|
|
868
|
+
|
|
869
|
+
def _compile_RequireStatement(self, node):
|
|
870
|
+
"""Compile require(condition, message)"""
|
|
871
|
+
self._compile_node(node.condition)
|
|
872
|
+
|
|
873
|
+
if node.message:
|
|
874
|
+
self._compile_node(node.message)
|
|
875
|
+
else:
|
|
876
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant("Requirement failed"))
|
|
877
|
+
|
|
878
|
+
self._emit(Opcode.REQUIRE)
|
|
879
|
+
|
|
880
|
+
def _compile_RevertStatement(self, node):
|
|
881
|
+
"""Compile revert(reason)"""
|
|
882
|
+
if node.reason:
|
|
883
|
+
self._compile_node(node.reason)
|
|
884
|
+
else:
|
|
885
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant("Transaction reverted"))
|
|
886
|
+
|
|
887
|
+
self._emit(Opcode.TX_REVERT)
|
|
888
|
+
|
|
889
|
+
def _compile_TryCatchStatement(self, node):
|
|
890
|
+
"""Compile try-catch block"""
|
|
891
|
+
catch_label = self._make_label()
|
|
892
|
+
end_label = self._make_label()
|
|
893
|
+
|
|
894
|
+
# SETUP_TRY catch_label
|
|
895
|
+
# (Assuming SETUP_TRY takes a jump target)
|
|
896
|
+
# Note: Opcode must handle jump target resolution.
|
|
897
|
+
# Typically JUMP opcodes use labels. Here we treat SETUP_TRY like a jump-setup.
|
|
898
|
+
self._emit(Opcode.SETUP_TRY, catch_label)
|
|
899
|
+
|
|
900
|
+
self._compile_node(node.try_block)
|
|
901
|
+
|
|
902
|
+
self._emit(Opcode.POP_TRY)
|
|
903
|
+
self._emit(Opcode.JUMP, end_label)
|
|
904
|
+
|
|
905
|
+
# Catch block
|
|
906
|
+
self._mark_label(catch_label)
|
|
907
|
+
|
|
908
|
+
# Exception provided on stack?
|
|
909
|
+
if node.error_variable:
|
|
910
|
+
self._emit(Opcode.STORE_NAME, self._add_constant(node.error_variable.value))
|
|
911
|
+
else:
|
|
912
|
+
self._emit(Opcode.POP)
|
|
913
|
+
|
|
914
|
+
self._compile_node(node.catch_block)
|
|
915
|
+
|
|
916
|
+
self._mark_label(end_label)
|
|
917
|
+
|
|
918
|
+
def _unsupported_message(self, node_type):
|
|
919
|
+
"""Helper for unsupported messages"""
|
|
920
|
+
return f"Node type '{node_type}' is not currently supported by the bytecode compiler."
|
|
921
|
+
|
|
922
|
+
def _compile_AwaitExpression(self, node):
|
|
923
|
+
"""Compile await expression"""
|
|
924
|
+
self._compile_node(node.argument)
|
|
925
|
+
self._emit(Opcode.AWAIT)
|
|
926
|
+
|
|
927
|
+
def _compile_ThisExpression(self, node):
|
|
928
|
+
"""Compile 'this' expression"""
|
|
929
|
+
# Load 'this' from environment (it's passed as implicit argument in methods)
|
|
930
|
+
name_idx = self._add_constant("this")
|
|
931
|
+
self._emit(Opcode.LOAD_NAME, name_idx)
|
|
932
|
+
|
|
933
|
+
def _compile_EntityStatement(self, node):
|
|
934
|
+
"""Compile Entity definition"""
|
|
935
|
+
# Similar to Contract, but uses DEFINE_ENTITY
|
|
936
|
+
entity_name = node.name.value
|
|
937
|
+
name_idx = self._add_constant(entity_name)
|
|
938
|
+
|
|
939
|
+
# Push name
|
|
940
|
+
self._emit(Opcode.LOAD_CONST, name_idx)
|
|
941
|
+
|
|
942
|
+
# Compile body members
|
|
943
|
+
member_count = 0
|
|
944
|
+
if hasattr(node.body, 'statements'):
|
|
945
|
+
for stmt in node.body.statements:
|
|
946
|
+
stmt_type = type(stmt).__name__
|
|
947
|
+
# Reuse state/action compilation
|
|
948
|
+
if stmt_type in ('StateStatement', 'ActionStatement'):
|
|
949
|
+
if stmt_type == 'StateStatement':
|
|
950
|
+
# Value
|
|
951
|
+
if getattr(stmt, 'initial_value', None):
|
|
952
|
+
self._compile_node(stmt.initial_value)
|
|
953
|
+
else:
|
|
954
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
955
|
+
# Name
|
|
956
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(stmt.name.value))
|
|
957
|
+
elif stmt_type == 'ActionStatement':
|
|
958
|
+
self._compile_node(stmt)
|
|
959
|
+
self._emit(Opcode.LOAD_NAME, self._add_constant(stmt.name.value))
|
|
960
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(stmt.name.value))
|
|
961
|
+
|
|
962
|
+
member_count += 1
|
|
963
|
+
|
|
964
|
+
self._emit(Opcode.DEFINE_ENTITY, member_count)
|
|
965
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
966
|
+
|
|
967
|
+
def _compile_DataStatement(self, node):
|
|
968
|
+
"""Compile Data/Dataclass definition"""
|
|
969
|
+
# Data objects are simpler entities
|
|
970
|
+
# For now, map to Entity logic or simple Struct
|
|
971
|
+
# We'll use DEFINE_ENTITY for now to keep it uniform
|
|
972
|
+
|
|
973
|
+
data_name = node.name.value
|
|
974
|
+
name_idx = self._add_constant(data_name)
|
|
975
|
+
self._emit(Opcode.LOAD_CONST, name_idx)
|
|
976
|
+
|
|
977
|
+
member_count = 0
|
|
978
|
+
if node.fields:
|
|
979
|
+
for field in node.fields:
|
|
980
|
+
# Default value
|
|
981
|
+
if field.default_value:
|
|
982
|
+
self._compile_node(field.default_value)
|
|
983
|
+
else:
|
|
984
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
985
|
+
|
|
986
|
+
# Field name
|
|
987
|
+
field_name = field.name.value if hasattr(field.name, 'value') else str(field.name)
|
|
988
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(field_name))
|
|
989
|
+
member_count += 1
|
|
990
|
+
|
|
991
|
+
self._emit(Opcode.DEFINE_ENTITY, member_count)
|
|
992
|
+
self._emit(Opcode.STORE_NAME, name_idx)
|
|
993
|
+
|
|
994
|
+
def _compile_CapabilityStatement(self, node):
|
|
995
|
+
"""Compile capability definition"""
|
|
996
|
+
# Load definition map
|
|
997
|
+
if node.definition:
|
|
998
|
+
self._compile_node(node.definition)
|
|
999
|
+
else:
|
|
1000
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
1001
|
+
|
|
1002
|
+
# Load name
|
|
1003
|
+
name = node.name.value if hasattr(node.name, 'value') else str(node.name)
|
|
1004
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(name))
|
|
1005
|
+
|
|
1006
|
+
self._emit(Opcode.DEFINE_CAPABILITY)
|
|
1007
|
+
|
|
1008
|
+
def _compile_GrantStatement(self, node):
|
|
1009
|
+
"""Compile grant capability"""
|
|
1010
|
+
# Logic: Push Entity (or name), Push Caps... Emit GRANT
|
|
1011
|
+
|
|
1012
|
+
# Push entity name (string)
|
|
1013
|
+
entity = node.entity_name.value if hasattr(node.entity_name, 'value') else str(node.entity_name)
|
|
1014
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(entity))
|
|
1015
|
+
|
|
1016
|
+
# Traverse capabilities
|
|
1017
|
+
count = 0
|
|
1018
|
+
for cap in node.capabilities:
|
|
1019
|
+
cap_name = cap.value if hasattr(cap, 'value') else str(cap)
|
|
1020
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(cap_name))
|
|
1021
|
+
count += 1
|
|
1022
|
+
|
|
1023
|
+
self._emit(Opcode.GRANT_CAPABILITY, count)
|
|
1024
|
+
|
|
1025
|
+
def _compile_RevokeStatement(self, node):
|
|
1026
|
+
"""Compile revoke capability"""
|
|
1027
|
+
# Push entity name
|
|
1028
|
+
entity = node.entity_name.value if hasattr(node.entity_name, 'value') else str(node.entity_name)
|
|
1029
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(entity))
|
|
1030
|
+
|
|
1031
|
+
# Traverse capabilities
|
|
1032
|
+
count = 0
|
|
1033
|
+
for cap in node.capabilities:
|
|
1034
|
+
cap_name = cap.value if hasattr(cap, 'value') else str(cap)
|
|
1035
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(cap_name))
|
|
1036
|
+
count += 1
|
|
1037
|
+
|
|
1038
|
+
self._emit(Opcode.REVOKE_CAPABILITY, count)
|
|
1039
|
+
|
|
1040
|
+
def _compile_AuditStatement(self, node):
|
|
1041
|
+
"""Compile audit log"""
|
|
1042
|
+
# AuditStatement(data_name, action_type, timestamp)
|
|
1043
|
+
|
|
1044
|
+
# Load data name (variable being audited)
|
|
1045
|
+
data_name = node.data_name.value if hasattr(node.data_name, 'value') else str(node.data_name)
|
|
1046
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(data_name))
|
|
1047
|
+
|
|
1048
|
+
# Load action type
|
|
1049
|
+
if hasattr(node.action_type, 'value'):
|
|
1050
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(node.action_type.value))
|
|
1051
|
+
else:
|
|
1052
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(str(node.action_type)))
|
|
1053
|
+
|
|
1054
|
+
# Load timestamp (if any)
|
|
1055
|
+
if node.timestamp:
|
|
1056
|
+
self._compile_node(node.timestamp)
|
|
1057
|
+
else:
|
|
1058
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None))
|
|
1059
|
+
|
|
1060
|
+
self._emit(Opcode.AUDIT_LOG)
|
|
1061
|
+
|
|
1062
|
+
def _compile_RestrictStatement(self, node):
|
|
1063
|
+
"""Compile restrict access"""
|
|
1064
|
+
# RestrictStatement(target, restriction_type)
|
|
1065
|
+
# target is PropertyAccessExpression usually (obj.field) or Identifier
|
|
1066
|
+
|
|
1067
|
+
if hasattr(node.target, 'object'):
|
|
1068
|
+
# Property access: obj.field
|
|
1069
|
+
self._compile_node(node.target.object) # Push object
|
|
1070
|
+
|
|
1071
|
+
# Helper to get property name
|
|
1072
|
+
prop_name = node.target.property.value if hasattr(node.target.property, 'value') else str(node.target.property)
|
|
1073
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(prop_name))
|
|
1074
|
+
else:
|
|
1075
|
+
# Just identifier
|
|
1076
|
+
self._emit(Opcode.LOAD_NAME, self._add_constant(node.target.value))
|
|
1077
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(None)) # No property
|
|
1078
|
+
|
|
1079
|
+
# Load restriction string
|
|
1080
|
+
res_type = node.restriction_type if isinstance(node.restriction_type, str) else str(node.restriction_type)
|
|
1081
|
+
self._emit(Opcode.LOAD_CONST, self._add_constant(res_type))
|
|
1082
|
+
|
|
1083
|
+
self._emit(Opcode.RESTRICT_ACCESS)
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
def _compile_PatternStatement(self, node):
|
|
1087
|
+
"""Compile pattern matching (match statement)"""
|
|
1088
|
+
# Logic:
|
|
1089
|
+
# evaluate value -> store in temp
|
|
1090
|
+
# for each case:
|
|
1091
|
+
# load value
|
|
1092
|
+
# evaluate pattern
|
|
1093
|
+
# check match (simulated EQ)
|
|
1094
|
+
# jump_if_false -> next_case
|
|
1095
|
+
# execute action
|
|
1096
|
+
# jump -> end
|
|
1097
|
+
|
|
1098
|
+
# Evaluate match subject
|
|
1099
|
+
self._compile_node(node.value if hasattr(node, 'value') else node.expression)
|
|
1100
|
+
|
|
1101
|
+
end_label = self._make_label()
|
|
1102
|
+
|
|
1103
|
+
# Iterate cases
|
|
1104
|
+
for case in node.cases:
|
|
1105
|
+
next_case_lbl = self._make_label()
|
|
1106
|
+
|
|
1107
|
+
# Duplicate Subject (Stack: [..., subj, subj])
|
|
1108
|
+
self._emit(Opcode.DUP)
|
|
1109
|
+
|
|
1110
|
+
# Evaluate Pattern
|
|
1111
|
+
# Note: AST might call it 'pattern' or something else depending on Case node type
|
|
1112
|
+
pattern_node = getattr(case, 'pattern', None)
|
|
1113
|
+
if pattern_node:
|
|
1114
|
+
# LiteralPattern holds a literal node in .value
|
|
1115
|
+
if isinstance(pattern_node, zexus_ast.LiteralPattern):
|
|
1116
|
+
self._compile_node(pattern_node.value)
|
|
1117
|
+
# Wildcard/Variable patterns always match; skip pattern compilation
|
|
1118
|
+
elif isinstance(pattern_node, (zexus_ast.WildcardPattern, zexus_ast.VariablePattern)):
|
|
1119
|
+
pass
|
|
1120
|
+
else:
|
|
1121
|
+
self._compile_node(pattern_node)
|
|
1122
|
+
|
|
1123
|
+
# Equality check
|
|
1124
|
+
self._emit(Opcode.EQ)
|
|
1125
|
+
|
|
1126
|
+
# Jump if False
|
|
1127
|
+
self._emit(Opcode.JUMP_IF_FALSE, next_case_lbl)
|
|
1128
|
+
|
|
1129
|
+
# Match!
|
|
1130
|
+
# Pop subject
|
|
1131
|
+
self._emit(Opcode.POP)
|
|
1132
|
+
|
|
1133
|
+
# Execute Action/Consequence
|
|
1134
|
+
# MatchExpression uses 'consequence', PatternStatement uses 'action', MatchCase uses 'result'
|
|
1135
|
+
body = getattr(case, 'consequence', getattr(case, 'action', getattr(case, 'result', None)))
|
|
1136
|
+
self._compile_node(body)
|
|
1137
|
+
self._emit(Opcode.JUMP, end_label)
|
|
1138
|
+
|
|
1139
|
+
# Next Case
|
|
1140
|
+
self._mark_label(next_case_lbl)
|
|
1141
|
+
|
|
1142
|
+
# No match? Pop subject
|
|
1143
|
+
self._emit(Opcode.POP)
|
|
1144
|
+
self._mark_label(end_label)
|
|
1145
|
+
|
|
1146
|
+
# Alias MatchExpression to logic (it's similar enough for basic cases)
|
|
1147
|
+
_compile_MatchExpression = _compile_PatternStatement
|
|
1148
|
+
|
|
380
1149
|
# ==================== Fallback for unsupported nodes ====================
|
|
381
1150
|
|
|
382
1151
|
def __getattr__(self, name):
|
|
@@ -384,10 +1153,8 @@ class BytecodeCompiler:
|
|
|
384
1153
|
if name.startswith('_compile_'):
|
|
385
1154
|
# Return a stub that logs and continues
|
|
386
1155
|
def stub(node):
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
null_idx = self._add_constant(None)
|
|
390
|
-
self._emit(Opcode.LOAD_CONST, null_idx)
|
|
1156
|
+
node_type = name[9:]
|
|
1157
|
+
raise UnsupportedNodeError(node_type, self._unsupported_message(node_type))
|
|
391
1158
|
return stub
|
|
392
1159
|
raise AttributeError(name)
|
|
393
1160
|
|