zexus 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +2513 -0
- package/bin/zexus +2 -0
- package/bin/zpics +2 -0
- package/bin/zpm +2 -0
- package/bin/zx +2 -0
- package/bin/zx-deploy +2 -0
- package/bin/zx-dev +2 -0
- package/bin/zx-run +2 -0
- package/package.json +66 -0
- package/scripts/README.md +24 -0
- package/scripts/postinstall.js +44 -0
- package/shared_config.json +24 -0
- package/src/README.md +1525 -0
- package/src/tests/run_zexus_tests.py +117 -0
- package/src/tests/test_all_phases.zx +346 -0
- package/src/tests/test_blockchain_features.zx +306 -0
- package/src/tests/test_complexity_features.zx +321 -0
- package/src/tests/test_core_integration.py +185 -0
- package/src/tests/test_phase10_ecosystem.zx +177 -0
- package/src/tests/test_phase1_modifiers.zx +87 -0
- package/src/tests/test_phase2_plugins.zx +80 -0
- package/src/tests/test_phase3_security.zx +97 -0
- package/src/tests/test_phase4_vfs.zx +116 -0
- package/src/tests/test_phase5_types.zx +117 -0
- package/src/tests/test_phase6_metaprogramming.zx +125 -0
- package/src/tests/test_phase7_optimization.zx +132 -0
- package/src/tests/test_phase9_advanced_types.zx +157 -0
- package/src/tests/test_security_features.py +419 -0
- package/src/tests/test_security_features.zx +276 -0
- package/src/tests/test_simple_zx.zx +1 -0
- package/src/tests/test_verification_simple.zx +69 -0
- package/src/zexus/__init__.py +28 -0
- package/src/zexus/__main__.py +5 -0
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/ecosystem.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__/hybrid_orchestrator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/virtual_filesystem.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/advanced_types.py +401 -0
- package/src/zexus/blockchain/__init__.py +40 -0
- package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/crypto.py +463 -0
- package/src/zexus/blockchain/ledger.py +255 -0
- package/src/zexus/blockchain/transaction.py +267 -0
- package/src/zexus/builtin_modules.py +284 -0
- package/src/zexus/builtin_plugins.py +317 -0
- package/src/zexus/capability_system.py +372 -0
- package/src/zexus/cli/__init__.py +2 -0
- package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +707 -0
- package/src/zexus/cli/zpm.py +203 -0
- package/src/zexus/compare_interpreter_compiler.py +146 -0
- package/src/zexus/compiler/__init__.py +169 -0
- package/src/zexus/compiler/__pycache__/__init__.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__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/bytecode.py +266 -0
- package/src/zexus/compiler/compat_runtime.py +277 -0
- package/src/zexus/compiler/lexer.py +257 -0
- package/src/zexus/compiler/parser.py +779 -0
- package/src/zexus/compiler/semantic.py +118 -0
- package/src/zexus/compiler/zexus_ast.py +454 -0
- package/src/zexus/complexity_system.py +575 -0
- package/src/zexus/concurrency_system.py +493 -0
- package/src/zexus/config.py +201 -0
- package/src/zexus/crypto_bridge.py +19 -0
- package/src/zexus/dependency_injection.py +423 -0
- package/src/zexus/ecosystem.py +434 -0
- package/src/zexus/environment.py +101 -0
- package/src/zexus/environment_manager.py +119 -0
- package/src/zexus/error_reporter.py +314 -0
- package/src/zexus/evaluator/__init__.py +12 -0
- package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
- 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__/integration.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +700 -0
- package/src/zexus/evaluator/core.py +891 -0
- package/src/zexus/evaluator/expressions.py +827 -0
- package/src/zexus/evaluator/functions.py +3989 -0
- package/src/zexus/evaluator/integration.py +396 -0
- package/src/zexus/evaluator/statements.py +4303 -0
- package/src/zexus/evaluator/utils.py +126 -0
- package/src/zexus/evaluator_original.py +2041 -0
- package/src/zexus/external_bridge.py +16 -0
- package/src/zexus/find_affected_imports.sh +155 -0
- package/src/zexus/hybrid_orchestrator.py +152 -0
- package/src/zexus/input_validation.py +259 -0
- package/src/zexus/lexer.py +571 -0
- package/src/zexus/logging.py +89 -0
- package/src/zexus/lsp/__init__.py +9 -0
- package/src/zexus/lsp/completion_provider.py +207 -0
- package/src/zexus/lsp/definition_provider.py +22 -0
- package/src/zexus/lsp/hover_provider.py +71 -0
- package/src/zexus/lsp/server.py +269 -0
- package/src/zexus/lsp/symbol_provider.py +31 -0
- package/src/zexus/metaprogramming.py +321 -0
- package/src/zexus/module_cache.py +89 -0
- package/src/zexus/module_manager.py +107 -0
- package/src/zexus/object.py +973 -0
- package/src/zexus/optimization.py +424 -0
- package/src/zexus/parser/__init__.py +31 -0
- package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
- 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/integration.py +86 -0
- package/src/zexus/parser/parser.py +3977 -0
- package/src/zexus/parser/strategy_context.py +7254 -0
- package/src/zexus/parser/strategy_structural.py +1033 -0
- package/src/zexus/persistence.py +391 -0
- package/src/zexus/plugin_system.py +290 -0
- package/src/zexus/policy_engine.py +365 -0
- package/src/zexus/profiler/__init__.py +5 -0
- package/src/zexus/profiler/profiler.py +233 -0
- package/src/zexus/purity_system.py +398 -0
- package/src/zexus/runtime/__init__.py +20 -0
- package/src/zexus/runtime/async_runtime.py +324 -0
- package/src/zexus/search_old_imports.sh +65 -0
- package/src/zexus/security.py +1407 -0
- package/src/zexus/stack_trace.py +233 -0
- package/src/zexus/stdlib/__init__.py +27 -0
- package/src/zexus/stdlib/blockchain.py +341 -0
- package/src/zexus/stdlib/compression.py +167 -0
- package/src/zexus/stdlib/crypto.py +124 -0
- package/src/zexus/stdlib/datetime.py +163 -0
- package/src/zexus/stdlib/db_mongo.py +199 -0
- package/src/zexus/stdlib/db_mysql.py +162 -0
- package/src/zexus/stdlib/db_postgres.py +163 -0
- package/src/zexus/stdlib/db_sqlite.py +133 -0
- package/src/zexus/stdlib/encoding.py +230 -0
- package/src/zexus/stdlib/fs.py +195 -0
- package/src/zexus/stdlib/http.py +219 -0
- package/src/zexus/stdlib/http_server.py +248 -0
- package/src/zexus/stdlib/json_module.py +61 -0
- package/src/zexus/stdlib/math.py +360 -0
- package/src/zexus/stdlib/os_module.py +265 -0
- package/src/zexus/stdlib/regex.py +148 -0
- package/src/zexus/stdlib/sockets.py +253 -0
- package/src/zexus/stdlib/test_framework.zx +208 -0
- package/src/zexus/stdlib/test_runner.zx +119 -0
- package/src/zexus/stdlib_integration.py +341 -0
- package/src/zexus/strategy_recovery.py +256 -0
- package/src/zexus/syntax_validator.py +356 -0
- package/src/zexus/testing/zpics.py +407 -0
- package/src/zexus/testing/zpics_runtime.py +369 -0
- package/src/zexus/type_system.py +374 -0
- package/src/zexus/validation_system.py +569 -0
- package/src/zexus/virtual_filesystem.py +355 -0
- package/src/zexus/vm/__init__.py +8 -0
- package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
- 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__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +420 -0
- package/src/zexus/vm/bytecode.py +428 -0
- package/src/zexus/vm/bytecode_converter.py +297 -0
- package/src/zexus/vm/cache.py +532 -0
- package/src/zexus/vm/jit.py +720 -0
- package/src/zexus/vm/memory_manager.py +520 -0
- package/src/zexus/vm/memory_pool.py +511 -0
- package/src/zexus/vm/optimizer.py +478 -0
- package/src/zexus/vm/parallel_vm.py +899 -0
- package/src/zexus/vm/peephole_optimizer.py +452 -0
- package/src/zexus/vm/profiler.py +527 -0
- package/src/zexus/vm/register_allocator.py +462 -0
- package/src/zexus/vm/register_vm.py +520 -0
- package/src/zexus/vm/ssa_converter.py +757 -0
- package/src/zexus/vm/vm.py +1392 -0
- package/src/zexus/zexus_ast.py +1782 -0
- package/src/zexus/zexus_token.py +253 -0
- package/src/zexus/zpm/__init__.py +15 -0
- package/src/zexus/zpm/installer.py +116 -0
- package/src/zexus/zpm/package_manager.py +208 -0
- package/src/zexus/zpm/publisher.py +98 -0
- package/src/zexus/zpm/registry.py +110 -0
- package/src/zexus.egg-info/PKG-INFO +2235 -0
- package/src/zexus.egg-info/SOURCES.txt +876 -0
- package/src/zexus.egg-info/dependency_links.txt +1 -0
- package/src/zexus.egg-info/entry_points.txt +3 -0
- package/src/zexus.egg-info/not-zip-safe +1 -0
- package/src/zexus.egg-info/requires.txt +14 -0
- package/src/zexus.egg-info/top_level.txt +2 -0
- package/zexus.json +14 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Low-level Bytecode Generator for Zexus compiler frontend.
|
|
3
|
+
|
|
4
|
+
Generates a stack-machine Bytecode object:
|
|
5
|
+
- bytecode.instructions: list of (opcode, operand)
|
|
6
|
+
- bytecode.constants: list of python literals / nested Bytecode function descriptors
|
|
7
|
+
|
|
8
|
+
New opcodes introduced/used:
|
|
9
|
+
- LOAD_CONST (idx) ; push constant
|
|
10
|
+
- LOAD_NAME (idx) ; push env[name] at runtime (name stored in constants)
|
|
11
|
+
- STORE_NAME (idx) ; pop and store into env[name]
|
|
12
|
+
- CALL_NAME (name_idx, arg_count) ; call function by name (env or builtins)
|
|
13
|
+
- CALL_FUNC_CONST (func_const_idx, arg_count) ; call function by constant descriptor
|
|
14
|
+
- RETURN
|
|
15
|
+
- SPAWN_CALL (call_operand) ; spawn a call as task (call_operand is same structure as CALL_*)
|
|
16
|
+
- AWAIT
|
|
17
|
+
- Other control ops: JUMP, JUMP_IF_FALSE, etc.
|
|
18
|
+
|
|
19
|
+
This generator focuses on action/function lowering and call sites.
|
|
20
|
+
"""
|
|
21
|
+
from typing import List, Any, Dict, Tuple
|
|
22
|
+
from .zexus_ast import (
|
|
23
|
+
Program, LetStatement, ExpressionStatement, PrintStatement, ReturnStatement,
|
|
24
|
+
IfStatement, WhileStatement, Identifier, IntegerLiteral, StringLiteral,
|
|
25
|
+
Boolean as AST_Boolean, InfixExpression, PrefixExpression, CallExpression,
|
|
26
|
+
ActionStatement, BlockStatement, MapLiteral, ListLiteral, AwaitExpression
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# --- Bytecode representation ---
|
|
30
|
+
class Bytecode:
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self.instructions: List[Tuple[str, Any]] = []
|
|
33
|
+
self.constants: List[Any] = []
|
|
34
|
+
|
|
35
|
+
def add_instruction(self, opcode: str, operand: Any = None):
|
|
36
|
+
self.instructions.append((opcode, operand))
|
|
37
|
+
|
|
38
|
+
def add_constant(self, value: Any) -> int:
|
|
39
|
+
idx = len(self.constants)
|
|
40
|
+
self.constants.append(value)
|
|
41
|
+
return idx
|
|
42
|
+
|
|
43
|
+
# --- Generator ---
|
|
44
|
+
class BytecodeGenerator:
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.bytecode = Bytecode()
|
|
47
|
+
|
|
48
|
+
def generate(self, program: Program) -> Bytecode:
|
|
49
|
+
self.bytecode = Bytecode()
|
|
50
|
+
for stmt in getattr(program, "statements", []):
|
|
51
|
+
self._emit_statement(stmt, self.bytecode)
|
|
52
|
+
return self.bytecode
|
|
53
|
+
|
|
54
|
+
# Statement lowering
|
|
55
|
+
def _emit_statement(self, stmt, bc: Bytecode):
|
|
56
|
+
t = type(stmt).__name__
|
|
57
|
+
|
|
58
|
+
if t == "LetStatement":
|
|
59
|
+
# Evaluate value -> push result, then STORE_NAME
|
|
60
|
+
self._emit_expression(stmt.value, bc)
|
|
61
|
+
name_idx = bc.add_constant(stmt.name.value)
|
|
62
|
+
bc.add_instruction("STORE_NAME", name_idx)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if t == "ExpressionStatement":
|
|
66
|
+
self._emit_expression(stmt.expression, bc)
|
|
67
|
+
# drop result (no-op) or keep for top-level
|
|
68
|
+
bc.add_instruction("POP", None)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
if t == "PrintStatement":
|
|
72
|
+
self._emit_expression(stmt.value, bc)
|
|
73
|
+
bc.add_instruction("PRINT", None)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
if t == "ReturnStatement":
|
|
77
|
+
self._emit_expression(stmt.return_value, bc)
|
|
78
|
+
bc.add_instruction("RETURN", None)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
if t == "ActionStatement":
|
|
82
|
+
# Compile action body into a nested Bytecode; store as function descriptor constant
|
|
83
|
+
func_bc = Bytecode()
|
|
84
|
+
# compile body: we expect BlockStatement
|
|
85
|
+
for s in getattr(stmt.body, "statements", []):
|
|
86
|
+
self._emit_statement(s, func_bc)
|
|
87
|
+
# ensure function returns (implicit)
|
|
88
|
+
func_bc.add_instruction("RETURN", None)
|
|
89
|
+
# function descriptor: dict with bytecode, params list, is_async flag
|
|
90
|
+
params = [p.value for p in getattr(stmt, "parameters", [])]
|
|
91
|
+
func_desc = {"bytecode": func_bc, "params": params, "is_async": getattr(stmt, "is_async", False)}
|
|
92
|
+
func_const_idx = bc.add_constant(func_desc)
|
|
93
|
+
# store function descriptor into environment under name
|
|
94
|
+
name_idx = bc.add_constant(stmt.name.value)
|
|
95
|
+
# STORE_FUNC: operand (name_idx, func_const_idx)
|
|
96
|
+
bc.add_instruction("STORE_FUNC", (name_idx, func_const_idx))
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
if t == "IfStatement":
|
|
100
|
+
# Very basic lowering: condition, JUMP_IF_FALSE to else/start, consequence, [else], ...
|
|
101
|
+
self._emit_expression(stmt.condition, bc)
|
|
102
|
+
# placeholder jump; compute positions
|
|
103
|
+
jump_pos = len(bc.instructions)
|
|
104
|
+
bc.add_instruction("JUMP_IF_FALSE", None)
|
|
105
|
+
# consequence
|
|
106
|
+
for s in getattr(stmt.consequence, "statements", []):
|
|
107
|
+
self._emit_statement(s, bc)
|
|
108
|
+
# update jump to after consequence
|
|
109
|
+
end_pos = len(bc.instructions)
|
|
110
|
+
bc.instructions[jump_pos] = ("JUMP_IF_FALSE", end_pos)
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
if t == "WhileStatement":
|
|
114
|
+
start_pos = len(bc.instructions)
|
|
115
|
+
self._emit_expression(stmt.condition, bc)
|
|
116
|
+
# placeholder jump
|
|
117
|
+
jump_pos = len(bc.instructions)
|
|
118
|
+
bc.add_instruction("JUMP_IF_FALSE", None)
|
|
119
|
+
# body
|
|
120
|
+
for s in getattr(stmt.body, "statements", []):
|
|
121
|
+
self._emit_statement(s, bc)
|
|
122
|
+
# loop back
|
|
123
|
+
bc.add_instruction("JUMP", start_pos)
|
|
124
|
+
end_pos = len(bc.instructions)
|
|
125
|
+
bc.instructions[jump_pos] = ("JUMP_IF_FALSE", end_pos)
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
# Event/emit/enum/import handled at higher-level generator earlier; treat as NOP here
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Expression lowering
|
|
132
|
+
def _emit_expression(self, expr, bc: Bytecode):
|
|
133
|
+
if expr is None:
|
|
134
|
+
bc.add_instruction("LOAD_CONST", bc.add_constant(None))
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
typ = type(expr).__name__
|
|
138
|
+
|
|
139
|
+
if typ == "IntegerLiteral":
|
|
140
|
+
const_idx = bc.add_constant(expr.value)
|
|
141
|
+
bc.add_instruction("LOAD_CONST", const_idx)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
if typ == "StringLiteral":
|
|
145
|
+
const_idx = bc.add_constant(expr.value)
|
|
146
|
+
bc.add_instruction("LOAD_CONST", const_idx)
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
if typ == "Identifier":
|
|
150
|
+
# push variable value at runtime
|
|
151
|
+
name_idx = bc.add_constant(expr.value)
|
|
152
|
+
bc.add_instruction("LOAD_NAME", name_idx)
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
if typ == "CallExpression":
|
|
156
|
+
# Evaluate arguments first (push in order)
|
|
157
|
+
for arg in expr.arguments:
|
|
158
|
+
self._emit_expression(arg, bc)
|
|
159
|
+
|
|
160
|
+
# If function is an Identifier -> CALL_NAME (by name lookup at runtime)
|
|
161
|
+
if isinstance(expr.function, Identifier):
|
|
162
|
+
name_idx = bc.add_constant(expr.function.value)
|
|
163
|
+
# operand: (name_const_idx, arg_count)
|
|
164
|
+
bc.add_instruction("CALL_NAME", (name_idx, len(expr.arguments)))
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
# If function is a literal function descriptor (constant), emit CALL_FUNC_CONST
|
|
168
|
+
if isinstance(expr.function, ActionLiteral):
|
|
169
|
+
# compile inline action literal into nested func bytecode and store as constant
|
|
170
|
+
# compile nested action body into func_bc
|
|
171
|
+
func_bc = Bytecode()
|
|
172
|
+
# lower the action literal's body statements (best-effort)
|
|
173
|
+
for s in getattr(expr.function.body, "statements", []):
|
|
174
|
+
self._emit_statement(s, func_bc)
|
|
175
|
+
func_bc.add_instruction("RETURN", None)
|
|
176
|
+
func_desc = {"bytecode": func_bc, "params": [p.value for p in expr.function.parameters], "is_async": getattr(expr.function, "is_async", False)}
|
|
177
|
+
func_const_idx = bc.add_constant(func_desc)
|
|
178
|
+
bc.add_instruction("CALL_FUNC_CONST", (func_const_idx, len(expr.arguments)))
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
# Otherwise function expression evaluated to value on stack, call with CALL_TOP
|
|
182
|
+
self._emit_expression(expr.function, bc)
|
|
183
|
+
bc.add_instruction("CALL_TOP", len(expr.arguments))
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
# NEW: AwaitExpression lowering to CALL_* followed by AWAIT
|
|
187
|
+
if typ == "AwaitExpression":
|
|
188
|
+
inner = expr.expression
|
|
189
|
+
# If inner is call by name, emit CALL_NAME then AWAIT
|
|
190
|
+
if isinstance(inner, CallExpression) and isinstance(inner.function, Identifier):
|
|
191
|
+
for arg in inner.arguments:
|
|
192
|
+
self._emit_expression(arg, bc)
|
|
193
|
+
name_idx = bc.add_constant(inner.function.value)
|
|
194
|
+
bc.add_instruction("CALL_NAME", (name_idx, len(inner.arguments)))
|
|
195
|
+
bc.add_instruction("AWAIT", None)
|
|
196
|
+
return
|
|
197
|
+
# If inner is call to function expression, evaluate function then CALL_TOP then AWAIT
|
|
198
|
+
if isinstance(inner, CallExpression):
|
|
199
|
+
for arg in inner.arguments:
|
|
200
|
+
self._emit_expression(arg, bc)
|
|
201
|
+
self._emit_expression(inner.function, bc)
|
|
202
|
+
bc.add_instruction("CALL_TOP", len(inner.arguments))
|
|
203
|
+
bc.add_instruction("AWAIT", None)
|
|
204
|
+
return
|
|
205
|
+
# generic: emit inner then AWAIT
|
|
206
|
+
self._emit_expression(inner, bc)
|
|
207
|
+
bc.add_instruction("AWAIT", None)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
if typ == "InfixExpression":
|
|
211
|
+
# Evaluate left then right then op
|
|
212
|
+
self._emit_expression(expr.left, bc)
|
|
213
|
+
self._emit_expression(expr.right, bc)
|
|
214
|
+
op_map = {
|
|
215
|
+
'+': 'ADD', '-': 'SUB', '*': 'MUL', '/': 'DIV',
|
|
216
|
+
'==': 'EQ', '!=': 'NEQ', '<': 'LT', '>': 'GT',
|
|
217
|
+
'<=': 'LTE', '>=': 'GTE', '&&': 'AND', '||': 'OR'
|
|
218
|
+
}
|
|
219
|
+
bc.add_instruction(op_map.get(expr.operator, "UNKNOWN_OP"), None)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
if typ == "PrefixExpression":
|
|
223
|
+
self._emit_expression(expr.right, bc)
|
|
224
|
+
if expr.operator == "!":
|
|
225
|
+
bc.add_instruction("NOT", None)
|
|
226
|
+
elif expr.operator == "-":
|
|
227
|
+
bc.add_instruction("NEG", None)
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
if typ == "ListLiteral":
|
|
231
|
+
# emit each element and then BUILD_LIST with count
|
|
232
|
+
for el in expr.elements:
|
|
233
|
+
self._emit_expression(el, bc)
|
|
234
|
+
bc.add_instruction("BUILD_LIST", len(expr.elements))
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
if typ == "MapLiteral":
|
|
238
|
+
# emit k,v pairs as literals (best-effort)
|
|
239
|
+
items = {}
|
|
240
|
+
for k_expr, v_expr in expr.pairs:
|
|
241
|
+
# assume keys are string or identifier
|
|
242
|
+
if isinstance(k_expr, StringLiteral):
|
|
243
|
+
key = k_expr.value
|
|
244
|
+
elif isinstance(k_expr, Identifier):
|
|
245
|
+
key = k_expr.value
|
|
246
|
+
else:
|
|
247
|
+
key = str(k_expr)
|
|
248
|
+
# lower value to constant if possible
|
|
249
|
+
if isinstance(v_expr, (StringLiteral, IntegerLiteral)):
|
|
250
|
+
items[key] = v_expr.value
|
|
251
|
+
else:
|
|
252
|
+
# fallback: emit value and store under a temp constant (not ideal)
|
|
253
|
+
self._emit_expression(v_expr, bc)
|
|
254
|
+
# pop and store into a constant slot? Simplify: mark as None
|
|
255
|
+
items[key] = None
|
|
256
|
+
const_idx = bc.add_constant(items)
|
|
257
|
+
bc.add_instruction("LOAD_CONST", const_idx)
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
# fallback: push None
|
|
261
|
+
const_idx = bc.add_constant(None)
|
|
262
|
+
bc.add_instruction("LOAD_CONST", const_idx)
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
# Backwards compatibility
|
|
266
|
+
Generator = BytecodeGenerator
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Compatibility shim: expose a small set of runtime utilities that the
|
|
2
|
+
interpreter provides. This is a best-effort adapter: when the interpreter
|
|
3
|
+
is importable we re-export real implementations; otherwise we provide
|
|
4
|
+
safe, minimal fallbacks to avoid hard import errors in tooling.
|
|
5
|
+
|
|
6
|
+
This file intentionally keeps fallbacks minimal and side-effect free.
|
|
7
|
+
"""
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
# UPDATED: Import from new evaluator structure
|
|
12
|
+
from ..evaluator.utils import (
|
|
13
|
+
EVAL_SUMMARY, NULL, TRUE, FALSE,
|
|
14
|
+
_is_awaitable, _resolve_awaitable, _python_to_zexus, _zexus_to_python,
|
|
15
|
+
_to_python, _to_str, is_error, is_truthy, debug_log
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from ..evaluator import evaluate
|
|
19
|
+
from ..evaluator.functions import FunctionEvaluatorMixin
|
|
20
|
+
from ..object import EvaluationError
|
|
21
|
+
|
|
22
|
+
# Get builtins from FunctionEvaluatorMixin
|
|
23
|
+
fe = FunctionEvaluatorMixin()
|
|
24
|
+
fe.__init__()
|
|
25
|
+
BUILTINS = fe.builtins
|
|
26
|
+
|
|
27
|
+
# Create an Evaluator instance to access methods
|
|
28
|
+
from ..evaluator.core import Evaluator
|
|
29
|
+
evaluator = Evaluator()
|
|
30
|
+
|
|
31
|
+
# Alias evaluator methods
|
|
32
|
+
eval_program = evaluator.eval_program
|
|
33
|
+
eval_block_statement = evaluator.eval_block_statement
|
|
34
|
+
eval_expressions = evaluator.eval_expressions
|
|
35
|
+
eval_identifier = evaluator.eval_identifier
|
|
36
|
+
eval_if_expression = evaluator.eval_if_expression
|
|
37
|
+
eval_infix_expression = evaluator.eval_infix_expression
|
|
38
|
+
eval_prefix_expression = evaluator.eval_prefix_expression
|
|
39
|
+
eval_assignment_expression = evaluator.eval_assignment_expression
|
|
40
|
+
eval_let_statement_fixed = evaluator.eval_let_statement
|
|
41
|
+
eval_try_catch_statement_fixed = evaluator.eval_try_catch_statement
|
|
42
|
+
apply_function = evaluator.apply_function
|
|
43
|
+
array_map = evaluator._array_map
|
|
44
|
+
array_filter = evaluator._array_filter
|
|
45
|
+
array_reduce = evaluator._array_reduce
|
|
46
|
+
extend_function_env = evaluator.extend_function_env
|
|
47
|
+
unwrap_return_value = evaluator.unwrap_return_value
|
|
48
|
+
|
|
49
|
+
# Builtin functions are already in BUILTINS, but we can create aliases
|
|
50
|
+
# Get them from the evaluator's builtins
|
|
51
|
+
builtin_datetime_now = BUILTINS.get("now", lambda *a, **k: None)
|
|
52
|
+
builtin_timestamp = BUILTINS.get("timestamp", lambda *a, **k: None)
|
|
53
|
+
builtin_math_random = BUILTINS.get("random", lambda *a, **k: None)
|
|
54
|
+
builtin_to_hex = BUILTINS.get("to_hex", lambda *a, **k: None)
|
|
55
|
+
builtin_from_hex = BUILTINS.get("from_hex", lambda *a, **k: None)
|
|
56
|
+
builtin_sqrt = BUILTINS.get("sqrt", lambda *a, **k: None)
|
|
57
|
+
builtin_file_read_text = BUILTINS.get("file_read_text", lambda *a, **k: None)
|
|
58
|
+
builtin_file_write_text = BUILTINS.get("file_write_text", lambda *a, **k: None)
|
|
59
|
+
builtin_file_exists = BUILTINS.get("file_exists", lambda *a, **k: None)
|
|
60
|
+
builtin_file_read_json = BUILTINS.get("file_read_json", lambda *a, **k: None)
|
|
61
|
+
builtin_file_write_json = BUILTINS.get("file_write_json", lambda *a, **k: None)
|
|
62
|
+
builtin_file_append = BUILTINS.get("file_append", lambda *a, **k: None)
|
|
63
|
+
builtin_file_list_dir = BUILTINS.get("file_list_dir", lambda *a, **k: None)
|
|
64
|
+
builtin_debug_log = BUILTINS.get("debug_log", lambda *a, **k: None)
|
|
65
|
+
builtin_debug_trace = BUILTINS.get("debug_trace", lambda *a, **k: None)
|
|
66
|
+
builtin_string = BUILTINS.get("string", lambda *a, **k: None)
|
|
67
|
+
builtin_len = BUILTINS.get("len", lambda *a, **k: None)
|
|
68
|
+
builtin_first = BUILTINS.get("first", lambda *a, **k: None)
|
|
69
|
+
builtin_rest = BUILTINS.get("rest", lambda *a, **k: None)
|
|
70
|
+
builtin_push = BUILTINS.get("push", lambda *a, **k: None)
|
|
71
|
+
builtin_reduce = BUILTINS.get("reduce", lambda *a, **k: None)
|
|
72
|
+
builtin_map = BUILTINS.get("map", lambda *a, **k: None)
|
|
73
|
+
builtin_filter = BUILTINS.get("filter", lambda *a, **k: None)
|
|
74
|
+
|
|
75
|
+
# Render registry is now instance-specific in FunctionEvaluatorMixin
|
|
76
|
+
RENDER_REGISTRY = evaluator.render_registry if hasattr(evaluator, 'render_registry') else {
|
|
77
|
+
'screens': {}, 'components': {}, 'themes': {}, 'canvases': {}, 'current_theme': None
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Import permission check (if available)
|
|
81
|
+
try:
|
|
82
|
+
from ..evaluator.statements import StatementEvaluatorMixin
|
|
83
|
+
stmt_evaluator = StatementEvaluatorMixin()
|
|
84
|
+
check_import_permission = stmt_evaluator._check_import_permission
|
|
85
|
+
except Exception:
|
|
86
|
+
def check_import_permission(exported_value, importer_file, env):
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"⚠️ Could not import from new evaluator structure: {e}")
|
|
91
|
+
# Fallbacks (keep existing fallback code)
|
|
92
|
+
class EvaluationError(Exception):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
EVAL_SUMMARY = {}
|
|
96
|
+
NULL = None
|
|
97
|
+
TRUE = True
|
|
98
|
+
FALSE = False
|
|
99
|
+
|
|
100
|
+
def _is_awaitable(obj: Any) -> bool:
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def _resolve_awaitable(obj: Any) -> Any:
|
|
104
|
+
return obj
|
|
105
|
+
|
|
106
|
+
# Try to reuse conversion helpers from object.py if available
|
|
107
|
+
try:
|
|
108
|
+
from ..object import File as _File
|
|
109
|
+
|
|
110
|
+
def _python_to_zexus(value: Any) -> Any:
|
|
111
|
+
try:
|
|
112
|
+
return _File._python_to_zexus(value)
|
|
113
|
+
except Exception:
|
|
114
|
+
return value
|
|
115
|
+
|
|
116
|
+
def _zexus_to_python(value: Any) -> Any:
|
|
117
|
+
try:
|
|
118
|
+
return _File._zexus_to_python(value)
|
|
119
|
+
except Exception:
|
|
120
|
+
return value
|
|
121
|
+
|
|
122
|
+
except Exception:
|
|
123
|
+
def _python_to_zexus(value: Any) -> Any:
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
def _zexus_to_python(value: Any) -> Any:
|
|
127
|
+
return value
|
|
128
|
+
|
|
129
|
+
def _to_python(value: Any) -> Any:
|
|
130
|
+
return value
|
|
131
|
+
|
|
132
|
+
def _to_str(obj: Any) -> str:
|
|
133
|
+
return str(obj)
|
|
134
|
+
|
|
135
|
+
# Minimal evaluation helper fallbacks
|
|
136
|
+
def is_error(obj: Any) -> bool:
|
|
137
|
+
return isinstance(obj, EvaluationError)
|
|
138
|
+
|
|
139
|
+
def is_truthy(obj: Any) -> bool:
|
|
140
|
+
return bool(obj) and obj is not NULL
|
|
141
|
+
|
|
142
|
+
# UPDATED: eval_node now points to evaluate
|
|
143
|
+
def eval_node(node, env, *a, **kw):
|
|
144
|
+
raise EvaluationError("eval_node: interpreter runtime not available")
|
|
145
|
+
|
|
146
|
+
def evaluate(program, env, debug_mode=False):
|
|
147
|
+
raise EvaluationError("evaluate: interpreter runtime not available")
|
|
148
|
+
|
|
149
|
+
def eval_program(statements, env):
|
|
150
|
+
raise EvaluationError("eval_program: interpreter runtime not available")
|
|
151
|
+
|
|
152
|
+
def eval_block_statement(block, env):
|
|
153
|
+
raise EvaluationError("eval_block_statement: interpreter runtime not available")
|
|
154
|
+
|
|
155
|
+
def eval_expressions(expressions, env):
|
|
156
|
+
raise EvaluationError("eval_expressions: interpreter runtime not available")
|
|
157
|
+
|
|
158
|
+
def eval_identifier(node, env):
|
|
159
|
+
raise EvaluationError("eval_identifier: interpreter runtime not available")
|
|
160
|
+
|
|
161
|
+
def eval_if_expression(node, env):
|
|
162
|
+
raise EvaluationError("eval_if_expression: interpreter runtime not available")
|
|
163
|
+
|
|
164
|
+
def eval_infix_expression(op, left, right):
|
|
165
|
+
raise EvaluationError("eval_infix_expression: interpreter runtime not available")
|
|
166
|
+
|
|
167
|
+
def eval_prefix_expression(op, right):
|
|
168
|
+
raise EvaluationError("eval_prefix_expression: interpreter runtime not available")
|
|
169
|
+
|
|
170
|
+
def eval_assignment_expression(node, env):
|
|
171
|
+
raise EvaluationError("eval_assignment_expression: interpreter runtime not available")
|
|
172
|
+
|
|
173
|
+
def eval_let_statement_fixed(node, env, *a, **kw):
|
|
174
|
+
raise EvaluationError("eval_let_statement_fixed: interpreter runtime not available")
|
|
175
|
+
|
|
176
|
+
def eval_try_catch_statement_fixed(node, env, *a, **kw):
|
|
177
|
+
raise EvaluationError("eval_try_catch_statement_fixed: interpreter runtime not available")
|
|
178
|
+
|
|
179
|
+
def extend_function_env(fn, args):
|
|
180
|
+
raise EvaluationError("extend_function_env: interpreter runtime not available")
|
|
181
|
+
|
|
182
|
+
def unwrap_return_value(obj):
|
|
183
|
+
return obj
|
|
184
|
+
|
|
185
|
+
# Minimal renderer fallback
|
|
186
|
+
RENDER_REGISTRY = {'screens': {}, 'components': {}, 'themes': {}, 'canvases': {}, 'current_theme': None}
|
|
187
|
+
|
|
188
|
+
# Try to create small wrappers for builtin functions by reading from object.File when present
|
|
189
|
+
try:
|
|
190
|
+
from ..object import File as _File
|
|
191
|
+
|
|
192
|
+
def _wrap_file_read_text(path):
|
|
193
|
+
return _File.read_text(path)
|
|
194
|
+
|
|
195
|
+
def _wrap_file_write_text(path, content):
|
|
196
|
+
return _File.write_text(path, content)
|
|
197
|
+
|
|
198
|
+
def _wrap_file_exists(path):
|
|
199
|
+
return _File.exists(path)
|
|
200
|
+
|
|
201
|
+
def _wrap_file_read_json(path):
|
|
202
|
+
return _File.read_json(path)
|
|
203
|
+
|
|
204
|
+
def _wrap_file_write_json(path, data):
|
|
205
|
+
return _File.write_json(path, data)
|
|
206
|
+
|
|
207
|
+
def _wrap_file_append(path, content):
|
|
208
|
+
return _File.append_text(path, content)
|
|
209
|
+
|
|
210
|
+
def _wrap_file_list_dir(path):
|
|
211
|
+
return _File.list_directory(path)
|
|
212
|
+
|
|
213
|
+
except Exception:
|
|
214
|
+
_wrap_file_read_text = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file read not available'))
|
|
215
|
+
_wrap_file_write_text = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file write not available'))
|
|
216
|
+
_wrap_file_exists = lambda *a, **k: False
|
|
217
|
+
_wrap_file_read_json = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file read json not available'))
|
|
218
|
+
_wrap_file_write_json = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file write json not available'))
|
|
219
|
+
_wrap_file_append = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file append not available'))
|
|
220
|
+
_wrap_file_list_dir = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('file list not available'))
|
|
221
|
+
|
|
222
|
+
# Expose builtin_* names as top-level names (fallback wrappers)
|
|
223
|
+
builtin_file_read_text = _wrap_file_read_text
|
|
224
|
+
builtin_file_write_text = _wrap_file_write_text
|
|
225
|
+
builtin_file_exists = _wrap_file_exists
|
|
226
|
+
builtin_file_read_json = _wrap_file_read_json
|
|
227
|
+
builtin_file_write_json = _wrap_file_write_json
|
|
228
|
+
builtin_file_append = _wrap_file_append
|
|
229
|
+
builtin_file_list_dir = _wrap_file_list_dir
|
|
230
|
+
|
|
231
|
+
# Other builtin placeholders
|
|
232
|
+
builtin_datetime_now = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('datetime not available'))
|
|
233
|
+
builtin_timestamp = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('timestamp not available'))
|
|
234
|
+
builtin_math_random = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('math random not available'))
|
|
235
|
+
builtin_to_hex = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('to_hex not available'))
|
|
236
|
+
builtin_from_hex = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('from_hex not available'))
|
|
237
|
+
builtin_sqrt = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('sqrt not available'))
|
|
238
|
+
builtin_debug_log = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('debug_log not available'))
|
|
239
|
+
builtin_debug_trace = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('debug_trace not available'))
|
|
240
|
+
builtin_string = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('string not available'))
|
|
241
|
+
builtin_len = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('len not available'))
|
|
242
|
+
builtin_first = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('first not available'))
|
|
243
|
+
builtin_rest = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('rest not available'))
|
|
244
|
+
builtin_push = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('push not available'))
|
|
245
|
+
builtin_reduce = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('reduce not available'))
|
|
246
|
+
builtin_map = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('map not available'))
|
|
247
|
+
builtin_filter = lambda *a, **k: (_ for _ in ()).throw(EvaluationError('filter not available'))
|
|
248
|
+
|
|
249
|
+
def apply_function(fn, args):
|
|
250
|
+
raise EvaluationError("apply_function: runtime not available")
|
|
251
|
+
|
|
252
|
+
def array_map(array_obj, lambda_fn, env=None):
|
|
253
|
+
raise EvaluationError("array_map: runtime not available")
|
|
254
|
+
|
|
255
|
+
def array_filter(array_obj, lambda_fn, env=None):
|
|
256
|
+
raise EvaluationError("array_filter: runtime not available")
|
|
257
|
+
|
|
258
|
+
def array_reduce(array_obj, lambda_fn, initial_value=None, env=None):
|
|
259
|
+
raise EvaluationError("array_reduce: runtime not available")
|
|
260
|
+
|
|
261
|
+
BUILTINS = {}
|
|
262
|
+
|
|
263
|
+
def check_import_permission(exported_value, importer_file, env):
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
# Note: eval_node is now an alias for evaluate in the new structure
|
|
267
|
+
# For compatibility, we'll keep eval_node as a function that calls evaluate
|
|
268
|
+
def eval_node(node, env, *args, **kwargs):
|
|
269
|
+
"""Compatibility wrapper: eval_node now calls evaluate"""
|
|
270
|
+
return evaluate(node, env, *args, **kwargs)
|
|
271
|
+
|
|
272
|
+
__all__ = [
|
|
273
|
+
'EVAL_SUMMARY', 'EvaluationError', 'NULL', 'TRUE', 'FALSE',
|
|
274
|
+
'_is_awaitable', '_resolve_awaitable', '_python_to_zexus', '_zexus_to_python',
|
|
275
|
+
'_to_python', '_to_str', 'apply_function', 'array_map', 'array_filter', 'array_reduce',
|
|
276
|
+
'BUILTINS', 'check_import_permission', 'evaluate', 'eval_node' # UPDATED
|
|
277
|
+
]
|