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.
Files changed (159) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  45. package/src/zexus/cli/main.py +300 -20
  46. package/src/zexus/cli/zpm.py +1 -1
  47. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  48. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/lexer.py +10 -5
  53. package/src/zexus/concurrency_system.py +79 -0
  54. package/src/zexus/config.py +54 -0
  55. package/src/zexus/crypto_bridge.py +244 -8
  56. package/src/zexus/dap/__init__.py +10 -0
  57. package/src/zexus/dap/__main__.py +4 -0
  58. package/src/zexus/dap/dap_server.py +391 -0
  59. package/src/zexus/dap/debug_engine.py +298 -0
  60. package/src/zexus/environment.py +10 -1
  61. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  62. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  63. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  64. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  65. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  66. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  67. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/bytecode_compiler.py +441 -37
  70. package/src/zexus/evaluator/core.py +560 -49
  71. package/src/zexus/evaluator/expressions.py +122 -49
  72. package/src/zexus/evaluator/functions.py +417 -16
  73. package/src/zexus/evaluator/statements.py +521 -118
  74. package/src/zexus/evaluator/unified_execution.py +573 -72
  75. package/src/zexus/evaluator/utils.py +14 -2
  76. package/src/zexus/event_loop.py +186 -0
  77. package/src/zexus/lexer.py +742 -486
  78. package/src/zexus/lsp/__init__.py +1 -1
  79. package/src/zexus/lsp/definition_provider.py +163 -9
  80. package/src/zexus/lsp/server.py +22 -8
  81. package/src/zexus/lsp/symbol_provider.py +182 -9
  82. package/src/zexus/module_cache.py +237 -9
  83. package/src/zexus/object.py +64 -6
  84. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  85. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  86. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  87. package/src/zexus/parser/parser.py +786 -285
  88. package/src/zexus/parser/strategy_context.py +407 -66
  89. package/src/zexus/parser/strategy_structural.py +117 -19
  90. package/src/zexus/persistence.py +15 -1
  91. package/src/zexus/renderer/__init__.py +15 -0
  92. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  93. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  94. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  95. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  96. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  97. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  98. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  99. package/src/zexus/renderer/tk_backend.py +208 -0
  100. package/src/zexus/renderer/web_backend.py +260 -0
  101. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  103. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  104. package/src/zexus/runtime/file_flags.py +137 -0
  105. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  107. package/src/zexus/security.py +424 -34
  108. package/src/zexus/stdlib/fs.py +23 -18
  109. package/src/zexus/stdlib/http.py +289 -186
  110. package/src/zexus/stdlib/sockets.py +207 -163
  111. package/src/zexus/stdlib/websockets.py +282 -0
  112. package/src/zexus/stdlib_integration.py +369 -2
  113. package/src/zexus/strategy_recovery.py +6 -3
  114. package/src/zexus/type_checker.py +423 -0
  115. package/src/zexus/virtual_filesystem.py +189 -2
  116. package/src/zexus/vm/__init__.py +113 -3
  117. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  118. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  119. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  120. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  121. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  122. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  123. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  124. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  125. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  126. package/src/zexus/vm/async_optimizer.py +14 -1
  127. package/src/zexus/vm/binary_bytecode.py +659 -0
  128. package/src/zexus/vm/bytecode.py +28 -1
  129. package/src/zexus/vm/bytecode_converter.py +26 -12
  130. package/src/zexus/vm/cabi.c +1985 -0
  131. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  132. package/src/zexus/vm/cabi.h +127 -0
  133. package/src/zexus/vm/cache.py +557 -17
  134. package/src/zexus/vm/compiler.py +703 -5
  135. package/src/zexus/vm/fastops.c +15743 -0
  136. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  137. package/src/zexus/vm/fastops.pyx +288 -0
  138. package/src/zexus/vm/gas_metering.py +50 -9
  139. package/src/zexus/vm/jit.py +83 -2
  140. package/src/zexus/vm/native_jit_backend.py +1816 -0
  141. package/src/zexus/vm/native_runtime.cpp +1388 -0
  142. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  143. package/src/zexus/vm/optimizer.py +161 -11
  144. package/src/zexus/vm/parallel_vm.py +118 -42
  145. package/src/zexus/vm/peephole_optimizer.py +82 -4
  146. package/src/zexus/vm/profiler.py +38 -18
  147. package/src/zexus/vm/register_allocator.py +16 -5
  148. package/src/zexus/vm/register_vm.py +8 -5
  149. package/src/zexus/vm/vm.py +3411 -573
  150. package/src/zexus/vm/wasm_compiler.py +658 -0
  151. package/src/zexus/zexus_ast.py +63 -11
  152. package/src/zexus/zexus_token.py +13 -5
  153. package/src/zexus/zpm/installer.py +55 -15
  154. package/src/zexus/zpm/package_manager.py +1 -1
  155. package/src/zexus/zpm/registry.py +257 -28
  156. package/src/zexus.egg-info/PKG-INFO +7 -4
  157. package/src/zexus.egg-info/SOURCES.txt +116 -9
  158. package/src/zexus.egg-info/entry_points.txt +1 -0
  159. 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
- # Now collect the rest of the declaration (NAME = VALUE)
139
- while i < n and tokens[i].type not in stop_types:
140
- # Stop if we see another statement starter (but not CONST/LET since we just saw it)
141
- if tokens[i].type in statement_starters and tokens[i].type not in [CONST, LET]:
142
- break
143
- export_tokens.append(tokens[i])
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
- # IDENT followed by LPAREN is a function call (already handled below, but listed for clarity)
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
- found_brace_block = True
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
- print(f" prev_tok={prev_tok.literal if prev_tok else None}, type={prev_tok.type if prev_tok else None}")
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
- print(f" -> Continuing (RHS of assignment)")
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
- print(f"[DEBUG STRUCT] Found EXPORT token at index {i}")
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
- print(f"[DEBUG STRUCT] Next token at {i}: {tokens[i].type if i < n else 'EOF'}, CONST={CONST}, LET={LET}")
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
- print(f"[DEBUG STRUCT] Export followed by CONST/LET, collecting declaration")
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])
@@ -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
- serialized = {k: self._serialize(v) for k, v in obj.pairs.items()}
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}")
@@ -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