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
package/src/zexus/vm/compiler.py
CHANGED
|
@@ -54,18 +54,44 @@ class BytecodeCompiler:
|
|
|
54
54
|
|
|
55
55
|
self._compile_node(node)
|
|
56
56
|
|
|
57
|
+
self._resolve_labels()
|
|
58
|
+
|
|
57
59
|
return Bytecode(self.instructions, self.constants)
|
|
58
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
|
+
|
|
59
76
|
def _add_constant(self, value) -> int:
|
|
60
77
|
"""Add constant to pool, return index"""
|
|
61
|
-
# Deduplicate constants
|
|
62
|
-
|
|
63
|
-
|
|
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:
|
|
64
89
|
return self.constant_map[key]
|
|
65
90
|
|
|
66
91
|
idx = len(self.constants)
|
|
67
92
|
self.constants.append(value)
|
|
68
|
-
|
|
93
|
+
if key is not None:
|
|
94
|
+
self.constant_map[key] = idx
|
|
69
95
|
return idx
|
|
70
96
|
|
|
71
97
|
def _emit(self, opcode: Opcode, *args):
|
|
@@ -117,6 +143,7 @@ class BytecodeCompiler:
|
|
|
117
143
|
"""Return a friendly message for unsupported nodes."""
|
|
118
144
|
hints = {
|
|
119
145
|
"UseStatement": "Module imports",
|
|
146
|
+
"FromStatement": "Module imports",
|
|
120
147
|
"EntityStatement": "Entity declarations",
|
|
121
148
|
"ContractStatement": "Contract declarations",
|
|
122
149
|
"ActionStatement": "Action/function declarations",
|
|
@@ -170,7 +197,53 @@ class BytecodeCompiler:
|
|
|
170
197
|
self._emit(Opcode.PRINT)
|
|
171
198
|
|
|
172
199
|
def _compile_LetStatement(self, node):
|
|
173
|
-
"""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
|
|
174
247
|
# Compile value
|
|
175
248
|
self._compile_node(node.value)
|
|
176
249
|
|
|
@@ -224,6 +297,30 @@ class BytecodeCompiler:
|
|
|
224
297
|
"""Load boolean constant"""
|
|
225
298
|
const_idx = self._add_constant(node.value)
|
|
226
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)
|
|
227
324
|
|
|
228
325
|
# ==================== Binary Operations ====================
|
|
229
326
|
|
|
@@ -300,6 +397,29 @@ class BytecodeCompiler:
|
|
|
300
397
|
self._emit(Opcode.NOP)
|
|
301
398
|
self.instructions[-1] = (Opcode.NOP, end_label)
|
|
302
399
|
|
|
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()
|
|
409
|
+
end_label = self._make_label()
|
|
410
|
+
|
|
411
|
+
self._emit(Opcode.JUMP_IF_FALSE, else_label)
|
|
412
|
+
|
|
413
|
+
# True branch
|
|
414
|
+
self._compile_node(node.true_value)
|
|
415
|
+
self._emit(Opcode.JUMP, end_label)
|
|
416
|
+
|
|
417
|
+
# False branch
|
|
418
|
+
self._mark_label(else_label)
|
|
419
|
+
self._compile_node(node.false_value)
|
|
420
|
+
|
|
421
|
+
self._mark_label(end_label)
|
|
422
|
+
|
|
303
423
|
def _compile_WhileStatement(self, node):
|
|
304
424
|
"""Compile while loop"""
|
|
305
425
|
start_label = self._make_label()
|
|
@@ -423,6 +543,8 @@ class BytecodeCompiler:
|
|
|
423
543
|
count = len(node.elements)
|
|
424
544
|
self._emit(Opcode.BUILD_LIST, count)
|
|
425
545
|
|
|
546
|
+
_compile_ListLiteral = _compile_ArrayLiteral
|
|
547
|
+
|
|
426
548
|
def _compile_MapLiteral(self, node):
|
|
427
549
|
"""Compile map/dictionary literal"""
|
|
428
550
|
# Compile key-value pairs
|
|
@@ -447,7 +569,583 @@ class BytecodeCompiler:
|
|
|
447
569
|
self._compile_node(node.left)
|
|
448
570
|
self._compile_node(node.index)
|
|
449
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
|
+
|
|
450
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
|
+
|
|
451
1149
|
# ==================== Fallback for unsupported nodes ====================
|
|
452
1150
|
|
|
453
1151
|
def __getattr__(self, name):
|