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
|
@@ -28,6 +28,8 @@ class StructuralAnalyzer:
|
|
|
28
28
|
block_id = 0
|
|
29
29
|
n = len(tokens)
|
|
30
30
|
|
|
31
|
+
debug_enabled = zexus_config.enable_debug_logs
|
|
32
|
+
|
|
31
33
|
# helper sets for stopping heuristics (mirrors context parser)
|
|
32
34
|
stop_types = {SEMICOLON, RBRACE}
|
|
33
35
|
|
|
@@ -37,11 +39,11 @@ class StructuralAnalyzer:
|
|
|
37
39
|
# Statement starters (keywords that begin a new statement)
|
|
38
40
|
# NOTE: SEND and RECEIVE removed - they can be used as function calls in expressions
|
|
39
41
|
statement_starters = {
|
|
40
|
-
LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, EXTERNAL,
|
|
42
|
+
LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, FINALLY, EXTERNAL,
|
|
41
43
|
SCREEN, COLOR, CANVAS, GRAPHICS, ANIMATION, CLOCK,
|
|
42
44
|
EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, PERSISTENT, AUDIT,
|
|
43
45
|
RESTRICT, SANDBOX, TRAIL, GC, BUFFER, SIMD,
|
|
44
|
-
DEFER, PATTERN, ENUM, STREAM, WATCH,
|
|
46
|
+
DEFER, PATTERN, ENUM, STREAM, WATCH, MATCH,
|
|
45
47
|
CAPABILITY, GRANT, REVOKE, VALIDATE, SANITIZE, IMMUTABLE,
|
|
46
48
|
INTERFACE, TYPE_ALIAS, MODULE, PACKAGE, USING,
|
|
47
49
|
CHANNEL, ATOMIC,
|
|
@@ -135,13 +137,44 @@ class StructuralAnalyzer:
|
|
|
135
137
|
declaration_type = tokens[i].type
|
|
136
138
|
export_tokens.append(tokens[i])
|
|
137
139
|
i += 1
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
|
|
141
|
+
# Track nesting counts so that braces/parentheses inside the value don't prematurely terminate the statement
|
|
142
|
+
paren_depth = 0
|
|
143
|
+
brace_depth = 0
|
|
144
|
+
bracket_depth = 0
|
|
145
|
+
|
|
146
|
+
while i < n:
|
|
147
|
+
current = tokens[i]
|
|
148
|
+
|
|
149
|
+
# Update nesting counters before applying boundary heuristics
|
|
150
|
+
if current.type == LPAREN:
|
|
151
|
+
paren_depth += 1
|
|
152
|
+
elif current.type == RPAREN:
|
|
153
|
+
paren_depth = max(paren_depth - 1, 0)
|
|
154
|
+
elif current.type == LBRACE:
|
|
155
|
+
brace_depth += 1
|
|
156
|
+
elif current.type == RBRACE:
|
|
157
|
+
brace_depth = max(brace_depth - 1, 0)
|
|
158
|
+
elif current.type == LBRACKET:
|
|
159
|
+
bracket_depth += 1
|
|
160
|
+
elif current.type == RBRACKET:
|
|
161
|
+
bracket_depth = max(bracket_depth - 1, 0)
|
|
162
|
+
|
|
163
|
+
export_tokens.append(current)
|
|
144
164
|
i += 1
|
|
165
|
+
|
|
166
|
+
in_nested_structure = (paren_depth > 0 or brace_depth > 0 or bracket_depth > 0)
|
|
167
|
+
|
|
168
|
+
# Stop if we reach a clear boundary at top level (first semicolon or closing brace outside of literal)
|
|
169
|
+
if not in_nested_structure and current.type in stop_types:
|
|
170
|
+
break
|
|
171
|
+
|
|
172
|
+
# Stop if we see another statement starter at top level (but not CONST/LET since we just saw it)
|
|
173
|
+
if not in_nested_structure and current.type in statement_starters and current.type not in [CONST, LET]:
|
|
174
|
+
# Remove the boundary token so the next statement sees it
|
|
175
|
+
export_tokens.pop()
|
|
176
|
+
i -= 1
|
|
177
|
+
break
|
|
145
178
|
elif i < n and tokens[i].type == CONTRACT:
|
|
146
179
|
# export contract Name { ... } - collect entire contract
|
|
147
180
|
export_tokens.append(tokens[i])
|
|
@@ -171,6 +204,30 @@ class StructuralAnalyzer:
|
|
|
171
204
|
elif tokens[i].type == RBRACE:
|
|
172
205
|
brace_count -= 1
|
|
173
206
|
i += 1
|
|
207
|
+
elif i < n and tokens[i].type == DATA:
|
|
208
|
+
# export data Name { ... } - collect entire data declaration
|
|
209
|
+
export_tokens.append(tokens[i])
|
|
210
|
+
i += 1
|
|
211
|
+
|
|
212
|
+
brace_count = 0
|
|
213
|
+
|
|
214
|
+
# Collect modifiers, type params, and until first brace
|
|
215
|
+
while i < n:
|
|
216
|
+
export_tokens.append(tokens[i])
|
|
217
|
+
if tokens[i].type == LBRACE:
|
|
218
|
+
brace_count = 1
|
|
219
|
+
i += 1
|
|
220
|
+
break
|
|
221
|
+
i += 1
|
|
222
|
+
|
|
223
|
+
# Collect body until matching closing brace
|
|
224
|
+
while i < n and brace_count > 0:
|
|
225
|
+
export_tokens.append(tokens[i])
|
|
226
|
+
if tokens[i].type == LBRACE:
|
|
227
|
+
brace_count += 1
|
|
228
|
+
elif tokens[i].type == RBRACE:
|
|
229
|
+
brace_count -= 1
|
|
230
|
+
i += 1
|
|
174
231
|
elif i < n and tokens[i].type == FUNCTION:
|
|
175
232
|
# export function name(...) { ... } - collect entire function
|
|
176
233
|
export_tokens.append(tokens[i])
|
|
@@ -451,8 +508,16 @@ class StructuralAnalyzer:
|
|
|
451
508
|
catch_tokens = [catch_token] + pre_brace_tokens + catch_block_tokens
|
|
452
509
|
final_idx = after_catch_idx
|
|
453
510
|
|
|
511
|
+
# Check for finally block
|
|
512
|
+
finally_tokens = []
|
|
513
|
+
if final_idx < n and tokens[final_idx].type == FINALLY:
|
|
514
|
+
finally_token = tokens[final_idx]
|
|
515
|
+
finally_block_tokens, after_finally_idx = self._collect_brace_block(tokens, final_idx + 1)
|
|
516
|
+
finally_tokens = [finally_token] + finally_block_tokens
|
|
517
|
+
final_idx = after_finally_idx
|
|
518
|
+
|
|
454
519
|
# Combine all tokens
|
|
455
|
-
full_tokens = [t] + try_block_tokens + catch_tokens
|
|
520
|
+
full_tokens = [t] + try_block_tokens + catch_tokens + finally_tokens
|
|
456
521
|
full_tokens = [tk for tk in full_tokens if not _is_empty_token(tk)]
|
|
457
522
|
|
|
458
523
|
# Create the main try-catch block
|
|
@@ -744,7 +809,27 @@ class StructuralAnalyzer:
|
|
|
744
809
|
# Break if we've already completed the LET/CONST and this is an indexed assignment
|
|
745
810
|
if is_indexed_assignment and in_assignment and seen_assign:
|
|
746
811
|
break
|
|
747
|
-
|
|
812
|
+
|
|
813
|
+
# Pattern 4: IDENT followed by LPAREN is a function call expression
|
|
814
|
+
# This is a NEW statement if we're in LET/CONST and have seen the main assign
|
|
815
|
+
# and the function call is on a new line (prevents absorbing next statement)
|
|
816
|
+
elif tj.type == IDENT and j + 1 < n and tokens[j + 1].type == LPAREN:
|
|
817
|
+
if stmt_tokens and in_assignment and seen_assign:
|
|
818
|
+
last_token = stmt_tokens[-1]
|
|
819
|
+
last_line = getattr(last_token, 'line', None)
|
|
820
|
+
current_line = getattr(tj, 'line', None)
|
|
821
|
+
if last_line is not None and current_line is not None and current_line > last_line:
|
|
822
|
+
# Safety: don't break if previous token is an infix operator
|
|
823
|
+
# (e.g., let x = foo() +\n bar() across two lines)
|
|
824
|
+
_infix_ops = {PLUS, MINUS, STAR, SLASH, MOD, AND, OR,
|
|
825
|
+
EQ, NOT_EQ, LT, GT, LTE, GTE, POWER,
|
|
826
|
+
DOT, COMMA, ASSIGN, QUESTION, NULLISH,
|
|
827
|
+
PLUS_ASSIGN, MINUS_ASSIGN, STAR_ASSIGN,
|
|
828
|
+
SLASH_ASSIGN, MOD_ASSIGN, POWER_ASSIGN,
|
|
829
|
+
APPEND, IMPORT_OP}
|
|
830
|
+
if last_token.type not in _infix_ops:
|
|
831
|
+
# New line after completed assignment - this is a new statement
|
|
832
|
+
break
|
|
748
833
|
|
|
749
834
|
# Detect colon-based block (tolerable syntax for action/function/if/while etc.)
|
|
750
835
|
if tj.type == COLON and nesting == 0 and t.type in {ACTION, FUNCTION, IF, WHILE, FOR}:
|
|
@@ -760,8 +845,10 @@ class StructuralAnalyzer:
|
|
|
760
845
|
# Track nesting level BEFORE dedent check (so we don't break inside {...} or [...] or (...))
|
|
761
846
|
if tj.type in {LPAREN, LBRACE, LBRACKET}:
|
|
762
847
|
# Only mark as brace block if NOT already in colon block (to distinguish code blocks from data literals)
|
|
848
|
+
# ALSO: Don't mark as brace block for destructuring patterns in LET/CONST before '='
|
|
763
849
|
if tj.type == LBRACE and not found_colon_block:
|
|
764
|
-
|
|
850
|
+
if not (in_assignment and not seen_assign):
|
|
851
|
+
found_brace_block = True
|
|
765
852
|
nesting += 1
|
|
766
853
|
elif tj.type in {RPAREN, RBRACE, RBRACKET}:
|
|
767
854
|
nesting -= 1
|
|
@@ -792,10 +879,16 @@ class StructuralAnalyzer:
|
|
|
792
879
|
# We're still parsing the condition - don't break yet
|
|
793
880
|
pass
|
|
794
881
|
else:
|
|
795
|
-
# For LET/CONST, allow FUNCTION, SANDBOX, SANITIZE as RHS (expressions)
|
|
882
|
+
# For LET/CONST, allow FUNCTION, SANDBOX, SANITIZE, MATCH as RHS (expressions)
|
|
796
883
|
# Also allow DEBUG when followed by ( for debug(x) function calls in assignments
|
|
797
884
|
# Also allow IF when followed by THEN (if-then-else expression)
|
|
798
|
-
allow_in_assignment = tj.type in {FUNCTION, SANDBOX, SANITIZE}
|
|
885
|
+
allow_in_assignment = tj.type in {FUNCTION, SANDBOX, SANITIZE, MATCH}
|
|
886
|
+
if in_assignment and allow_in_assignment and stmt_tokens:
|
|
887
|
+
prev_token = stmt_tokens[-1]
|
|
888
|
+
prev_line = getattr(prev_token, 'line', None)
|
|
889
|
+
current_line = getattr(tj, 'line', None)
|
|
890
|
+
if prev_line is not None and current_line is not None and current_line > prev_line:
|
|
891
|
+
allow_in_assignment = False
|
|
799
892
|
allow_debug_call = tj.type == DEBUG and j + 1 < n and tokens[j + 1].type == LPAREN
|
|
800
893
|
allow_if_then_else = False
|
|
801
894
|
if tj.type == IF:
|
|
@@ -965,10 +1058,12 @@ class StructuralAnalyzer:
|
|
|
965
1058
|
# CRITICAL FIX: Don't break if the previous token was ASSIGN
|
|
966
1059
|
# This means the IDENT is the RHS value, not a new statement
|
|
967
1060
|
prev_tok = run_tokens[-1] if run_tokens else None
|
|
968
|
-
|
|
1061
|
+
if debug_enabled:
|
|
1062
|
+
print(f" prev_tok={prev_tok.literal if prev_tok else None}, type={prev_tok.type if prev_tok else None}")
|
|
969
1063
|
if prev_tok and prev_tok.type == ASSIGN:
|
|
970
1064
|
# This IDENT is the RHS of the assignment, not a new statement
|
|
971
|
-
|
|
1065
|
+
if debug_enabled:
|
|
1066
|
+
print(" -> Continuing (RHS of assignment)")
|
|
972
1067
|
pass # Don't break, continue collecting
|
|
973
1068
|
else:
|
|
974
1069
|
# This is likely a new statement on a new line
|
|
@@ -1110,7 +1205,7 @@ class StructuralAnalyzer:
|
|
|
1110
1205
|
stop_types = {SEMICOLON, RBRACE}
|
|
1111
1206
|
# NOTE: SEND and RECEIVE removed - they can be used as function calls in expressions
|
|
1112
1207
|
statement_starters = {
|
|
1113
|
-
LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, EXTERNAL,
|
|
1208
|
+
LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, FINALLY, EXTERNAL,
|
|
1114
1209
|
SCREEN, COLOR, CANVAS, GRAPHICS, ANIMATION, CLOCK,
|
|
1115
1210
|
EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, AUDIT,
|
|
1116
1211
|
RESTRICT, SANDBOX, TRAIL, NATIVE, GC, INLINE, BUFFER, SIMD,
|
|
@@ -1169,7 +1264,8 @@ class StructuralAnalyzer:
|
|
|
1169
1264
|
|
|
1170
1265
|
# Export statement detection - collect export keyword + following declaration
|
|
1171
1266
|
if t.type == EXPORT:
|
|
1172
|
-
|
|
1267
|
+
if debug_enabled:
|
|
1268
|
+
print(f"[DEBUG STRUCT] Found EXPORT token at index {i}")
|
|
1173
1269
|
if cur:
|
|
1174
1270
|
results.append(cur)
|
|
1175
1271
|
cur = []
|
|
@@ -1179,9 +1275,11 @@ class StructuralAnalyzer:
|
|
|
1179
1275
|
i += 1
|
|
1180
1276
|
|
|
1181
1277
|
# Check if followed by const/let declaration (export const X = value)
|
|
1182
|
-
|
|
1278
|
+
if debug_enabled:
|
|
1279
|
+
print(f"[DEBUG STRUCT] Next token at {i}: {tokens[i].type if i < n else 'EOF'}, CONST={CONST}, LET={LET}")
|
|
1183
1280
|
if i < n and tokens[i].type in [CONST, LET]:
|
|
1184
|
-
|
|
1281
|
+
if debug_enabled:
|
|
1282
|
+
print(f"[DEBUG STRUCT] Export followed by CONST/LET, collecting declaration")
|
|
1185
1283
|
# Collect the entire declaration
|
|
1186
1284
|
while i < n and tokens[i].type not in stop_types:
|
|
1187
1285
|
export_tokens.append(tokens[i])
|
package/src/zexus/persistence.py
CHANGED
|
@@ -315,6 +315,19 @@ class PersistentStorage:
|
|
|
315
315
|
cursor = self.conn.cursor()
|
|
316
316
|
cursor.execute('DELETE FROM variables')
|
|
317
317
|
self.conn.commit()
|
|
318
|
+
|
|
319
|
+
def _key_to_str(self, key):
|
|
320
|
+
"""Convert a Zexus object key to a Python string for JSON serialization."""
|
|
321
|
+
if isinstance(key, String):
|
|
322
|
+
return key.value
|
|
323
|
+
if isinstance(key, Integer):
|
|
324
|
+
return str(key.value)
|
|
325
|
+
if isinstance(key, Float):
|
|
326
|
+
return str(key.value)
|
|
327
|
+
if isinstance(key, BooleanObj):
|
|
328
|
+
return str(key.value)
|
|
329
|
+
# Fallback for other types
|
|
330
|
+
return str(key)
|
|
318
331
|
|
|
319
332
|
def _serialize(self, obj: Object) -> Dict[str, str]:
|
|
320
333
|
"""Serialize Zexus object to JSON"""
|
|
@@ -330,7 +343,8 @@ class PersistentStorage:
|
|
|
330
343
|
serialized = [self._serialize(e) for e in obj.elements]
|
|
331
344
|
return {'type': 'list', 'value': json.dumps(serialized)}
|
|
332
345
|
elif isinstance(obj, Map):
|
|
333
|
-
|
|
346
|
+
# Convert Zexus String keys to Python strings for JSON compatibility
|
|
347
|
+
serialized = {self._key_to_str(k): self._serialize(v) for k, v in obj.pairs.items()}
|
|
334
348
|
return {'type': 'map', 'value': json.dumps(serialized)}
|
|
335
349
|
elif obj is Null or obj is NULL:
|
|
336
350
|
return {'type': 'null', 'value': json.dumps(None)}
|
|
@@ -43,4 +43,19 @@ __all__ = [
|
|
|
43
43
|
"register_graphics",
|
|
44
44
|
"render_screen",
|
|
45
45
|
"set_theme",
|
|
46
|
+
# GUI backends
|
|
47
|
+
"TkBackend",
|
|
48
|
+
"WebBackend",
|
|
49
|
+
"create_tk_backend",
|
|
50
|
+
"create_web_backend",
|
|
46
51
|
]
|
|
52
|
+
|
|
53
|
+
# Lazy imports for GUI backends (tkinter may not be installed)
|
|
54
|
+
def __getattr__(name: str):
|
|
55
|
+
if name in ("TkBackend", "create_tk_backend"):
|
|
56
|
+
from .tk_backend import TkBackend, create_tk_backend
|
|
57
|
+
return TkBackend if name == "TkBackend" else create_tk_backend
|
|
58
|
+
if name in ("WebBackend", "create_web_backend"):
|
|
59
|
+
from .web_backend import WebBackend, create_web_backend
|
|
60
|
+
return WebBackend if name == "WebBackend" else create_web_backend
|
|
61
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tk GUI Backend for the Zexus Renderer.
|
|
3
|
+
|
|
4
|
+
Provides a real windowed GUI using Python's built-in ``tkinter``.
|
|
5
|
+
The backend translates the existing ``Screen`` / ``Canvas`` / ``Component``
|
|
6
|
+
model into Tk widgets.
|
|
7
|
+
|
|
8
|
+
Usage from Zexus::
|
|
9
|
+
|
|
10
|
+
@zexus gui_backend = "tk"
|
|
11
|
+
|
|
12
|
+
screen main {
|
|
13
|
+
title: "My App"
|
|
14
|
+
width: 400
|
|
15
|
+
height: 300
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
component greeting label {
|
|
19
|
+
text: "Hello from Zexus!"
|
|
20
|
+
x: 50
|
|
21
|
+
y: 30
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
add greeting to main
|
|
25
|
+
show main
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import threading
|
|
31
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
import tkinter as tk
|
|
35
|
+
from tkinter import font as tkfont
|
|
36
|
+
TK_AVAILABLE = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
TK_AVAILABLE = False
|
|
39
|
+
|
|
40
|
+
from .layout import Screen, ScreenComponent, ScreenRegistry, Button, Label, TextBox
|
|
41
|
+
from .canvas import Canvas, CanvasRegistry
|
|
42
|
+
from .color_system import RGBColor
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _rgb_hex(color: Optional[RGBColor]) -> str:
|
|
46
|
+
"""Convert ``RGBColor`` to Tk hex string."""
|
|
47
|
+
if color is None:
|
|
48
|
+
return "#000000"
|
|
49
|
+
return f"#{color.r:02x}{color.g:02x}{color.b:02x}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TkBackend:
|
|
53
|
+
"""Windowed GUI backend backed by tkinter.
|
|
54
|
+
|
|
55
|
+
The Tk event loop runs on a **dedicated thread** so the Zexus
|
|
56
|
+
evaluator thread isn't blocked.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, screen_registry: Optional[ScreenRegistry] = None,
|
|
60
|
+
canvas_registry: Optional[CanvasRegistry] = None) -> None:
|
|
61
|
+
if not TK_AVAILABLE:
|
|
62
|
+
raise RuntimeError(
|
|
63
|
+
"tkinter is not available. On Ubuntu: sudo apt install python3-tk"
|
|
64
|
+
)
|
|
65
|
+
self.screen_registry = screen_registry or ScreenRegistry()
|
|
66
|
+
self.canvas_registry = canvas_registry or CanvasRegistry()
|
|
67
|
+
self._root: Optional[tk.Tk] = None
|
|
68
|
+
self._windows: Dict[str, tk.Toplevel] = {}
|
|
69
|
+
self._tk_canvases: Dict[str, tk.Canvas] = {}
|
|
70
|
+
self._event_handlers: Dict[str, Callable] = {}
|
|
71
|
+
self._thread: Optional[threading.Thread] = None
|
|
72
|
+
self._ready = threading.Event()
|
|
73
|
+
|
|
74
|
+
# -- Lifecycle ---------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
def start(self) -> None:
|
|
77
|
+
"""Launch the Tk main loop on a background thread."""
|
|
78
|
+
if self._thread and self._thread.is_alive():
|
|
79
|
+
return
|
|
80
|
+
self._thread = threading.Thread(target=self._run_mainloop, daemon=True)
|
|
81
|
+
self._thread.start()
|
|
82
|
+
self._ready.wait(timeout=5)
|
|
83
|
+
|
|
84
|
+
def stop(self) -> None:
|
|
85
|
+
"""Destroy all windows and shut down the Tk loop."""
|
|
86
|
+
if self._root:
|
|
87
|
+
self._root.after(0, self._root.destroy)
|
|
88
|
+
|
|
89
|
+
def _run_mainloop(self) -> None:
|
|
90
|
+
self._root = tk.Tk()
|
|
91
|
+
self._root.withdraw() # hide root window
|
|
92
|
+
self._ready.set()
|
|
93
|
+
self._root.mainloop()
|
|
94
|
+
|
|
95
|
+
# -- Screen rendering --------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def show_screen(self, name: str) -> None:
|
|
98
|
+
"""Render a registered ``Screen`` as a Tk window."""
|
|
99
|
+
screen = self.screen_registry.get_screen(name)
|
|
100
|
+
width = int(screen.get("width", 400))
|
|
101
|
+
height = int(screen.get("height", 300))
|
|
102
|
+
title = str(screen.get("title", name))
|
|
103
|
+
bg = str(screen.get("background", "#ffffff"))
|
|
104
|
+
|
|
105
|
+
def _create():
|
|
106
|
+
win = tk.Toplevel(self._root)
|
|
107
|
+
win.title(title)
|
|
108
|
+
win.geometry(f"{width}x{height}")
|
|
109
|
+
win.configure(bg=bg)
|
|
110
|
+
self._windows[name] = win
|
|
111
|
+
|
|
112
|
+
for child in screen.children:
|
|
113
|
+
self._render_component(win, child)
|
|
114
|
+
|
|
115
|
+
self._root.after(0, _create)
|
|
116
|
+
|
|
117
|
+
def close_screen(self, name: str) -> None:
|
|
118
|
+
win = self._windows.pop(name, None)
|
|
119
|
+
if win:
|
|
120
|
+
self._root.after(0, win.destroy)
|
|
121
|
+
|
|
122
|
+
# -- Component rendering -----------------------------------------------
|
|
123
|
+
|
|
124
|
+
def _render_component(self, parent: tk.Toplevel, comp: ScreenComponent) -> None:
|
|
125
|
+
x = int(comp.get("x", 10))
|
|
126
|
+
y = int(comp.get("y", 10))
|
|
127
|
+
fg = str(comp.get("color", "black"))
|
|
128
|
+
text = str(comp.get("text", comp.name))
|
|
129
|
+
|
|
130
|
+
if isinstance(comp, Button):
|
|
131
|
+
handler = self._event_handlers.get(f"{comp.name}:click")
|
|
132
|
+
btn = tk.Button(parent, text=text, fg=fg,
|
|
133
|
+
command=handler if handler else lambda: None)
|
|
134
|
+
btn.place(x=x, y=y)
|
|
135
|
+
|
|
136
|
+
elif isinstance(comp, TextBox):
|
|
137
|
+
w = int(comp.get("width", 20))
|
|
138
|
+
entry = tk.Entry(parent, width=w, fg=fg)
|
|
139
|
+
entry.insert(0, text)
|
|
140
|
+
entry.place(x=x, y=y)
|
|
141
|
+
|
|
142
|
+
elif isinstance(comp, Label):
|
|
143
|
+
size = int(comp.get("fontSize", 12))
|
|
144
|
+
lbl = tk.Label(parent, text=text, fg=fg,
|
|
145
|
+
font=("TkDefaultFont", size))
|
|
146
|
+
lbl.place(x=x, y=y)
|
|
147
|
+
|
|
148
|
+
else:
|
|
149
|
+
# Generic component → label
|
|
150
|
+
lbl = tk.Label(parent, text=text)
|
|
151
|
+
lbl.place(x=x, y=y)
|
|
152
|
+
|
|
153
|
+
# -- Canvas rendering --------------------------------------------------
|
|
154
|
+
|
|
155
|
+
def show_canvas(self, canvas_id: str) -> None:
|
|
156
|
+
"""Render a ``Canvas`` in a new Tk window."""
|
|
157
|
+
canvas = self.canvas_registry.get(canvas_id)
|
|
158
|
+
|
|
159
|
+
def _create():
|
|
160
|
+
win = tk.Toplevel(self._root)
|
|
161
|
+
win.title(f"Canvas: {canvas_id}")
|
|
162
|
+
tk_c = tk.Canvas(win, width=canvas.width * 4,
|
|
163
|
+
height=canvas.height * 4, bg="white")
|
|
164
|
+
tk_c.pack()
|
|
165
|
+
self._tk_canvases[canvas_id] = tk_c
|
|
166
|
+
self._draw_canvas(tk_c, canvas)
|
|
167
|
+
|
|
168
|
+
self._root.after(0, _create)
|
|
169
|
+
|
|
170
|
+
def _draw_canvas(self, tk_c: tk.Canvas, canvas: Canvas) -> None:
|
|
171
|
+
scale = 4
|
|
172
|
+
for op_type, args in canvas.operations:
|
|
173
|
+
if op_type == "line":
|
|
174
|
+
x1, y1, x2, y2 = args
|
|
175
|
+
tk_c.create_line(
|
|
176
|
+
x1 * scale, y1 * scale,
|
|
177
|
+
x2 * scale, y2 * scale,
|
|
178
|
+
fill="black", width=2,
|
|
179
|
+
)
|
|
180
|
+
elif op_type == "text":
|
|
181
|
+
x, y, text = args
|
|
182
|
+
tk_c.create_text(x * scale, y * scale, text=text, anchor="nw")
|
|
183
|
+
|
|
184
|
+
# -- Event binding -----------------------------------------------------
|
|
185
|
+
|
|
186
|
+
def on(self, component_name: str, event: str, handler: Callable) -> None:
|
|
187
|
+
"""Register an event handler for a component.
|
|
188
|
+
|
|
189
|
+
Example: ``backend.on("my_button", "click", my_handler)``
|
|
190
|
+
"""
|
|
191
|
+
self._event_handlers[f"{component_name}:{event}"] = handler
|
|
192
|
+
|
|
193
|
+
# -- Utility -----------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
def wait(self) -> None:
|
|
196
|
+
"""Block until the Tk loop terminates (all windows closed)."""
|
|
197
|
+
if self._thread:
|
|
198
|
+
self._thread.join()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
# Public factory
|
|
203
|
+
# ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
def create_tk_backend(**kwargs) -> TkBackend:
|
|
206
|
+
backend = TkBackend(**kwargs)
|
|
207
|
+
backend.start()
|
|
208
|
+
return backend
|