zexus 1.6.8 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +12 -5
  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/capability_system.py +184 -9
  45. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  46. package/src/zexus/cli/main.py +383 -34
  47. package/src/zexus/cli/zpm.py +1 -1
  48. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  53. package/src/zexus/compiler/bytecode.py +124 -7
  54. package/src/zexus/compiler/compat_runtime.py +6 -2
  55. package/src/zexus/compiler/lexer.py +16 -5
  56. package/src/zexus/compiler/parser.py +108 -7
  57. package/src/zexus/compiler/semantic.py +18 -19
  58. package/src/zexus/compiler/zexus_ast.py +26 -1
  59. package/src/zexus/concurrency_system.py +79 -0
  60. package/src/zexus/config.py +54 -0
  61. package/src/zexus/crypto_bridge.py +244 -8
  62. package/src/zexus/dap/__init__.py +10 -0
  63. package/src/zexus/dap/__main__.py +4 -0
  64. package/src/zexus/dap/dap_server.py +391 -0
  65. package/src/zexus/dap/debug_engine.py +298 -0
  66. package/src/zexus/environment.py +112 -9
  67. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  70. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  71. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  72. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  73. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  74. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  75. package/src/zexus/evaluator/bytecode_compiler.py +457 -37
  76. package/src/zexus/evaluator/core.py +644 -50
  77. package/src/zexus/evaluator/expressions.py +358 -62
  78. package/src/zexus/evaluator/functions.py +458 -20
  79. package/src/zexus/evaluator/resource_limiter.py +4 -4
  80. package/src/zexus/evaluator/statements.py +774 -122
  81. package/src/zexus/evaluator/unified_execution.py +573 -72
  82. package/src/zexus/evaluator/utils.py +14 -2
  83. package/src/zexus/evaluator_original.py +1 -1
  84. package/src/zexus/event_loop.py +186 -0
  85. package/src/zexus/lexer.py +742 -458
  86. package/src/zexus/lsp/__init__.py +1 -1
  87. package/src/zexus/lsp/definition_provider.py +163 -9
  88. package/src/zexus/lsp/server.py +22 -8
  89. package/src/zexus/lsp/symbol_provider.py +182 -9
  90. package/src/zexus/module_cache.py +239 -9
  91. package/src/zexus/module_manager.py +129 -1
  92. package/src/zexus/object.py +76 -6
  93. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  94. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  95. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  96. package/src/zexus/parser/parser.py +1349 -408
  97. package/src/zexus/parser/strategy_context.py +755 -58
  98. package/src/zexus/parser/strategy_structural.py +121 -21
  99. package/src/zexus/persistence.py +15 -1
  100. package/src/zexus/renderer/__init__.py +61 -0
  101. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  103. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  104. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  105. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  106. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  107. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  108. package/src/zexus/renderer/backend.py +261 -0
  109. package/src/zexus/renderer/canvas.py +78 -0
  110. package/src/zexus/renderer/color_system.py +201 -0
  111. package/src/zexus/renderer/graphics.py +31 -0
  112. package/src/zexus/renderer/layout.py +222 -0
  113. package/src/zexus/renderer/main_renderer.py +66 -0
  114. package/src/zexus/renderer/painter.py +30 -0
  115. package/src/zexus/renderer/tk_backend.py +208 -0
  116. package/src/zexus/renderer/web_backend.py +260 -0
  117. package/src/zexus/runtime/__init__.py +10 -2
  118. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  120. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  121. package/src/zexus/runtime/file_flags.py +137 -0
  122. package/src/zexus/runtime/load_manager.py +368 -0
  123. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  125. package/src/zexus/security.py +424 -34
  126. package/src/zexus/stdlib/fs.py +23 -18
  127. package/src/zexus/stdlib/http.py +289 -186
  128. package/src/zexus/stdlib/sockets.py +207 -163
  129. package/src/zexus/stdlib/websockets.py +282 -0
  130. package/src/zexus/stdlib_integration.py +369 -2
  131. package/src/zexus/strategy_recovery.py +6 -3
  132. package/src/zexus/type_checker.py +423 -0
  133. package/src/zexus/virtual_filesystem.py +189 -2
  134. package/src/zexus/vm/__init__.py +113 -3
  135. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  136. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  137. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  138. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  139. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  140. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  141. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  142. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  143. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  144. package/src/zexus/vm/async_optimizer.py +80 -6
  145. package/src/zexus/vm/binary_bytecode.py +659 -0
  146. package/src/zexus/vm/bytecode.py +59 -11
  147. package/src/zexus/vm/bytecode_converter.py +26 -12
  148. package/src/zexus/vm/cabi.c +1985 -0
  149. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  150. package/src/zexus/vm/cabi.h +127 -0
  151. package/src/zexus/vm/cache.py +561 -17
  152. package/src/zexus/vm/compiler.py +818 -51
  153. package/src/zexus/vm/fastops.c +15743 -0
  154. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  155. package/src/zexus/vm/fastops.pyx +288 -0
  156. package/src/zexus/vm/gas_metering.py +50 -9
  157. package/src/zexus/vm/jit.py +364 -20
  158. package/src/zexus/vm/native_jit_backend.py +1816 -0
  159. package/src/zexus/vm/native_runtime.cpp +1388 -0
  160. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  161. package/src/zexus/vm/optimizer.py +161 -11
  162. package/src/zexus/vm/parallel_vm.py +140 -45
  163. package/src/zexus/vm/peephole_optimizer.py +82 -4
  164. package/src/zexus/vm/profiler.py +38 -18
  165. package/src/zexus/vm/register_allocator.py +16 -5
  166. package/src/zexus/vm/register_vm.py +8 -5
  167. package/src/zexus/vm/vm.py +3581 -531
  168. package/src/zexus/vm/wasm_compiler.py +658 -0
  169. package/src/zexus/zexus_ast.py +137 -11
  170. package/src/zexus/zexus_token.py +16 -5
  171. package/src/zexus/zpm/installer.py +55 -15
  172. package/src/zexus/zpm/package_manager.py +1 -1
  173. package/src/zexus/zpm/registry.py +257 -28
  174. package/src/zexus.egg-info/PKG-INFO +16 -6
  175. package/src/zexus.egg-info/SOURCES.txt +129 -17
  176. package/src/zexus.egg-info/entry_points.txt +1 -0
  177. 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,10 +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,
41
- SCREEN, EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, PERSISTENT, AUDIT,
42
+ LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, FINALLY, EXTERNAL,
43
+ SCREEN, COLOR, CANVAS, GRAPHICS, ANIMATION, CLOCK,
44
+ EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, PERSISTENT, AUDIT,
42
45
  RESTRICT, SANDBOX, TRAIL, GC, BUFFER, SIMD,
43
- DEFER, PATTERN, ENUM, STREAM, WATCH,
46
+ DEFER, PATTERN, ENUM, STREAM, WATCH, MATCH,
44
47
  CAPABILITY, GRANT, REVOKE, VALIDATE, SANITIZE, IMMUTABLE,
45
48
  INTERFACE, TYPE_ALIAS, MODULE, PACKAGE, USING,
46
49
  CHANNEL, ATOMIC,
@@ -134,13 +137,44 @@ class StructuralAnalyzer:
134
137
  declaration_type = tokens[i].type
135
138
  export_tokens.append(tokens[i])
136
139
  i += 1
137
- # Now collect the rest of the declaration (NAME = VALUE)
138
- while i < n and tokens[i].type not in stop_types:
139
- # Stop if we see another statement starter (but not CONST/LET since we just saw it)
140
- if tokens[i].type in statement_starters and tokens[i].type not in [CONST, LET]:
141
- break
142
- 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)
143
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
144
178
  elif i < n and tokens[i].type == CONTRACT:
145
179
  # export contract Name { ... } - collect entire contract
146
180
  export_tokens.append(tokens[i])
@@ -170,6 +204,30 @@ class StructuralAnalyzer:
170
204
  elif tokens[i].type == RBRACE:
171
205
  brace_count -= 1
172
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
173
231
  elif i < n and tokens[i].type == FUNCTION:
174
232
  # export function name(...) { ... } - collect entire function
175
233
  export_tokens.append(tokens[i])
@@ -450,8 +508,16 @@ class StructuralAnalyzer:
450
508
  catch_tokens = [catch_token] + pre_brace_tokens + catch_block_tokens
451
509
  final_idx = after_catch_idx
452
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
+
453
519
  # Combine all tokens
454
- full_tokens = [t] + try_block_tokens + catch_tokens
520
+ full_tokens = [t] + try_block_tokens + catch_tokens + finally_tokens
455
521
  full_tokens = [tk for tk in full_tokens if not _is_empty_token(tk)]
456
522
 
457
523
  # Create the main try-catch block
@@ -743,7 +809,27 @@ class StructuralAnalyzer:
743
809
  # Break if we've already completed the LET/CONST and this is an indexed assignment
744
810
  if is_indexed_assignment and in_assignment and seen_assign:
745
811
  break
746
- # 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
747
833
 
748
834
  # Detect colon-based block (tolerable syntax for action/function/if/while etc.)
749
835
  if tj.type == COLON and nesting == 0 and t.type in {ACTION, FUNCTION, IF, WHILE, FOR}:
@@ -759,8 +845,10 @@ class StructuralAnalyzer:
759
845
  # Track nesting level BEFORE dedent check (so we don't break inside {...} or [...] or (...))
760
846
  if tj.type in {LPAREN, LBRACE, LBRACKET}:
761
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 '='
762
849
  if tj.type == LBRACE and not found_colon_block:
763
- found_brace_block = True
850
+ if not (in_assignment and not seen_assign):
851
+ found_brace_block = True
764
852
  nesting += 1
765
853
  elif tj.type in {RPAREN, RBRACE, RBRACKET}:
766
854
  nesting -= 1
@@ -791,10 +879,16 @@ class StructuralAnalyzer:
791
879
  # We're still parsing the condition - don't break yet
792
880
  pass
793
881
  else:
794
- # For LET/CONST, allow FUNCTION, SANDBOX, SANITIZE as RHS (expressions)
882
+ # For LET/CONST, allow FUNCTION, SANDBOX, SANITIZE, MATCH as RHS (expressions)
795
883
  # Also allow DEBUG when followed by ( for debug(x) function calls in assignments
796
884
  # Also allow IF when followed by THEN (if-then-else expression)
797
- 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
798
892
  allow_debug_call = tj.type == DEBUG and j + 1 < n and tokens[j + 1].type == LPAREN
799
893
  allow_if_then_else = False
800
894
  if tj.type == IF:
@@ -964,10 +1058,12 @@ class StructuralAnalyzer:
964
1058
  # CRITICAL FIX: Don't break if the previous token was ASSIGN
965
1059
  # This means the IDENT is the RHS value, not a new statement
966
1060
  prev_tok = run_tokens[-1] if run_tokens else None
967
- 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}")
968
1063
  if prev_tok and prev_tok.type == ASSIGN:
969
1064
  # This IDENT is the RHS of the assignment, not a new statement
970
- print(f" -> Continuing (RHS of assignment)")
1065
+ if debug_enabled:
1066
+ print(" -> Continuing (RHS of assignment)")
971
1067
  pass # Don't break, continue collecting
972
1068
  else:
973
1069
  # This is likely a new statement on a new line
@@ -1109,8 +1205,9 @@ class StructuralAnalyzer:
1109
1205
  stop_types = {SEMICOLON, RBRACE}
1110
1206
  # NOTE: SEND and RECEIVE removed - they can be used as function calls in expressions
1111
1207
  statement_starters = {
1112
- LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, EXTERNAL,
1113
- SCREEN, EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, AUDIT,
1208
+ LET, CONST, DATA, PRINT, FOR, IF, WHILE, RETURN, CONTINUE, BREAK, THROW, ACTION, FUNCTION, TRY, FINALLY, EXTERNAL,
1209
+ SCREEN, COLOR, CANVAS, GRAPHICS, ANIMATION, CLOCK,
1210
+ EXPORT, USE, DEBUG, ENTITY, CONTRACT, VERIFY, PROTECT, SEAL, AUDIT,
1114
1211
  RESTRICT, SANDBOX, TRAIL, NATIVE, GC, INLINE, BUFFER, SIMD,
1115
1212
  DEFER, PATTERN, ENUM, STREAM, WATCH,
1116
1213
  CAPABILITY, GRANT, REVOKE, VALIDATE, SANITIZE, IMMUTABLE,
@@ -1167,7 +1264,8 @@ class StructuralAnalyzer:
1167
1264
 
1168
1265
  # Export statement detection - collect export keyword + following declaration
1169
1266
  if t.type == EXPORT:
1170
- 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}")
1171
1269
  if cur:
1172
1270
  results.append(cur)
1173
1271
  cur = []
@@ -1177,9 +1275,11 @@ class StructuralAnalyzer:
1177
1275
  i += 1
1178
1276
 
1179
1277
  # Check if followed by const/let declaration (export const X = value)
1180
- 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}")
1181
1280
  if i < n and tokens[i].type in [CONST, LET]:
1182
- 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")
1183
1283
  # Collect the entire declaration
1184
1284
  while i < n and tokens[i].type not in stop_types:
1185
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)}
@@ -0,0 +1,61 @@
1
+ """Zexus renderer package.
2
+
3
+ This package exposes a cohesive rendering subsystem that can be consumed by
4
+ both the interpreter and the VM/compiler paths. The public API mirrors the
5
+ previous top-level ``renderer`` module so existing call sites remain
6
+ compatible while the implementation now lives alongside the core runtime.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from .backend import (
11
+ RendererBackend,
12
+ add_to_screen,
13
+ create_canvas,
14
+ create_named_canvas,
15
+ define_color,
16
+ define_component,
17
+ define_screen,
18
+ draw_line,
19
+ draw_text,
20
+ inspect_registry,
21
+ mix,
22
+ register_animation,
23
+ register_clock,
24
+ register_graphics,
25
+ render_screen,
26
+ set_theme,
27
+ )
28
+
29
+ __all__ = [
30
+ "RendererBackend",
31
+ "add_to_screen",
32
+ "create_canvas",
33
+ "create_named_canvas",
34
+ "define_color",
35
+ "define_component",
36
+ "define_screen",
37
+ "draw_line",
38
+ "draw_text",
39
+ "inspect_registry",
40
+ "mix",
41
+ "register_animation",
42
+ "register_clock",
43
+ "register_graphics",
44
+ "render_screen",
45
+ "set_theme",
46
+ # GUI backends
47
+ "TkBackend",
48
+ "WebBackend",
49
+ "create_tk_backend",
50
+ "create_web_backend",
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,261 @@
1
+ """Renderer backend exposed to the evaluator and VM implementations."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass, field
5
+ import re
6
+ from typing import Any, Dict, Iterable, Optional
7
+
8
+ from .canvas import CanvasRegistry
9
+ from .color_system import ColorPalette, RGBColor, Theme
10
+ from .layout import Screen, ScreenRegistry
11
+ from .main_renderer import ZexusScreenRenderer
12
+
13
+
14
+ @dataclass
15
+ class RendererState:
16
+ screens: ScreenRegistry = field(default_factory=ScreenRegistry)
17
+ canvases: CanvasRegistry = field(default_factory=CanvasRegistry)
18
+ themes: Dict[str, Theme] = field(default_factory=dict)
19
+ current_theme: Optional[str] = None
20
+ colours: Dict[str, str] = field(default_factory=dict)
21
+ graphics: Dict[str, Dict[str, Any]] = field(default_factory=dict)
22
+ animations: Dict[str, Dict[str, Any]] = field(default_factory=dict)
23
+ clocks: Dict[str, Dict[str, Any]] = field(default_factory=dict)
24
+ canvas_aliases: Dict[str, str] = field(default_factory=dict)
25
+
26
+
27
+ class RendererBackend:
28
+ def __init__(self) -> None:
29
+ self.palette = ColorPalette()
30
+ self.state = RendererState()
31
+ self.renderer = ZexusScreenRenderer(self.state.screens)
32
+
33
+ # ------------------------------------------------------------------
34
+ # Colour helpers
35
+ # ------------------------------------------------------------------
36
+ def mix(self, colour_a: str, colour_b: str, ratio: float = 0.5) -> RGBColor:
37
+ return self.palette.mix(colour_a, colour_b, ratio)
38
+
39
+ def define_color(self, name: str, spec: object) -> RGBColor:
40
+ colour = self._coerce_colour(spec)
41
+ self.palette.define(name, colour)
42
+ self.state.colours[name] = str(colour)
43
+ return colour
44
+
45
+ # ------------------------------------------------------------------
46
+ # Screen/component management
47
+ # ------------------------------------------------------------------
48
+ def define_screen(self, name: str, properties: Dict[str, Any] | None = None) -> None:
49
+ self.renderer.define_screen(name, **(properties or {}))
50
+
51
+ def define_component(self, name: str, properties: Dict[str, Any] | None = None) -> None:
52
+ props = dict(properties or {})
53
+ component_type = props.pop("type", None)
54
+ self.renderer.define_component(name, component_type, **props)
55
+
56
+ def add_to_screen(self, screen_name: str, component_name: str, overrides: Dict[str, Any] | None = None) -> None:
57
+ self.renderer.add_to_screen(screen_name, component_name, **(overrides or {}))
58
+
59
+ def render_screen(self, name: str, overrides: Dict[str, Any] | None = None) -> str:
60
+ return self.renderer.render_screen(name, **(overrides or {}))
61
+
62
+ # ------------------------------------------------------------------
63
+ # Theme handling
64
+ # ------------------------------------------------------------------
65
+ def set_theme(self, target_or_theme: str, theme_name: str | None = None) -> None:
66
+ if theme_name is None:
67
+ self.state.current_theme = target_or_theme
68
+ if target_or_theme not in self.state.themes:
69
+ self.state.themes[target_or_theme] = Theme(target_or_theme, self.palette)
70
+ return
71
+
72
+ if target_or_theme in self.state.screens.list_screens():
73
+ screen = self.state.screens.get_screen(target_or_theme)
74
+ screen.properties["theme"] = theme_name
75
+ if theme_name not in self.state.themes:
76
+ self.state.themes[theme_name] = Theme(theme_name, self.palette)
77
+ else:
78
+ if theme_name not in self.state.themes:
79
+ self.state.themes[theme_name] = Theme(theme_name, self.palette)
80
+
81
+ # ------------------------------------------------------------------
82
+ # Canvas helpers
83
+ # ------------------------------------------------------------------
84
+ def create_canvas(self, width: int, height: int) -> str:
85
+ return self.state.canvases.create(width=width, height=height)
86
+
87
+ def create_named_canvas(self, name: str, width: int, height: int) -> str:
88
+ identifier = self.create_canvas(width, height)
89
+ self.state.canvas_aliases[name] = identifier
90
+ return identifier
91
+
92
+ def draw_line(self, canvas_id: str, x1: int, y1: int, x2: int, y2: int) -> None:
93
+ canvas = self.state.canvases.get(self._resolve_canvas_identifier(canvas_id))
94
+ canvas.draw_line(x1, y1, x2, y2)
95
+
96
+ def draw_text(self, canvas_id: str, x: int, y: int, text: str) -> None:
97
+ canvas = self.state.canvases.get(self._resolve_canvas_identifier(canvas_id))
98
+ canvas.draw_text(x, y, text)
99
+
100
+ # ------------------------------------------------------------------
101
+ # Higher-level registries
102
+ # ------------------------------------------------------------------
103
+ def register_graphics(self, name: str, layers: Iterable[str] | Dict[str, Any]) -> None:
104
+ if isinstance(layers, dict):
105
+ payload = dict(layers)
106
+ else:
107
+ payload = {f"layer_{idx}": layer for idx, layer in enumerate(layers)}
108
+ self.state.graphics[name] = payload
109
+
110
+ def register_animation(self, name: str, frames: Dict[str, Any]) -> None:
111
+ self.state.animations[name] = dict(frames)
112
+
113
+ def register_clock(self, name: str, config: Dict[str, Any]) -> None:
114
+ self.state.clocks[name] = dict(config)
115
+
116
+ # ------------------------------------------------------------------
117
+ def inspect_registry(self) -> Dict[str, Any]:
118
+ return {
119
+ "screens": {
120
+ name: {
121
+ "properties": dict(screen.properties),
122
+ "children": [child.name for child in screen.children],
123
+ "theme": screen.properties.get("theme"),
124
+ }
125
+ for name, screen in self.state.screens.snapshot().items()
126
+ },
127
+ "components": {
128
+ name: dict(component.properties)
129
+ for name, component in self.state.screens.component_snapshot().items()
130
+ },
131
+ "themes": {name: theme.snapshot() for name, theme in self.state.themes.items()},
132
+ "canvases": self.state.canvases.snapshot(),
133
+ "canvas_aliases": dict(self.state.canvas_aliases),
134
+ "colours": dict(self.state.colours),
135
+ "graphics": {name: dict(data) for name, data in self.state.graphics.items()},
136
+ "animations": {name: dict(data) for name, data in self.state.animations.items()},
137
+ "clocks": {name: dict(data) for name, data in self.state.clocks.items()},
138
+ "current_theme": self.state.current_theme,
139
+ }
140
+
141
+ # ------------------------------------------------------------------
142
+ # Internal helpers
143
+ # ------------------------------------------------------------------
144
+ def _resolve_canvas_identifier(self, identifier: str) -> str:
145
+ if identifier in self.state.canvases.canvases:
146
+ return identifier
147
+ return self.state.canvas_aliases.get(identifier, identifier)
148
+
149
+ def _coerce_colour(self, spec: object) -> RGBColor:
150
+ if isinstance(spec, RGBColor):
151
+ return spec
152
+
153
+ if isinstance(spec, str):
154
+ text = spec.strip()
155
+ match = re.fullmatch(r"#?([0-9a-fA-F]{6})", text)
156
+ if match:
157
+ raw = match.group(1)
158
+ return RGBColor(int(raw[0:2], 16), int(raw[2:4], 16), int(raw[4:6], 16))
159
+ try:
160
+ return self.palette.get(text)
161
+ except KeyError as exc:
162
+ raise ValueError(str(exc)) from exc
163
+
164
+ if isinstance(spec, dict):
165
+ if {"r", "g", "b"}.issubset(spec):
166
+ return RGBColor(int(spec["r"]), int(spec["g"]), int(spec["b"]))
167
+ if {"red", "green", "blue"}.issubset(spec):
168
+ return RGBColor(int(spec["red"]), int(spec["green"]), int(spec["blue"]))
169
+ if "hex" in spec:
170
+ return self._coerce_colour(str(spec["hex"]))
171
+ raise ValueError("Colour map must include r/g/b values or hex")
172
+
173
+ if isinstance(spec, (list, tuple)) and len(spec) == 3:
174
+ r, g, b = spec
175
+ return RGBColor(int(r), int(g), int(b))
176
+
177
+ raise ValueError(f"Unsupported colour specification: {spec}")
178
+
179
+
180
+ # Module level helper mirroring the legacy API -------------------------
181
+ _BACKEND = RendererBackend()
182
+
183
+
184
+ def mix(colour_a: str, colour_b: str, ratio: float = 0.5) -> RGBColor:
185
+ return _BACKEND.mix(colour_a, colour_b, ratio)
186
+
187
+
188
+ def define_color(name: str, spec: object) -> RGBColor:
189
+ return _BACKEND.define_color(name, spec)
190
+
191
+
192
+ def define_screen(name: str, properties: Dict[str, Any] | None = None) -> None:
193
+ _BACKEND.define_screen(name, properties)
194
+
195
+
196
+ def define_component(name: str, properties: Dict[str, Any] | None = None) -> None:
197
+ _BACKEND.define_component(name, properties)
198
+
199
+
200
+ def add_to_screen(screen_name: str, component_name: str, overrides: Dict[str, Any] | None = None) -> None:
201
+ _BACKEND.add_to_screen(screen_name, component_name, overrides)
202
+
203
+
204
+ def render_screen(name: str, overrides: Dict[str, Any] | None = None) -> str:
205
+ return _BACKEND.render_screen(name, overrides)
206
+
207
+
208
+ def set_theme(target_or_theme: str, theme_name: str | None = None) -> None:
209
+ _BACKEND.set_theme(target_or_theme, theme_name)
210
+
211
+
212
+ def create_canvas(width: int, height: int) -> str:
213
+ return _BACKEND.create_canvas(width, height)
214
+
215
+
216
+ def create_named_canvas(name: str, width: int, height: int) -> str:
217
+ return _BACKEND.create_named_canvas(name, width, height)
218
+
219
+
220
+ def draw_line(canvas_id: str, x1: int, y1: int, x2: int, y2: int) -> None:
221
+ _BACKEND.draw_line(canvas_id, x1, y1, x2, y2)
222
+
223
+
224
+ def draw_text(canvas_id: str, x: int, y: int, text: str) -> None:
225
+ _BACKEND.draw_text(canvas_id, x, y, text)
226
+
227
+
228
+ def register_graphics(name: str, data: Dict[str, Any] | Iterable[str]) -> None:
229
+ _BACKEND.register_graphics(name, data)
230
+
231
+
232
+ def register_animation(name: str, data: Dict[str, Any]) -> None:
233
+ _BACKEND.register_animation(name, data)
234
+
235
+
236
+ def register_clock(name: str, data: Dict[str, Any]) -> None:
237
+ _BACKEND.register_clock(name, data)
238
+
239
+
240
+ def inspect_registry() -> Dict[str, Any]:
241
+ return _BACKEND.inspect_registry()
242
+
243
+
244
+ __all__ = [
245
+ "RendererBackend",
246
+ "add_to_screen",
247
+ "create_canvas",
248
+ "create_named_canvas",
249
+ "define_color",
250
+ "define_component",
251
+ "define_screen",
252
+ "draw_line",
253
+ "draw_text",
254
+ "inspect_registry",
255
+ "mix",
256
+ "register_animation",
257
+ "register_clock",
258
+ "register_graphics",
259
+ "render_screen",
260
+ "set_theme",
261
+ ]