zexus 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +2513 -0
  3. package/bin/zexus +2 -0
  4. package/bin/zpics +2 -0
  5. package/bin/zpm +2 -0
  6. package/bin/zx +2 -0
  7. package/bin/zx-deploy +2 -0
  8. package/bin/zx-dev +2 -0
  9. package/bin/zx-run +2 -0
  10. package/package.json +66 -0
  11. package/scripts/README.md +24 -0
  12. package/scripts/postinstall.js +44 -0
  13. package/shared_config.json +24 -0
  14. package/src/README.md +1525 -0
  15. package/src/tests/run_zexus_tests.py +117 -0
  16. package/src/tests/test_all_phases.zx +346 -0
  17. package/src/tests/test_blockchain_features.zx +306 -0
  18. package/src/tests/test_complexity_features.zx +321 -0
  19. package/src/tests/test_core_integration.py +185 -0
  20. package/src/tests/test_phase10_ecosystem.zx +177 -0
  21. package/src/tests/test_phase1_modifiers.zx +87 -0
  22. package/src/tests/test_phase2_plugins.zx +80 -0
  23. package/src/tests/test_phase3_security.zx +97 -0
  24. package/src/tests/test_phase4_vfs.zx +116 -0
  25. package/src/tests/test_phase5_types.zx +117 -0
  26. package/src/tests/test_phase6_metaprogramming.zx +125 -0
  27. package/src/tests/test_phase7_optimization.zx +132 -0
  28. package/src/tests/test_phase9_advanced_types.zx +157 -0
  29. package/src/tests/test_security_features.py +419 -0
  30. package/src/tests/test_security_features.zx +276 -0
  31. package/src/tests/test_simple_zx.zx +1 -0
  32. package/src/tests/test_verification_simple.zx +69 -0
  33. package/src/zexus/__init__.py +28 -0
  34. package/src/zexus/__main__.py +5 -0
  35. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
  37. package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
  38. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  39. package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
  40. package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
  41. package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
  42. package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
  43. package/src/zexus/__pycache__/ecosystem.cpython-312.pyc +0 -0
  44. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  45. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  46. package/src/zexus/__pycache__/hybrid_orchestrator.cpython-312.pyc +0 -0
  47. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  48. package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
  49. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  50. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  51. package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
  52. package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
  53. package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
  54. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  55. package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
  56. package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
  57. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  58. package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
  59. package/src/zexus/__pycache__/virtual_filesystem.cpython-312.pyc +0 -0
  60. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  61. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  62. package/src/zexus/advanced_types.py +401 -0
  63. package/src/zexus/blockchain/__init__.py +40 -0
  64. package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
  66. package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
  67. package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
  68. package/src/zexus/blockchain/crypto.py +463 -0
  69. package/src/zexus/blockchain/ledger.py +255 -0
  70. package/src/zexus/blockchain/transaction.py +267 -0
  71. package/src/zexus/builtin_modules.py +284 -0
  72. package/src/zexus/builtin_plugins.py +317 -0
  73. package/src/zexus/capability_system.py +372 -0
  74. package/src/zexus/cli/__init__.py +2 -0
  75. package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  76. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  77. package/src/zexus/cli/main.py +707 -0
  78. package/src/zexus/cli/zpm.py +203 -0
  79. package/src/zexus/compare_interpreter_compiler.py +146 -0
  80. package/src/zexus/compiler/__init__.py +169 -0
  81. package/src/zexus/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
  82. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  83. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  84. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  85. package/src/zexus/compiler/bytecode.py +266 -0
  86. package/src/zexus/compiler/compat_runtime.py +277 -0
  87. package/src/zexus/compiler/lexer.py +257 -0
  88. package/src/zexus/compiler/parser.py +779 -0
  89. package/src/zexus/compiler/semantic.py +118 -0
  90. package/src/zexus/compiler/zexus_ast.py +454 -0
  91. package/src/zexus/complexity_system.py +575 -0
  92. package/src/zexus/concurrency_system.py +493 -0
  93. package/src/zexus/config.py +201 -0
  94. package/src/zexus/crypto_bridge.py +19 -0
  95. package/src/zexus/dependency_injection.py +423 -0
  96. package/src/zexus/ecosystem.py +434 -0
  97. package/src/zexus/environment.py +101 -0
  98. package/src/zexus/environment_manager.py +119 -0
  99. package/src/zexus/error_reporter.py +314 -0
  100. package/src/zexus/evaluator/__init__.py +12 -0
  101. package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  103. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  104. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  105. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  106. package/src/zexus/evaluator/__pycache__/integration.cpython-312.pyc +0 -0
  107. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  108. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  109. package/src/zexus/evaluator/bytecode_compiler.py +700 -0
  110. package/src/zexus/evaluator/core.py +891 -0
  111. package/src/zexus/evaluator/expressions.py +827 -0
  112. package/src/zexus/evaluator/functions.py +3989 -0
  113. package/src/zexus/evaluator/integration.py +396 -0
  114. package/src/zexus/evaluator/statements.py +4303 -0
  115. package/src/zexus/evaluator/utils.py +126 -0
  116. package/src/zexus/evaluator_original.py +2041 -0
  117. package/src/zexus/external_bridge.py +16 -0
  118. package/src/zexus/find_affected_imports.sh +155 -0
  119. package/src/zexus/hybrid_orchestrator.py +152 -0
  120. package/src/zexus/input_validation.py +259 -0
  121. package/src/zexus/lexer.py +571 -0
  122. package/src/zexus/logging.py +89 -0
  123. package/src/zexus/lsp/__init__.py +9 -0
  124. package/src/zexus/lsp/completion_provider.py +207 -0
  125. package/src/zexus/lsp/definition_provider.py +22 -0
  126. package/src/zexus/lsp/hover_provider.py +71 -0
  127. package/src/zexus/lsp/server.py +269 -0
  128. package/src/zexus/lsp/symbol_provider.py +31 -0
  129. package/src/zexus/metaprogramming.py +321 -0
  130. package/src/zexus/module_cache.py +89 -0
  131. package/src/zexus/module_manager.py +107 -0
  132. package/src/zexus/object.py +973 -0
  133. package/src/zexus/optimization.py +424 -0
  134. package/src/zexus/parser/__init__.py +31 -0
  135. package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
  136. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  137. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  138. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  139. package/src/zexus/parser/integration.py +86 -0
  140. package/src/zexus/parser/parser.py +3977 -0
  141. package/src/zexus/parser/strategy_context.py +7254 -0
  142. package/src/zexus/parser/strategy_structural.py +1033 -0
  143. package/src/zexus/persistence.py +391 -0
  144. package/src/zexus/plugin_system.py +290 -0
  145. package/src/zexus/policy_engine.py +365 -0
  146. package/src/zexus/profiler/__init__.py +5 -0
  147. package/src/zexus/profiler/profiler.py +233 -0
  148. package/src/zexus/purity_system.py +398 -0
  149. package/src/zexus/runtime/__init__.py +20 -0
  150. package/src/zexus/runtime/async_runtime.py +324 -0
  151. package/src/zexus/search_old_imports.sh +65 -0
  152. package/src/zexus/security.py +1407 -0
  153. package/src/zexus/stack_trace.py +233 -0
  154. package/src/zexus/stdlib/__init__.py +27 -0
  155. package/src/zexus/stdlib/blockchain.py +341 -0
  156. package/src/zexus/stdlib/compression.py +167 -0
  157. package/src/zexus/stdlib/crypto.py +124 -0
  158. package/src/zexus/stdlib/datetime.py +163 -0
  159. package/src/zexus/stdlib/db_mongo.py +199 -0
  160. package/src/zexus/stdlib/db_mysql.py +162 -0
  161. package/src/zexus/stdlib/db_postgres.py +163 -0
  162. package/src/zexus/stdlib/db_sqlite.py +133 -0
  163. package/src/zexus/stdlib/encoding.py +230 -0
  164. package/src/zexus/stdlib/fs.py +195 -0
  165. package/src/zexus/stdlib/http.py +219 -0
  166. package/src/zexus/stdlib/http_server.py +248 -0
  167. package/src/zexus/stdlib/json_module.py +61 -0
  168. package/src/zexus/stdlib/math.py +360 -0
  169. package/src/zexus/stdlib/os_module.py +265 -0
  170. package/src/zexus/stdlib/regex.py +148 -0
  171. package/src/zexus/stdlib/sockets.py +253 -0
  172. package/src/zexus/stdlib/test_framework.zx +208 -0
  173. package/src/zexus/stdlib/test_runner.zx +119 -0
  174. package/src/zexus/stdlib_integration.py +341 -0
  175. package/src/zexus/strategy_recovery.py +256 -0
  176. package/src/zexus/syntax_validator.py +356 -0
  177. package/src/zexus/testing/zpics.py +407 -0
  178. package/src/zexus/testing/zpics_runtime.py +369 -0
  179. package/src/zexus/type_system.py +374 -0
  180. package/src/zexus/validation_system.py +569 -0
  181. package/src/zexus/virtual_filesystem.py +355 -0
  182. package/src/zexus/vm/__init__.py +8 -0
  183. package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
  184. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  185. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  186. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  187. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  188. package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
  189. package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
  190. package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
  191. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  192. package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
  193. package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
  194. package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
  195. package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
  196. package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
  197. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  198. package/src/zexus/vm/async_optimizer.py +420 -0
  199. package/src/zexus/vm/bytecode.py +428 -0
  200. package/src/zexus/vm/bytecode_converter.py +297 -0
  201. package/src/zexus/vm/cache.py +532 -0
  202. package/src/zexus/vm/jit.py +720 -0
  203. package/src/zexus/vm/memory_manager.py +520 -0
  204. package/src/zexus/vm/memory_pool.py +511 -0
  205. package/src/zexus/vm/optimizer.py +478 -0
  206. package/src/zexus/vm/parallel_vm.py +899 -0
  207. package/src/zexus/vm/peephole_optimizer.py +452 -0
  208. package/src/zexus/vm/profiler.py +527 -0
  209. package/src/zexus/vm/register_allocator.py +462 -0
  210. package/src/zexus/vm/register_vm.py +520 -0
  211. package/src/zexus/vm/ssa_converter.py +757 -0
  212. package/src/zexus/vm/vm.py +1392 -0
  213. package/src/zexus/zexus_ast.py +1782 -0
  214. package/src/zexus/zexus_token.py +253 -0
  215. package/src/zexus/zpm/__init__.py +15 -0
  216. package/src/zexus/zpm/installer.py +116 -0
  217. package/src/zexus/zpm/package_manager.py +208 -0
  218. package/src/zexus/zpm/publisher.py +98 -0
  219. package/src/zexus/zpm/registry.py +110 -0
  220. package/src/zexus.egg-info/PKG-INFO +2235 -0
  221. package/src/zexus.egg-info/SOURCES.txt +876 -0
  222. package/src/zexus.egg-info/dependency_links.txt +1 -0
  223. package/src/zexus.egg-info/entry_points.txt +3 -0
  224. package/src/zexus.egg-info/not-zip-safe +1 -0
  225. package/src/zexus.egg-info/requires.txt +14 -0
  226. package/src/zexus.egg-info/top_level.txt +2 -0
  227. package/zexus.json +14 -0
@@ -0,0 +1,3977 @@
1
+ ## src/zexus/parser.py
2
+ import tempfile
3
+ import os
4
+ from ..zexus_token import *
5
+ from ..lexer import Lexer
6
+ from ..zexus_ast import *
7
+ from .strategy_structural import StructuralAnalyzer
8
+ from .strategy_context import ContextStackParser
9
+ from ..strategy_recovery import ErrorRecoveryEngine
10
+ from ..config import config # Import the config
11
+ from ..error_reporter import (
12
+ get_error_reporter,
13
+ SyntaxError as ZexusSyntaxError,
14
+ )
15
+
16
+ # Precedence constants
17
+ LOWEST, TERNARY, ASSIGN_PREC, NULLISH_PREC, LOGICAL, EQUALS, LESSGREATER, SUM, PRODUCT, PREFIX, CALL = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
18
+
19
+ precedences = {
20
+ QUESTION: TERNARY, # condition ? true : false (very low precedence)
21
+ ASSIGN: ASSIGN_PREC,
22
+ NULLISH: NULLISH_PREC, # value ?? default
23
+ OR: LOGICAL, AND: LOGICAL,
24
+ EQ: EQUALS, NOT_EQ: EQUALS,
25
+ LT: LESSGREATER, GT: LESSGREATER, LTE: LESSGREATER, GTE: LESSGREATER,
26
+ PLUS: SUM, MINUS: SUM,
27
+ SLASH: PRODUCT, STAR: PRODUCT, MOD: PRODUCT,
28
+ LPAREN: CALL,
29
+ LBRACKET: CALL,
30
+ DOT: CALL,
31
+ }
32
+
33
+ class UltimateParser:
34
+ def __init__(self, lexer, syntax_style=None, enable_advanced_strategies=None):
35
+ self.lexer = lexer
36
+ self.syntax_style = syntax_style or config.syntax_style
37
+ self.enable_advanced_strategies = (
38
+ enable_advanced_strategies
39
+ if enable_advanced_strategies is not None
40
+ else config.enable_advanced_parsing
41
+ )
42
+ self.errors = []
43
+ self.cur_token = None
44
+ self.peek_token = None
45
+
46
+ # Error reporter for better error messages
47
+ self.error_reporter = get_error_reporter()
48
+ self.filename = getattr(lexer, 'filename', '<stdin>')
49
+
50
+ # Multi-strategy architecture
51
+ if self.enable_advanced_strategies:
52
+ self._log("🚀 Initializing Ultimate Parser with Multi-Strategy Architecture...", "normal")
53
+ self.structural_analyzer = StructuralAnalyzer()
54
+ self.context_parser = ContextStackParser(self.structural_analyzer)
55
+ self.error_recovery = ErrorRecoveryEngine(self.structural_analyzer, self.context_parser)
56
+ self.block_map = {}
57
+ self.use_advanced_parsing = True
58
+ else:
59
+ self.use_advanced_parsing = False
60
+
61
+ # Traditional parser setup (fallback)
62
+ self.prefix_parse_fns = {
63
+ IDENT: self.parse_identifier,
64
+ INT: self.parse_integer_literal,
65
+ FLOAT: self.parse_float_literal,
66
+ STRING: self.parse_string_literal,
67
+ BANG: self.parse_prefix_expression,
68
+ MINUS: self.parse_prefix_expression,
69
+ TRUE: self.parse_boolean,
70
+ FALSE: self.parse_boolean,
71
+ NULL: self.parse_null,
72
+ THIS: self.parse_this,
73
+ LPAREN: self.parse_grouped_expression,
74
+ IF: self.parse_if_expression,
75
+ LBRACKET: self.parse_list_literal,
76
+ LBRACE: self.parse_map_literal, # CRITICAL: This handles { } objects
77
+ ACTION: self.parse_action_literal,
78
+ FUNCTION: self.parse_function_literal,
79
+ EMBEDDED: self.parse_embedded_literal,
80
+ LAMBDA: self.parse_lambda_expression,
81
+ DEBUG: self.parse_debug_statement,
82
+ TRY: self.parse_try_catch_statement,
83
+ EXTERNAL: self.parse_external_declaration,
84
+ ASYNC: self.parse_async_expression, # Support async <expression>
85
+ }
86
+ self.infix_parse_fns = {
87
+ PLUS: self.parse_infix_expression,
88
+ MINUS: self.parse_infix_expression,
89
+ SLASH: self.parse_infix_expression,
90
+ STAR: self.parse_infix_expression,
91
+ MOD: self.parse_infix_expression,
92
+ EQ: self.parse_infix_expression,
93
+ NOT_EQ: self.parse_infix_expression,
94
+ LT: self.parse_infix_expression,
95
+ GT: self.parse_infix_expression,
96
+ LTE: self.parse_infix_expression,
97
+ GTE: self.parse_infix_expression,
98
+ AND: self.parse_infix_expression,
99
+ OR: self.parse_infix_expression,
100
+ QUESTION: self.parse_ternary_expression, # condition ? true : false
101
+ NULLISH: self.parse_nullish_expression, # value ?? default
102
+ ASSIGN: self.parse_assignment_expression,
103
+ LAMBDA: self.parse_lambda_infix, # support arrow-style lambdas: params => body
104
+ LPAREN: self.parse_call_expression,
105
+ LBRACKET: self.parse_index_expression,
106
+ DOT: self.parse_method_call_expression,
107
+ }
108
+ self.next_token()
109
+ self.next_token()
110
+
111
+ def _log(self, message, level="normal"):
112
+ """Controlled logging based on config"""
113
+ if not config.enable_debug_logs:
114
+ return
115
+ if level == "verbose" and config.enable_debug_logs:
116
+ print(message)
117
+ elif level in ["normal", "minimal"]:
118
+ print(message)
119
+
120
+ def _create_parse_error(self, message, suggestion=None, token=None):
121
+ """
122
+ Create a properly formatted parse error with context.
123
+
124
+ Args:
125
+ message: Error message
126
+ suggestion: Optional helpful suggestion
127
+ token: Token where error occurred (defaults to current token)
128
+
129
+ Returns:
130
+ ZexusSyntaxError ready to be raised or appended
131
+ """
132
+ if token is None:
133
+ token = self.cur_token
134
+
135
+ line = getattr(token, 'line', None)
136
+ column = getattr(token, 'column', None)
137
+
138
+ return self.error_reporter.report_error(
139
+ ZexusSyntaxError,
140
+ message,
141
+ line=line,
142
+ column=column,
143
+ filename=self.filename,
144
+ suggestion=suggestion
145
+ )
146
+
147
+ def parse_program(self):
148
+ """The tolerant parsing pipeline - OPTIMIZED"""
149
+ if not self.use_advanced_parsing:
150
+ return self._parse_traditional()
151
+
152
+ try:
153
+ # OPTIMIZATION: Check if we already have tokens cached
154
+ if not hasattr(self, '_cached_tokens'):
155
+ self._cached_tokens = self._collect_all_tokens()
156
+
157
+ all_tokens = self._cached_tokens
158
+
159
+ # OPTIMIZATION: Only analyze structure if not done before
160
+ if not hasattr(self, '_structure_analyzed'):
161
+ self.block_map = self.structural_analyzer.analyze(all_tokens)
162
+ self._structure_analyzed = True
163
+
164
+ if config.enable_debug_logs:
165
+ self.structural_analyzer.print_structure()
166
+
167
+ # Phase 2: Parse ALL blocks
168
+ program = self._parse_all_blocks_tolerantly(all_tokens)
169
+
170
+ # Fallback if advanced parsing fails
171
+ if len(program.statements) == 0 and len(all_tokens) > 10:
172
+ return self._parse_traditional()
173
+
174
+ self._log(f"✅ Parsing Complete: {len(program.statements)} statements, {len(self.errors)} errors", "minimal")
175
+ return program
176
+
177
+ except Exception as e:
178
+ self._log(f"⚠️ Advanced parsing failed, falling back to traditional: {e}", "normal")
179
+ self.use_advanced_parsing = False
180
+ return self._parse_traditional()
181
+
182
+ def parse_map_literal(self):
183
+ """Parse a map/object literal: { key: value, ... }"""
184
+ # consume '{'
185
+ self.next_token()
186
+
187
+ pairs = []
188
+
189
+ # Empty map
190
+ if self.cur_token_is(RBRACE):
191
+ self.next_token()
192
+ return MapLiteral(pairs=pairs)
193
+
194
+ # Collect key:value pairs with nesting support
195
+ while not self.cur_token_is(EOF) and not self.cur_token_is(RBRACE):
196
+ # Parse key
197
+ if self.cur_token_is(STRING):
198
+ key = StringLiteral(self.cur_token.literal)
199
+ self.next_token()
200
+ elif self.cur_token_is(IDENT):
201
+ key = Identifier(self.cur_token.literal)
202
+ self.next_token()
203
+ else:
204
+ # Tolerant: skip unexpected tokens until colon or next pair
205
+ self.next_token()
206
+ continue
207
+
208
+ # Expect colon
209
+ if not self.cur_token_is(COLON):
210
+ # Tolerant: try to recover by searching forward
211
+ while not self.cur_token_is(COLON) and not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
212
+ self.next_token()
213
+ if not self.cur_token_is(COLON):
214
+ break
215
+
216
+ # consume ':'
217
+ self.next_token()
218
+
219
+ # Parse value expression until comma or closing brace
220
+ value_tokens = []
221
+ nesting = 0
222
+ while not self.cur_token_is(EOF) and not (self.cur_token_is(COMMA) and nesting == 0) and not (self.cur_token_is(RBRACE) and nesting == 0):
223
+ if self.cur_token_is(LPAREN) or self.cur_token_is(LBRACE) or self.cur_token_is(LBRACKET):
224
+ nesting += 1
225
+ elif self.cur_token_is(RPAREN) or self.cur_token_is(RBRACE) or self.cur_token_is(RBRACKET):
226
+ nesting -= 1
227
+ value_tokens.append(self.cur_token)
228
+ self.next_token()
229
+
230
+ value_expr = self.parse_expression(LOWEST) if value_tokens else None
231
+ pairs.append((key, value_expr))
232
+
233
+ # If current token is comma, consume it and continue
234
+ if self.cur_token_is(COMMA):
235
+ self.next_token()
236
+
237
+ # Expect closing brace
238
+ if not self.cur_token_is(RBRACE):
239
+ error = self._create_parse_error(
240
+ "Expected '}' to close map literal",
241
+ suggestion="Make sure all opening braces { have matching closing braces }"
242
+ )
243
+ raise error
244
+
245
+ # consume '}'
246
+ self.next_token()
247
+
248
+ return MapLiteral(pairs=pairs)
249
+
250
+ def _collect_all_tokens(self):
251
+ """Collect all tokens for structural analysis - OPTIMIZED"""
252
+ tokens = []
253
+ original_position = self.lexer.position
254
+ original_cur = self.cur_token
255
+ original_peek = self.peek_token
256
+
257
+ # Reset lexer to beginning
258
+ self.lexer.position = 0
259
+ self.lexer.read_position = 0
260
+ self.lexer.ch = ''
261
+ self.lexer.read_char()
262
+
263
+ # OPTIMIZATION: Pre-allocate list with reasonable capacity
264
+ tokens = []
265
+
266
+ # OPTIMIZATION: Collect all tokens without logging overhead
267
+ max_tokens = 100000
268
+ iteration = 0
269
+ while iteration < max_tokens:
270
+ iteration += 1
271
+ token = self.lexer.next_token()
272
+ tokens.append(token)
273
+ if token.type == EOF:
274
+ break
275
+
276
+ if iteration >= max_tokens:
277
+ self._log(f"⚠️ WARNING: Hit token limit ({max_tokens}), possible lexer infinite loop", "normal")
278
+
279
+ # Restore parser state
280
+ self.lexer.position = original_position
281
+ self.cur_token = original_cur
282
+ self.peek_token = original_peek
283
+
284
+ return tokens
285
+
286
+ def _parse_all_blocks_tolerantly(self, all_tokens):
287
+ """Parse ALL blocks without aggressive filtering - MAXIMUM TOLERANCE"""
288
+ program = Program()
289
+ parsed_count = 0
290
+ error_count = 0
291
+
292
+ # Parse ALL top-level blocks
293
+ top_level_blocks = [
294
+ block_id for block_id, block_info in self.block_map.items()
295
+ if not block_info.get('parent') # Only top-level blocks
296
+ ]
297
+
298
+ self._log(f"🔧 Parsing {len(top_level_blocks)} top-level blocks...", "normal")
299
+
300
+ for block_id in top_level_blocks:
301
+ block_info = self.block_map[block_id]
302
+ try:
303
+ statement = self.context_parser.parse_block(block_info, all_tokens)
304
+ if statement:
305
+ program.statements.append(statement)
306
+ parsed_count += 1
307
+ if config.enable_debug_logs: # Only show detailed parsing in verbose mode
308
+ stmt_type = type(statement).__name__
309
+ self._log(f" ✅ Parsed: {stmt_type} at line {block_info['start_token'].line}", "verbose")
310
+
311
+ except Exception as e:
312
+ error_msg = f"Line {block_info['start_token'].line}: {str(e)}"
313
+ self.errors.append(error_msg)
314
+ error_count += 1
315
+ self._log(f" ❌ Parse error: {error_msg}", "normal")
316
+
317
+ # Traditional fallback if no blocks were parsed
318
+ if parsed_count == 0 and top_level_blocks:
319
+ self._log("🔄 No blocks parsed with context parser, trying traditional fallback...", "normal")
320
+ for block_id in top_level_blocks[:3]: # Try first 3 blocks
321
+ block_info = self.block_map[block_id]
322
+ try:
323
+ block_tokens = block_info['tokens']
324
+ if block_tokens:
325
+ block_code = ' '.join([t.literal for t in block_tokens if t.literal])
326
+ mini_lexer = Lexer(block_code)
327
+ mini_parser = UltimateParser(mini_lexer, self.syntax_style, False)
328
+ mini_program = mini_parser.parse_program()
329
+ if mini_program.statements:
330
+ program.statements.extend(mini_program.statements)
331
+ parsed_count += len(mini_program.statements)
332
+ self._log(f" ✅ Traditional fallback parsed {len(mini_program.statements)} statements", "normal")
333
+ except Exception as e:
334
+ self._log(f" ❌ Traditional fallback also failed: {e}", "normal")
335
+
336
+ return program
337
+
338
+ def _parse_traditional(self):
339
+ """Traditional recursive descent parsing (fallback)"""
340
+ program = Program()
341
+ while not self.cur_token_is(EOF):
342
+ stmt = self.parse_statement()
343
+ if stmt is not None:
344
+ program.statements.append(stmt)
345
+ self.next_token()
346
+ return program
347
+
348
+ # === TOLERANT PARSER METHODS ===
349
+
350
+ def parse_statement(self):
351
+ """Parse statement with maximum tolerance"""
352
+ # Special case: Check for async expression (async <expr>) before parsing modifiers
353
+ # This must come FIRST before modifier parsing
354
+ if self.cur_token_is(ASYNC) and self.peek_token and self.peek_token.type not in {ACTION, FUNCTION}:
355
+ # This is an async expression, not a modifier
356
+ # Parse it as an expression statement containing AsyncExpression
357
+ from ..zexus_ast import ExpressionStatement
358
+ expr = self.parse_expression(LOWEST)
359
+ node = ExpressionStatement(expression=expr)
360
+ if self.peek_token_is(SEMICOLON):
361
+ self.next_token()
362
+ return node
363
+
364
+ # Support optional leading modifiers: e.g. `secure async action foo {}`
365
+ modifiers = []
366
+ if self.cur_token and self.cur_token.type in {PUBLIC, PRIVATE, SEALED, ASYNC, NATIVE, INLINE, SECURE, PURE, VIEW, PAYABLE}:
367
+ modifiers = self._parse_modifiers()
368
+ try:
369
+ node = None
370
+ if self.cur_token_is(LET):
371
+ node = self.parse_let_statement()
372
+ elif self.cur_token_is(CONST):
373
+ node = self.parse_const_statement()
374
+ elif self.cur_token_is(DATA):
375
+ node = self.parse_data_statement()
376
+ elif self.cur_token_is(RETURN):
377
+ node = self.parse_return_statement()
378
+ elif self.cur_token_is(CONTINUE):
379
+ node = self.parse_continue_statement()
380
+ elif self.cur_token_is(BREAK):
381
+ node = self.parse_break_statement()
382
+ elif self.cur_token_is(THROW):
383
+ node = self.parse_throw_statement()
384
+ elif self.cur_token_is(PRINT):
385
+ node = self.parse_print_statement()
386
+ elif self.cur_token_is(FOR):
387
+ node = self.parse_for_each_statement()
388
+ elif self.cur_token_is(SCREEN):
389
+ node = self.parse_screen_statement()
390
+ elif self.cur_token_is(ACTION):
391
+ node = self.parse_action_statement()
392
+ elif self.cur_token_is(FUNCTION):
393
+ node = self.parse_function_statement()
394
+ elif self.cur_token_is(IF):
395
+ node = self.parse_if_statement()
396
+ elif self.cur_token_is(WHILE):
397
+ node = self.parse_while_statement()
398
+ elif self.cur_token_is(USE):
399
+ node = self.parse_use_statement()
400
+ elif self.cur_token_is(EXACTLY):
401
+ node = self.parse_exactly_statement()
402
+ elif self.cur_token_is(EXPORT):
403
+ node = self.parse_export_statement()
404
+ elif self.cur_token_is(DEBUG):
405
+ node = self.parse_debug_statement()
406
+ elif self.cur_token_is(TRY):
407
+ node = self.parse_try_catch_statement()
408
+ elif self.cur_token_is(EXTERNAL):
409
+ node = self.parse_external_declaration()
410
+ elif self.cur_token_is(ENTITY):
411
+ node = self.parse_entity_statement()
412
+ elif self.cur_token_is(VERIFY):
413
+ node = self.parse_verify_statement()
414
+ elif self.cur_token_is(CONTRACT):
415
+ node = self.parse_contract_statement()
416
+ elif self.cur_token_is(PROTECT):
417
+ node = self.parse_protect_statement()
418
+ elif self.cur_token_is(SEAL):
419
+ node = self.parse_seal_statement()
420
+ elif self.cur_token_is(AUDIT):
421
+ node = self.parse_audit_statement()
422
+ elif self.cur_token_is(RESTRICT):
423
+ node = self.parse_restrict_statement()
424
+ elif self.cur_token_is(SANDBOX):
425
+ node = self.parse_sandbox_statement()
426
+ elif self.cur_token_is(TRAIL):
427
+ node = self.parse_trail_statement()
428
+ elif self.cur_token_is(TX):
429
+ node = self.parse_tx_statement()
430
+ elif self.cur_token_is(NATIVE):
431
+ node = self.parse_native_statement()
432
+ elif self.cur_token_is(GC):
433
+ node = self.parse_gc_statement()
434
+ elif self.cur_token_is(INLINE):
435
+ node = self.parse_inline_statement()
436
+ elif self.cur_token_is(BUFFER):
437
+ node = self.parse_buffer_statement()
438
+ elif self.cur_token_is(SIMD):
439
+ node = self.parse_simd_statement()
440
+ elif self.cur_token_is(DEFER):
441
+ node = self.parse_defer_statement()
442
+ elif self.cur_token_is(PATTERN):
443
+ node = self.parse_pattern_statement()
444
+ elif self.cur_token_is(ENUM):
445
+ node = self.parse_enum_statement()
446
+ elif self.cur_token_is(STREAM):
447
+ node = self.parse_stream_statement()
448
+ elif self.cur_token_is(WATCH):
449
+ print(f"[PARSE_STMT] Matched WATCH", file=sys.stderr, flush=True)
450
+ node = self.parse_watch_statement()
451
+ elif self.cur_token_is(EMIT):
452
+ print(f"[PARSE_STMT] Matched EMIT", file=sys.stderr, flush=True)
453
+ node = self.parse_emit_statement()
454
+ elif self.cur_token_is(MODIFIER):
455
+ print(f"[PARSE_STMT] Matched MODIFIER", file=sys.stderr, flush=True)
456
+ node = self.parse_modifier_declaration()
457
+ # === SECURITY STATEMENT HANDLERS ===
458
+ elif self.cur_token_is(CAPABILITY):
459
+ print(f"[PARSE_STMT] Matched CAPABILITY", file=sys.stderr, flush=True)
460
+ node = self.parse_capability_statement()
461
+ elif self.cur_token_is(GRANT):
462
+ print(f"[PARSE_STMT] Matched GRANT", file=sys.stderr, flush=True)
463
+ node = self.parse_grant_statement()
464
+ elif self.cur_token_is(REVOKE):
465
+ print(f"[PARSE_STMT] Matched REVOKE", file=sys.stderr, flush=True)
466
+ node = self.parse_revoke_statement()
467
+ elif self.cur_token_is(VALIDATE):
468
+ print(f"[PARSE_STMT] Matched VALIDATE", file=sys.stderr, flush=True)
469
+ node = self.parse_validate_statement()
470
+ elif self.cur_token_is(SANITIZE):
471
+ print(f"[PARSE_STMT] Matched SANITIZE", file=sys.stderr, flush=True)
472
+ node = self.parse_sanitize_statement()
473
+ elif self.cur_token_is(INJECT):
474
+ print(f"[PARSE_STMT] Matched INJECT", file=sys.stderr, flush=True)
475
+ node = self.parse_inject_statement()
476
+ elif self.cur_token_is(IMMUTABLE):
477
+ print(f"[PARSE_STMT] Matched IMMUTABLE", file=sys.stderr, flush=True)
478
+ node = self.parse_immutable_statement()
479
+ # === COMPLEXITY STATEMENT HANDLERS ===
480
+ elif self.cur_token_is(INTERFACE):
481
+ print(f"[PARSE_STMT] Matched INTERFACE", file=sys.stderr, flush=True)
482
+ node = self.parse_interface_statement()
483
+ elif self.cur_token_is(TYPE_ALIAS):
484
+ print(f"[PARSE_STMT] Matched TYPE_ALIAS", file=sys.stderr, flush=True)
485
+ node = self.parse_type_alias_statement()
486
+ elif self.cur_token_is(MODULE):
487
+ print(f"[PARSE_STMT] Matched MODULE", file=sys.stderr, flush=True)
488
+ node = self.parse_module_statement()
489
+ elif self.cur_token_is(PACKAGE):
490
+ print(f"[PARSE_STMT] Matched PACKAGE", file=sys.stderr, flush=True)
491
+ node = self.parse_package_statement()
492
+ elif self.cur_token_is(USING):
493
+ print(f"[PARSE_STMT] Matched USING", file=sys.stderr, flush=True)
494
+ node = self.parse_using_statement()
495
+ elif self.cur_token_is(CHANNEL):
496
+ print(f"[PARSE_STMT] Matched CHANNEL", file=sys.stderr, flush=True)
497
+ node = self.parse_channel_statement()
498
+ elif self.cur_token_is(SEND):
499
+ print(f"[PARSE_STMT] Matched SEND", file=sys.stderr, flush=True)
500
+ node = self.parse_send_statement()
501
+ elif self.cur_token_is(RECEIVE):
502
+ print(f"[PARSE_STMT] Matched RECEIVE", file=sys.stderr, flush=True)
503
+ node = self.parse_receive_statement()
504
+ elif self.cur_token_is(ATOMIC):
505
+ print(f"[PARSE_STMT] Matched ATOMIC", file=sys.stderr, flush=True)
506
+ node = self.parse_atomic_statement()
507
+ # === BLOCKCHAIN STATEMENT HANDLERS ===
508
+ elif self.cur_token_is(LEDGER):
509
+ print(f"[PARSE_STMT] Matched LEDGER", file=sys.stderr, flush=True)
510
+ node = self.parse_ledger_statement()
511
+ elif self.cur_token_is(STATE):
512
+ print(f"[PARSE_STMT] Matched STATE", file=sys.stderr, flush=True)
513
+ node = self.parse_state_statement()
514
+ # REQUIRE is now handled by ContextStackParser for enhanced syntax support
515
+ elif self.cur_token_is(REVERT):
516
+ print(f"[PARSE_STMT] Matched REVERT", file=sys.stderr, flush=True)
517
+ node = self.parse_revert_statement()
518
+ elif self.cur_token_is(LIMIT):
519
+ print(f"[PARSE_STMT] Matched LIMIT", file=sys.stderr, flush=True)
520
+ node = self.parse_limit_statement()
521
+ else:
522
+ print(f"[PARSE_STMT] No match, falling back to expression statement", file=sys.stderr, flush=True)
523
+ node = self.parse_expression_statement()
524
+
525
+ if node is not None:
526
+ return attach_modifiers(node, modifiers)
527
+ return None
528
+ except Exception as e:
529
+ # TOLERANT: Don't stop execution for parse errors, just log and continue
530
+ error_msg = f"Line {self.cur_token.line}:{self.cur_token.column} - Parse error: {str(e)}"
531
+ self.errors.append(error_msg)
532
+ self._log(f"⚠️ {error_msg}", "normal")
533
+
534
+ # Try to recover and continue
535
+ self.recover_to_next_statement()
536
+ return None
537
+
538
+ def _parse_modifiers(self):
539
+ """Consume consecutive modifier tokens and return a list of modifier names."""
540
+ mods = []
541
+ # Accept modifiers in any order until we hit a non-modifier token
542
+ while self.cur_token and self.cur_token.type in {PUBLIC, PRIVATE, SEALED, ASYNC, NATIVE, INLINE, SECURE, PURE, VIEW, PAYABLE}:
543
+ # store the literal (e.g. 'secure') for readability
544
+ mods.append(self.cur_token.literal if getattr(self.cur_token, 'literal', None) else self.cur_token.type)
545
+ self.next_token()
546
+ return mods
547
+
548
+ def parse_block(self, block_type=""):
549
+ """Unified block parser with maximum tolerance for both syntax styles"""
550
+ # For universal syntax, require braces
551
+ if self.syntax_style == "universal":
552
+ # Accept a brace either as the current token or the peek token
553
+ if self.cur_token_is(LBRACE) or self.peek_token_is(LBRACE):
554
+ # If the current token is not the brace, advance to it
555
+ if not self.cur_token_is(LBRACE):
556
+ if not self.expect_peek(LBRACE):
557
+ return None
558
+ return self.parse_brace_block()
559
+ else:
560
+ # In universal mode, if no brace, treat as single statement
561
+ return self.parse_single_statement_block()
562
+
563
+ # For tolerable/auto mode, accept both styles
564
+ # Accept a brace either as the current token or the peek token
565
+ if self.cur_token_is(LBRACE) or self.peek_token_is(LBRACE):
566
+ if not self.cur_token_is(LBRACE):
567
+ if not self.expect_peek(LBRACE):
568
+ return None
569
+ return self.parse_brace_block()
570
+ elif self.peek_token_is(COLON):
571
+ if not self.expect_peek(COLON):
572
+ return None
573
+ return self.parse_single_statement_block()
574
+ else:
575
+ # TOLERANT: If no block indicator, assume single statement
576
+ return self.parse_single_statement_block()
577
+
578
+ def parse_brace_block(self):
579
+ """Parse { } block with tolerance for missing closing brace"""
580
+ block = BlockStatement()
581
+ self.next_token()
582
+ import sys
583
+ print(f"[BLOCK_START] Entering brace block, first token: {self.cur_token.type}={repr(self.cur_token.literal)}", file=sys.stderr, flush=True)
584
+
585
+ brace_count = 1
586
+ stmt_count = 0
587
+ while brace_count > 0 and not self.cur_token_is(EOF):
588
+ if self.cur_token_is(LBRACE):
589
+ brace_count += 1
590
+ elif self.cur_token_is(RBRACE):
591
+ brace_count -= 1
592
+ if brace_count == 0:
593
+ break
594
+
595
+ print(f"[BLOCK_STMT] About to parse statement {stmt_count}, token: {self.cur_token.type}={repr(self.cur_token.literal)}", file=sys.stderr, flush=True)
596
+ stmt = self.parse_statement()
597
+ print(f"[BLOCK_STMT] Parsed statement {stmt_count}: {type(stmt).__name__ if stmt else 'None'}", file=sys.stderr, flush=True)
598
+ if stmt is not None:
599
+ block.statements.append(stmt)
600
+ self.next_token()
601
+ stmt_count += 1
602
+
603
+ print(f"[BLOCK_END] Finished block with {len(block.statements)} statements", file=sys.stderr, flush=True)
604
+ # TOLERANT: Don't error if we hit EOF without closing brace
605
+ if self.cur_token_is(EOF) and brace_count > 0:
606
+ self.errors.append(f"Line {self.cur_token.line}: Unclosed block (reached EOF)")
607
+
608
+ return block
609
+
610
+ def parse_single_statement_block(self):
611
+ """Parse a single statement as a block
612
+
613
+ Note: For advanced parsing mode (default), multi-statement indented blocks
614
+ are handled by the StructuralAnalyzer + ContextStackParser pipeline.
615
+ This method is primarily for traditional recursive descent parsing.
616
+ """
617
+ block = BlockStatement()
618
+ # Don't consume the next token if it's the end of a structure
619
+ if not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
620
+ stmt = self.parse_statement()
621
+ if stmt:
622
+ block.statements.append(stmt)
623
+ return block
624
+
625
+ def parse_if_statement(self):
626
+ """Tolerant if statement parser with elif support"""
627
+ import sys
628
+ print(f"[PARSE_IF] Starting if statement parsing", file=sys.stderr, flush=True)
629
+ # Skip IF token
630
+ self.next_token()
631
+
632
+ # Parse condition (with or without parentheses)
633
+ if self.cur_token_is(LPAREN):
634
+ self.next_token() # Skip (
635
+ condition = self.parse_expression(LOWEST)
636
+ if not self.expect_peek(RPAREN):
637
+ # Expected closing paren after condition
638
+ return None
639
+ else:
640
+ # No parentheses - parse expression directly
641
+ condition = self.parse_expression(LOWEST)
642
+
643
+ if not condition:
644
+ error = self._create_parse_error(
645
+ "Expected condition after 'if'",
646
+ suggestion="Add a condition expression: if (condition) { ... }"
647
+ )
648
+ raise error
649
+
650
+ print(f"[PARSE_IF] Parsed condition, now at token: {self.cur_token.type}={repr(self.cur_token.literal)}", file=sys.stderr, flush=True)
651
+ # Parse consequence (flexible block style)
652
+ consequence = self.parse_block("if")
653
+ print(f"[PARSE_IF] Parsed consequence block, now at token: {self.cur_token.type}={repr(self.cur_token.literal)}", file=sys.stderr, flush=True)
654
+ if not consequence:
655
+ return None
656
+
657
+ # Parse elif clauses
658
+ elif_parts = []
659
+ while self.cur_token_is(ELIF):
660
+ self.next_token() # Move past elif
661
+
662
+ # Parse elif condition (with or without parentheses)
663
+ if self.cur_token_is(LPAREN):
664
+ self.next_token() # Skip (
665
+ elif_condition = self.parse_expression(LOWEST)
666
+ if not self.expect_peek(RPAREN):
667
+ # Expected closing paren after elif condition
668
+ return None
669
+ else:
670
+ # No parentheses - parse expression directly
671
+ elif_condition = self.parse_expression(LOWEST)
672
+
673
+ if not elif_condition:
674
+ error = self._create_parse_error(
675
+ "Expected condition after 'elif'",
676
+ suggestion="Add a condition expression: elif (condition) { ... }"
677
+ )
678
+ raise error
679
+
680
+ # Parse elif consequence block
681
+ elif_consequence = self.parse_block("elif")
682
+ if not elif_consequence:
683
+ return None
684
+
685
+ elif_parts.append((elif_condition, elif_consequence))
686
+
687
+ # Parse else clause
688
+ alternative = None
689
+ if self.cur_token_is(ELSE):
690
+ self.next_token()
691
+ alternative = self.parse_block("else")
692
+
693
+ return IfStatement(condition=condition, consequence=consequence, elif_parts=elif_parts, alternative=alternative)
694
+
695
+ def parse_action_statement(self):
696
+ """Tolerant action parser supporting both syntax styles"""
697
+ if not self.expect_peek(IDENT):
698
+ self.errors.append("Expected function name after 'action'")
699
+ return None
700
+
701
+ name = Identifier(self.cur_token.literal)
702
+
703
+ # Parse parameters (with or without parentheses)
704
+ parameters = []
705
+ if self.peek_token_is(LPAREN):
706
+ self.next_token() # Skip to (
707
+ self.next_token() # Skip (
708
+ parameters = self.parse_action_parameters()
709
+ if parameters is None:
710
+ return None
711
+ elif self.peek_token_is(IDENT):
712
+ # Single parameter without parentheses
713
+ self.next_token()
714
+ parameters = [Identifier(self.cur_token.literal)]
715
+
716
+ # Parse optional return type: -> type
717
+ return_type = None
718
+ if self.peek_token_is(MINUS):
719
+ # Check if this is -> (return type annotation)
720
+ self.next_token() # Move to MINUS
721
+ if self.peek_token_is(GT):
722
+ self.next_token() # Move to GT
723
+ self.next_token() # Move to type identifier
724
+ if self.cur_token_is(IDENT):
725
+ return_type = self.cur_token.literal
726
+
727
+ # Parse body (flexible style)
728
+ body = self.parse_block("action")
729
+ if not body:
730
+ return None
731
+
732
+ # Note: is_async will be set by parse_statement if async modifier is present
733
+ return ActionStatement(name=name, parameters=parameters, body=body, is_async=False, return_type=return_type)
734
+
735
+ def parse_function_statement(self):
736
+ """Tolerant function parser supporting both syntax styles"""
737
+ if not self.expect_peek(IDENT):
738
+ self.errors.append("Expected function name after 'function'")
739
+ return None
740
+
741
+ name = Identifier(self.cur_token.literal)
742
+
743
+ # Parse parameters (with or without parentheses)
744
+ parameters = []
745
+ if self.peek_token_is(LPAREN):
746
+ self.next_token() # Skip to (
747
+ self.next_token() # Skip (
748
+ parameters = self.parse_action_parameters()
749
+ if parameters is None:
750
+ return None
751
+ elif self.peek_token_is(IDENT):
752
+ # Single parameter without parentheses
753
+ self.next_token()
754
+ parameters = [Identifier(self.cur_token.literal)]
755
+
756
+ # Parse optional return type: -> type
757
+ return_type = None
758
+ if self.peek_token_is(MINUS):
759
+ # Check if this is -> (return type annotation)
760
+ self.next_token() # Move to MINUS
761
+ if self.peek_token_is(GT):
762
+ self.next_token() # Move to GT
763
+ self.next_token() # Move to type identifier
764
+ if self.cur_token_is(IDENT):
765
+ return_type = self.cur_token.literal
766
+
767
+ # Parse body (flexible style)
768
+ body = self.parse_block("function")
769
+ if not body:
770
+ return None
771
+
772
+ return FunctionStatement(name=name, parameters=parameters, body=body, return_type=return_type)
773
+
774
+ def parse_let_statement(self):
775
+ """Tolerant let statement parser"""
776
+ stmt = LetStatement(name=None, value=None)
777
+
778
+ if not self.expect_peek(IDENT):
779
+ error = self._create_parse_error(
780
+ "Expected variable name after 'let'",
781
+ suggestion="Use 'let' to declare a variable: let myVariable = value"
782
+ )
783
+ raise error
784
+
785
+ stmt.name = Identifier(value=self.cur_token.literal)
786
+
787
+ # TOLERANT: Allow both = and : for assignment
788
+ if self.peek_token_is(ASSIGN) or (self.peek_token_is(COLON) and self.peek_token.literal == ":"):
789
+ self.next_token()
790
+ else:
791
+ self.errors.append("Expected '=' or ':' after variable name")
792
+ return None
793
+
794
+ self.next_token()
795
+ stmt.value = self.parse_expression(LOWEST)
796
+
797
+ # TOLERANT: Semicolon is optional
798
+ if self.peek_token_is(SEMICOLON):
799
+ self.next_token()
800
+
801
+ return stmt
802
+
803
+ def parse_const_statement(self):
804
+ """Tolerant const statement parser - immutable variable declaration
805
+
806
+ Syntax: const NAME = value;
807
+ """
808
+ stmt = ConstStatement(name=None, value=None)
809
+
810
+ if not self.expect_peek(IDENT):
811
+ self.errors.append("Expected variable name after 'const'")
812
+ return None
813
+
814
+ stmt.name = Identifier(value=self.cur_token.literal)
815
+
816
+ # TOLERANT: Allow both = and : for assignment
817
+ if self.peek_token_is(ASSIGN) or (self.peek_token_is(COLON) and self.peek_token.literal == ":"):
818
+ self.next_token()
819
+ else:
820
+ self.errors.append("Expected '=' or ':' after variable name in const declaration")
821
+ return None
822
+
823
+ self.next_token()
824
+ stmt.value = self.parse_expression(LOWEST)
825
+
826
+ # TOLERANT: Semicolon is optional
827
+ if self.peek_token_is(SEMICOLON):
828
+ self.next_token()
829
+
830
+ return stmt
831
+
832
+ def parse_data_statement(self):
833
+ """Parse data statement (dataclass definition)
834
+
835
+ Syntax:
836
+ data TypeName {
837
+ field1: type,
838
+ field2: type = default,
839
+ field3: type require constraint
840
+ }
841
+
842
+ data immutable Point { x: number, y: number }
843
+ data verified Transaction { from: address, to: address }
844
+ data Box<T> { value: T }
845
+ data Dog extends Animal { breed: string }
846
+ @validated data Email { address: string }
847
+ """
848
+ from ..zexus_ast import DataStatement, DataField, Identifier
849
+
850
+ # Parse decorators before 'data' keyword (if called from decorator context)
851
+ decorators = []
852
+
853
+ # Current token is DATA
854
+ # Check for modifiers (immutable, verified, etc.)
855
+ modifiers = []
856
+
857
+ # Look ahead for modifiers before type name
858
+ self.next_token() # Move past 'data'
859
+
860
+ while self.cur_token and self.cur_token_is(IDENT) and self.cur_token.literal in ["immutable", "verified"]:
861
+ modifiers.append(self.cur_token.literal)
862
+ self.next_token()
863
+
864
+ # Type name
865
+ if not self.cur_token_is(IDENT):
866
+ self.errors.append("Expected type name after 'data'")
867
+ return None
868
+
869
+ type_name = self.cur_token.literal
870
+ self.next_token()
871
+
872
+ # Parse generic type parameters: <T, U, V>
873
+ type_params = []
874
+ if self.cur_token_is(LT):
875
+ self.next_token() # Skip <
876
+
877
+ # Parse comma-separated type parameter names
878
+ while not self.cur_token_is(GT) and not self.cur_token_is(EOF):
879
+ if self.cur_token_is(IDENT):
880
+ type_params.append(self.cur_token.literal)
881
+ self.next_token()
882
+
883
+ # Check for comma or closing >
884
+ if self.cur_token_is(COMMA):
885
+ self.next_token() # Skip comma
886
+ elif self.cur_token_is(GT):
887
+ break # Will advance past > below
888
+ else:
889
+ self.errors.append(f"Invalid type parameters: expected ',' or '>', got {self.cur_token.type}")
890
+ return None
891
+ else:
892
+ self.errors.append(f"Invalid type parameter: expected identifier, got {self.cur_token.type}")
893
+ return None
894
+
895
+ if self.cur_token_is(GT):
896
+ self.next_token() # Skip >
897
+
898
+ # Check for inheritance: extends ParentType
899
+ parent_type = None
900
+ if self.cur_token_is(IDENT) and self.cur_token.literal == "extends":
901
+ self.next_token() # Skip 'extends'
902
+ if self.cur_token_is(IDENT):
903
+ parent_type = self.cur_token.literal
904
+ self.next_token()
905
+ else:
906
+ self.errors.append("Expected parent type name after 'extends'")
907
+ return None
908
+
909
+ # Expect opening brace
910
+ if not self.cur_token_is(LBRACE):
911
+ self.errors.append(f"Expected '{{' after data type name, got {self.cur_token.type}")
912
+ return None
913
+
914
+ # Parse field definitions
915
+ fields = []
916
+ self.next_token() # Move past {
917
+
918
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
919
+
920
+ # Skip commas and semicolons
921
+ if self.cur_token_is(COMMA) or self.cur_token_is(SEMICOLON):
922
+ self.next_token()
923
+ continue
924
+
925
+ # Check for decorators: @logged, @cached, etc.
926
+ field_decorators = []
927
+ while self.cur_token_is(AT):
928
+ self.next_token() # Skip @
929
+ if self.cur_token_is(IDENT):
930
+ field_decorators.append(self.cur_token.literal)
931
+ self.next_token()
932
+
933
+ # Field must start with identifier (or 'method'/'operator'/'computed' keyword)
934
+ if not self.cur_token_is(IDENT):
935
+ self.next_token()
936
+ continue
937
+
938
+ # Check for special field types
939
+ if self.cur_token.literal == "computed":
940
+ # computed area => width * height
941
+ self.next_token() # Skip 'computed'
942
+ if not self.cur_token_is(IDENT):
943
+ self.errors.append("Expected field name after 'computed'")
944
+ self.next_token()
945
+ continue
946
+
947
+ field_name = self.cur_token.literal
948
+ self.next_token()
949
+
950
+ # Expect => (LAMBDA token)
951
+ if not self.cur_token_is(LAMBDA):
952
+ self.errors.append(f"Expected '=>' after computed field name, got {self.cur_token.type}")
953
+ self.next_token()
954
+ continue
955
+
956
+ self.next_token() # Skip =>
957
+
958
+ # Parse the computed expression
959
+ computed_expr = self.parse_expression(LOWEST)
960
+ # After parse_expression, cur_token is at the last token of the expression
961
+ # We need to move to the next token (which should be a delimiter or closing brace)
962
+ if not self.cur_token_is(RBRACE):
963
+ self.next_token()
964
+
965
+ field = DataField(
966
+ name=field_name,
967
+ field_type=None,
968
+ default_value=None,
969
+ constraint=None,
970
+ computed=computed_expr,
971
+ decorators=field_decorators
972
+ )
973
+ fields.append(field)
974
+ # Continue to next field
975
+ continue
976
+
977
+ elif self.cur_token.literal == "method":
978
+ # method add(x) { return this.value + x; }
979
+ self.next_token() # Skip 'method'
980
+ if not self.cur_token_is(IDENT):
981
+ self.errors.append("Expected method name after 'method'")
982
+ self.next_token()
983
+ continue
984
+
985
+ method_name = self.cur_token.literal
986
+ self.next_token()
987
+
988
+ # Parse parameters
989
+ method_params = []
990
+ if self.cur_token_is(LPAREN):
991
+ self.next_token() # Skip (
992
+ while not self.cur_token_is(RPAREN) and not self.cur_token_is(EOF):
993
+ if self.cur_token_is(IDENT):
994
+ method_params.append(self.cur_token.literal)
995
+ self.next_token()
996
+ # Skip optional type annotation: : type
997
+ if self.cur_token_is(COLON):
998
+ self.next_token() # Skip :
999
+ self.skip_type_annotation()
1000
+ if self.cur_token_is(COMMA):
1001
+ self.next_token()
1002
+ if self.cur_token_is(RPAREN):
1003
+ self.next_token() # Skip )
1004
+
1005
+ # Parse method body
1006
+ if not self.cur_token_is(LBRACE):
1007
+ self.errors.append("Expected '{' after method parameters")
1008
+ self.next_token()
1009
+ continue
1010
+
1011
+ method_body_block = self.parse_block("method")
1012
+ if method_body_block:
1013
+ field = DataField(
1014
+ name=method_name,
1015
+ method_body=method_body_block.statements,
1016
+ method_params=method_params,
1017
+ decorators=field_decorators
1018
+ )
1019
+ fields.append(field)
1020
+ # After parse_block, cur_token is at method body's }, move past it
1021
+ if self.cur_token_is(RBRACE):
1022
+ self.next_token()
1023
+ # Continue to next field
1024
+ continue
1025
+
1026
+ elif self.cur_token.literal == "action":
1027
+ # action get_value() -> T { return this.value; }
1028
+ # Same as method, just different keyword
1029
+ self.next_token() # Skip 'action'
1030
+ if not self.cur_token_is(IDENT):
1031
+ self.errors.append("Expected action name after 'action'")
1032
+ self.next_token()
1033
+ continue
1034
+
1035
+ action_name = self.cur_token.literal
1036
+ self.next_token()
1037
+
1038
+ # Parse parameters (with or without parentheses, with optional type annotations)
1039
+ action_params = []
1040
+ if self.cur_token_is(LPAREN):
1041
+ self.next_token() # Skip (
1042
+ while not self.cur_token_is(RPAREN) and not self.cur_token_is(EOF):
1043
+ if self.cur_token_is(IDENT):
1044
+ action_params.append(self.cur_token.literal)
1045
+ self.next_token()
1046
+ # Skip optional type annotation: : type
1047
+ if self.cur_token_is(COLON):
1048
+ self.next_token() # Skip :
1049
+ self.skip_type_annotation()
1050
+ if self.cur_token_is(COMMA):
1051
+ self.next_token()
1052
+ if self.cur_token_is(RPAREN):
1053
+ self.next_token() # Skip )
1054
+
1055
+ # Skip optional return type: -> type
1056
+ if self.cur_token_is(MINUS):
1057
+ self.next_token() # Skip -
1058
+ if self.cur_token_is(GT):
1059
+ self.next_token() # Skip >
1060
+ self.skip_type_annotation()
1061
+
1062
+ # Parse action body
1063
+ if not self.cur_token_is(LBRACE):
1064
+ self.errors.append("Expected '{' after action parameters")
1065
+ self.next_token()
1066
+ continue
1067
+
1068
+ action_body_block = self.parse_block("action")
1069
+ if action_body_block:
1070
+ field = DataField(
1071
+ name=action_name,
1072
+ method_body=action_body_block.statements,
1073
+ method_params=action_params,
1074
+ decorators=field_decorators
1075
+ )
1076
+ fields.append(field)
1077
+ # After parse_block, cur_token is at action body's }, move past it
1078
+ if self.cur_token_is(RBRACE):
1079
+ self.next_token()
1080
+ # Continue to next field
1081
+ continue
1082
+
1083
+ elif self.cur_token.literal == "operator":
1084
+ # operator +(other) { return Vector(this.x + other.x, this.y + other.y); }
1085
+ self.next_token() # Skip 'operator'
1086
+
1087
+ # Get the operator symbol
1088
+ operator_symbol = None
1089
+ if self.cur_token.type in {PLUS, MINUS, STAR, SLASH, MOD, EQ, NOT_EQ, LT, GT, LTE, GTE}:
1090
+ operator_symbol = self.cur_token.literal
1091
+ self.next_token()
1092
+ else:
1093
+ self.errors.append(f"Invalid operator symbol: {self.cur_token.literal}")
1094
+ self.next_token()
1095
+ continue
1096
+
1097
+ # Parse parameters
1098
+ method_params = []
1099
+ if self.cur_token_is(LPAREN):
1100
+ self.next_token() # Skip (
1101
+ while not self.cur_token_is(RPAREN) and not self.cur_token_is(EOF):
1102
+ if self.cur_token_is(IDENT):
1103
+ method_params.append(self.cur_token.literal)
1104
+ self.next_token()
1105
+ # Skip optional type annotation: : type
1106
+ if self.cur_token_is(COLON):
1107
+ self.next_token() # Skip :
1108
+ self.skip_type_annotation()
1109
+ if self.cur_token_is(COMMA):
1110
+ self.next_token()
1111
+ if self.cur_token_is(RPAREN):
1112
+ self.next_token() # Skip )
1113
+
1114
+ # Parse operator body
1115
+ if not self.cur_token_is(LBRACE):
1116
+ self.errors.append("Expected '{' after operator parameters")
1117
+ self.next_token()
1118
+ continue
1119
+
1120
+ operator_body_block = self.parse_block("operator")
1121
+ if operator_body_block:
1122
+ field = DataField(
1123
+ name=f"operator_{operator_symbol}",
1124
+ operator=operator_symbol,
1125
+ method_body=operator_body_block.statements,
1126
+ method_params=method_params,
1127
+ decorators=field_decorators
1128
+ )
1129
+ fields.append(field)
1130
+ # After parse_block, cur_token is at operator body's }, move past it
1131
+ if self.cur_token_is(RBRACE):
1132
+ self.next_token()
1133
+ # Continue to next field
1134
+ continue
1135
+
1136
+ else:
1137
+ # Regular field: name: type = default require constraint
1138
+ field_name = self.cur_token.literal
1139
+ self.next_token()
1140
+
1141
+ # Check for type annotation
1142
+ field_type = None
1143
+ if self.cur_token_is(COLON):
1144
+ self.next_token() # Skip :
1145
+ if self.cur_token_is(IDENT):
1146
+ field_type = self.cur_token.literal
1147
+ self.next_token()
1148
+
1149
+ # Check for default value
1150
+ default_value = None
1151
+ if self.cur_token_is(ASSIGN):
1152
+ self.next_token() # Skip =
1153
+ default_value = self.parse_expression(LOWEST)
1154
+
1155
+ # Check for constraint (require clause)
1156
+ constraint = None
1157
+ if self.cur_token_is(IDENT) and self.cur_token.literal == "require":
1158
+ self.next_token() # Skip 'require'
1159
+ constraint = self.parse_expression(LOWEST)
1160
+
1161
+ field = DataField(
1162
+ name=field_name,
1163
+ field_type=field_type,
1164
+ default_value=default_value,
1165
+ constraint=constraint,
1166
+ decorators=field_decorators
1167
+ )
1168
+ fields.append(field)
1169
+
1170
+ # Note: After parsing, we may be at the dataclass's closing }
1171
+ # - For methods/operators: we advanced past their body's }, so we're already positioned correctly
1172
+ # - For regular fields: we're at the dataclass's }, no need to advance (caller handles it)
1173
+
1174
+ return DataStatement(
1175
+ name=Identifier(type_name),
1176
+ fields=fields,
1177
+ modifiers=modifiers,
1178
+ parent=parent_type,
1179
+ decorators=decorators,
1180
+ type_params=type_params
1181
+ )
1182
+
1183
+ def parse_print_statement(self):
1184
+ """Tolerant print statement parser with support for:
1185
+ - Single argument: print(message)
1186
+ - Multiple arguments: print(arg1, arg2, arg3)
1187
+ - Conditional print: print(condition, message) - exactly 2 args
1188
+ """
1189
+ import sys
1190
+ # Debug logging (fail silently if file operations fail)
1191
+ try:
1192
+ log_path = os.path.join(tempfile.gettempdir(), 'parser_log.txt')
1193
+ with open(log_path, 'a') as f:
1194
+ f.write(f"=== parse_print_statement CALLED ===\n")
1195
+ f.flush()
1196
+ except (IOError, OSError, PermissionError):
1197
+ pass # Silently ignore debug logging errors
1198
+
1199
+ stmt = PrintStatement(values=[])
1200
+ self.next_token()
1201
+
1202
+ # Parse first expression
1203
+ first_expr = self.parse_expression(LOWEST)
1204
+ if first_expr:
1205
+ stmt.values.append(first_expr)
1206
+
1207
+ # Parse additional comma-separated expressions
1208
+ while self.peek_token_is(COMMA):
1209
+ self.next_token() # consume comma
1210
+ self.next_token() # move to next expression
1211
+ expr = self.parse_expression(LOWEST)
1212
+ if expr:
1213
+ stmt.values.append(expr)
1214
+
1215
+ # Check if this is conditional print (exactly 2 arguments)
1216
+ if len(stmt.values) == 2:
1217
+ # Conditional print: print(condition, message)
1218
+ stmt.condition = stmt.values[0]
1219
+ stmt.values = [stmt.values[1]]
1220
+ stmt.value = stmt.values[0]
1221
+ else:
1222
+ # Regular print: print(arg) or print(arg1, arg2, arg3, ...)
1223
+ # Keep backward compatibility with .value for single-expression prints
1224
+ stmt.value = stmt.values[0] if len(stmt.values) == 1 else None
1225
+
1226
+ # TOLERANT: Semicolon is optional
1227
+ if self.peek_token_is(SEMICOLON):
1228
+ self.next_token()
1229
+
1230
+ return stmt
1231
+
1232
+ def parse_try_catch_statement(self):
1233
+ """Enhanced try-catch parsing with structural awareness"""
1234
+ try_token = self.cur_token
1235
+ try_block = self.parse_block("try")
1236
+ if not try_block:
1237
+ return None
1238
+
1239
+ if self.cur_token_is(CATCH):
1240
+ pass
1241
+ elif not self.expect_peek(CATCH):
1242
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected 'catch' after try block")
1243
+ return None
1244
+
1245
+ error_var = None
1246
+ if self.peek_token_is(LPAREN):
1247
+ self.next_token()
1248
+ self.next_token()
1249
+ if not self.cur_token_is(IDENT):
1250
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected error variable name after 'catch('")
1251
+ return None
1252
+ error_var = Identifier(self.cur_token.literal)
1253
+ if not self.expect_peek(RPAREN):
1254
+ return None
1255
+ elif self.peek_token_is(IDENT):
1256
+ self.next_token()
1257
+ error_var = Identifier(self.cur_token.literal)
1258
+ else:
1259
+ error_var = Identifier("error")
1260
+
1261
+ catch_block = self.parse_block("catch")
1262
+ if not catch_block:
1263
+ return None
1264
+
1265
+ return TryCatchStatement(
1266
+ try_block=try_block,
1267
+ error_variable=error_var,
1268
+ catch_block=catch_block
1269
+ )
1270
+
1271
+ def parse_debug_statement(self):
1272
+ """Parse debug - dual mode: statement (debug x;) or function call (debug(x) or debug(cond, x))
1273
+
1274
+ When debug is followed by (, it's treated as a function call expression.
1275
+ Supports:
1276
+ - debug(value) - regular debug
1277
+ - debug(condition, value) - conditional debug (exactly 2 args)
1278
+ - debug value; - statement mode
1279
+ """
1280
+
1281
+ # DUAL-MODE: If followed by (, parse as function call with potential conditional
1282
+ if self.peek_token_is(LPAREN):
1283
+ # We need to parse the function call arguments
1284
+ self.next_token() # Move to LPAREN
1285
+ self.next_token() # Move to first argument
1286
+
1287
+ # Collect arguments
1288
+ args = []
1289
+ if not self.cur_token_is(RPAREN):
1290
+ arg = self.parse_expression(LOWEST)
1291
+ if arg:
1292
+ args.append(arg)
1293
+
1294
+ while self.peek_token_is(COMMA):
1295
+ self.next_token() # consume comma
1296
+ self.next_token() # move to next arg
1297
+ arg = self.parse_expression(LOWEST)
1298
+ if arg:
1299
+ args.append(arg)
1300
+
1301
+ # Expect closing paren
1302
+ if not self.peek_token_is(RPAREN):
1303
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected ')' after debug arguments")
1304
+ return None
1305
+ self.next_token() # consume RPAREN
1306
+
1307
+ # Check if conditional debug (2 args) or regular debug (1 arg)
1308
+ if len(args) == 2:
1309
+ # Conditional debug: debug(condition, value)
1310
+ return DebugStatement(value=args[1], condition=args[0])
1311
+ elif len(args) == 1:
1312
+ # Regular debug: debug(value)
1313
+ return DebugStatement(value=args[0])
1314
+ else:
1315
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - debug() requires 1 or 2 arguments")
1316
+ return None
1317
+
1318
+ # Otherwise, it's a debug statement (debug x;)
1319
+ self.next_token()
1320
+ value = self.parse_expression(LOWEST)
1321
+ if not value:
1322
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected expression after 'debug'")
1323
+ return None
1324
+
1325
+ return DebugStatement(value=value)
1326
+
1327
+ def parse_external_declaration(self):
1328
+ token = self.cur_token
1329
+
1330
+ # Support simple syntax: external identifier;
1331
+ if self.peek_token_is(IDENT):
1332
+ self.next_token()
1333
+ name = Identifier(self.cur_token.literal)
1334
+ return ExternalDeclaration(
1335
+ name=name,
1336
+ parameters=[],
1337
+ module_path=""
1338
+ )
1339
+
1340
+ # Full syntax: external action identifier from "module";
1341
+ if not self.expect_peek(ACTION):
1342
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier or 'action' after 'external'")
1343
+ return None
1344
+
1345
+ if not self.expect_peek(IDENT):
1346
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected function name after 'external action'")
1347
+ return None
1348
+
1349
+ name = Identifier(self.cur_token.literal)
1350
+
1351
+ parameters = []
1352
+ if self.peek_token_is(LPAREN):
1353
+ self.next_token()
1354
+ if not self.expect_peek(LPAREN):
1355
+ return None
1356
+ parameters = self.parse_action_parameters()
1357
+ if parameters is None:
1358
+ return None
1359
+
1360
+ if not self.expect_peek(FROM):
1361
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected 'from' after external function declaration")
1362
+ return None
1363
+
1364
+ if not self.expect_peek(STRING):
1365
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected module path string")
1366
+ return None
1367
+
1368
+ module_path = self.cur_token.literal
1369
+
1370
+ return ExternalDeclaration(
1371
+ name=name,
1372
+ parameters=parameters,
1373
+ module_path=module_path
1374
+ )
1375
+
1376
+ def recover_to_next_statement(self):
1377
+ """Tolerant error recovery"""
1378
+ while not self.cur_token_is(EOF):
1379
+ if self.cur_token_is(SEMICOLON):
1380
+ return
1381
+ next_keywords = [LET, RETURN, PRINT, FOR, ACTION, IF, WHILE, USE, EXPORT, DEBUG, TRY, EXTERNAL]
1382
+ if any(self.peek_token_is(kw) for kw in next_keywords):
1383
+ return
1384
+ self.next_token()
1385
+
1386
+ def parse_lambda_expression(self):
1387
+ token = self.cur_token
1388
+ parameters = []
1389
+
1390
+ self.next_token()
1391
+
1392
+ if self.cur_token_is(LPAREN):
1393
+ self.next_token()
1394
+ parameters = self._parse_parameter_list()
1395
+ if not self.expect_peek(RPAREN):
1396
+ return None
1397
+ elif self.cur_token_is(IDENT):
1398
+ parameters = self._parse_parameter_list()
1399
+
1400
+ # Normalize possible separators between parameter list and body
1401
+ # Accept either ':' (keyword-style), '=>' (tokenized as LAMBDA) or '->' style
1402
+ if self.cur_token_is(COLON):
1403
+ # current token is ':', advance to the first token of the body
1404
+ self.next_token()
1405
+ elif self.cur_token_is(MINUS) and self.peek_token_is(GT):
1406
+ # support '->' (legacy), consume both '-' and '>' and advance to body
1407
+ self.next_token()
1408
+ self.next_token()
1409
+ else:
1410
+ # If a colon is the *next* token (peek), consume it and advance past it
1411
+ if self.peek_token_is(COLON):
1412
+ # move to colon
1413
+ self.next_token()
1414
+ # and move to the token after colon (the start of the body)
1415
+ self.next_token()
1416
+ # Otherwise, continue — body parsing will attempt to parse the current token
1417
+
1418
+ body = self.parse_expression(LOWEST)
1419
+ return LambdaExpression(parameters=parameters, body=body)
1420
+
1421
+ def parse_lambda_infix(self, left):
1422
+ """Parse arrow-style lambda when encountering leftside 'params' followed by =>
1423
+
1424
+ Examples:
1425
+ x => x + 1
1426
+ (a, b) => a + b
1427
+ """
1428
+ # Current token is LAMBDA because caller advanced to it
1429
+ # Build parameter list from `left` expression
1430
+ params = []
1431
+ # Single identifier param
1432
+ if isinstance(left, Identifier):
1433
+ params = [left]
1434
+ else:
1435
+ # If left is a grouped expression returning a ListLiteral-like container
1436
+ # we'll attempt to extract identifiers from it (best-effort)
1437
+ try:
1438
+ if hasattr(left, 'elements'):
1439
+ for el in left.elements:
1440
+ if isinstance(el, Identifier):
1441
+ params.append(el)
1442
+ except Exception:
1443
+ pass
1444
+
1445
+ # Consume the LAMBDA token (already current)
1446
+ # The parse loop has already advanced current token to LAMBDA, so
1447
+ # now move to the body
1448
+ self.next_token()
1449
+
1450
+ # Support optional colon or arrow-like separators were handled at lexing stage
1451
+ if self.cur_token_is(COLON):
1452
+ self.next_token()
1453
+
1454
+ body = self.parse_expression(LOWEST)
1455
+ return LambdaExpression(parameters=params, body=body)
1456
+
1457
+ def _parse_parameter_list(self):
1458
+ parameters = []
1459
+
1460
+ if not self.cur_token_is(IDENT):
1461
+ return parameters
1462
+
1463
+ parameters.append(Identifier(self.cur_token.literal))
1464
+
1465
+ while self.peek_token_is(COMMA):
1466
+ self.next_token()
1467
+ self.next_token()
1468
+ if self.cur_token_is(IDENT):
1469
+ parameters.append(Identifier(self.cur_token.literal))
1470
+ else:
1471
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected parameter name")
1472
+ return parameters
1473
+
1474
+ return parameters
1475
+
1476
+ def parse_assignment_expression(self, left):
1477
+ if not isinstance(left, Identifier):
1478
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Cannot assign to {type(left).__name__}, only identifiers allowed")
1479
+ return None
1480
+
1481
+ expression = AssignmentExpression(name=left, value=None)
1482
+ self.next_token()
1483
+ expression.value = self.parse_expression(LOWEST)
1484
+ return expression
1485
+
1486
+ def parse_method_call_expression(self, left):
1487
+ if not self.cur_token_is(DOT):
1488
+ return None
1489
+
1490
+ # After a dot, allow keywords to be used as property/method names
1491
+ # This enables t.verify(), obj.data, etc. even though verify/data are keywords
1492
+ self.next_token()
1493
+
1494
+ # Accept any token with a literal as a property name (IDENT or keywords)
1495
+ # This allows using reserved keywords like 'verify', 'data', 'hash', etc. as property names
1496
+ if self.cur_token.literal:
1497
+ method = Identifier(self.cur_token.literal)
1498
+ else:
1499
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected property/method name after '.'")
1500
+ return None
1501
+
1502
+ if self.peek_token_is(LPAREN):
1503
+ self.next_token()
1504
+ arguments = self.parse_expression_list(RPAREN)
1505
+ return MethodCallExpression(object=left, method=method, arguments=arguments)
1506
+ else:
1507
+ return PropertyAccessExpression(object=left, property=method)
1508
+
1509
+ def parse_export_statement(self):
1510
+ token = self.cur_token
1511
+
1512
+ # Check for syntactic sugar: export action name() {} or export function name() {}
1513
+ if self.peek_token_is(ACTION) or self.peek_token_is(FUNCTION):
1514
+ self.next_token() # Move to ACTION/FUNCTION token
1515
+
1516
+ # Parse the action/function normally
1517
+ if self.cur_token_is(ACTION):
1518
+ func_stmt = self.parse_action_statement()
1519
+ else: # FUNCTION
1520
+ func_stmt = self.parse_function_statement()
1521
+
1522
+ if func_stmt is None:
1523
+ return None
1524
+
1525
+ # Extract the function name for export
1526
+ func_name = func_stmt.name.value if hasattr(func_stmt.name, 'value') else str(func_stmt.name)
1527
+
1528
+ # Create a compound statement: the function definition + export
1529
+ # We'll use a BlockStatement to hold both
1530
+ from ..zexus_ast import BlockStatement, ExportStatement
1531
+ export_stmt = ExportStatement(names=[Identifier(func_name)])
1532
+
1533
+ # Return a block containing both statements
1534
+ return BlockStatement(statements=[func_stmt, export_stmt])
1535
+
1536
+ names = []
1537
+
1538
+ # Support multiple forms: export { a, b }, export(a, b), export a, b ; export a:b; etc.
1539
+ if self.peek_token_is(LBRACE):
1540
+ # export { a, b, c } -- tolerant manual consumption to avoid conflicts with other parsers
1541
+ if not self.expect_peek(LBRACE):
1542
+ return None
1543
+ # move into the first token inside the braces
1544
+ self.next_token()
1545
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
1546
+ if self.cur_token_is(IDENT):
1547
+ names.append(Identifier(self.cur_token.literal))
1548
+ # consume separators if present
1549
+ if self.peek_token_is(COMMA) or self.peek_token_is(SEMICOLON) or self.peek_token_is(COLON):
1550
+ self.next_token() # move to separator
1551
+ self.next_token() # move to token after separator
1552
+ continue
1553
+ # otherwise advance
1554
+ self.next_token()
1555
+ # ensure we've consumed the closing brace
1556
+ if not self.cur_token_is(RBRACE):
1557
+ self.errors.append(f"Line {token.line}:{token.column} - Unterminated export block")
1558
+ return None
1559
+
1560
+ elif self.peek_token_is(LPAREN):
1561
+ # export(a, b) -- tolerant manual parsing of identifiers
1562
+ if not self.expect_peek(LPAREN):
1563
+ return None
1564
+ # move into first token inside parens
1565
+ self.next_token()
1566
+ while not self.cur_token_is(RPAREN) and not self.cur_token_is(EOF):
1567
+ if self.cur_token_is(IDENT):
1568
+ names.append(Identifier(self.cur_token.literal))
1569
+ if self.peek_token_is(COMMA) or self.peek_token_is(SEMICOLON) or self.peek_token_is(COLON):
1570
+ self.next_token()
1571
+ self.next_token()
1572
+ continue
1573
+ self.next_token()
1574
+ if not self.cur_token_is(RPAREN):
1575
+ self.errors.append(f"Line {token.line}:{token.column} - Unterminated export(...)")
1576
+ return None
1577
+
1578
+ else:
1579
+ # Single identifier or comma/sep separated list without braces
1580
+ if not self.expect_peek(IDENT):
1581
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'export'")
1582
+ return None
1583
+ names.append(Identifier(self.cur_token.literal))
1584
+ # allow subsequent separators
1585
+ while self.peek_token_is(COMMA) or self.peek_token_is(SEMICOLON) or self.peek_token_is(COLON):
1586
+ self.next_token()
1587
+ if not self.expect_peek(IDENT):
1588
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after separator in export")
1589
+ return None
1590
+ names.append(Identifier(self.cur_token.literal))
1591
+
1592
+ # After names, optionally parse `to` allowed_files and `with` permission
1593
+ allowed_files = []
1594
+ if self.peek_token_is(IDENT) and self.peek_token.literal == "to":
1595
+ self.next_token()
1596
+ self.next_token()
1597
+
1598
+ if not self.peek_token_is(STRING):
1599
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected file path after 'to'")
1600
+ return None
1601
+
1602
+ while self.peek_token_is(STRING):
1603
+ self.next_token()
1604
+ allowed_files.append(self.cur_token.literal)
1605
+ if self.peek_token_is(COMMA):
1606
+ self.next_token()
1607
+ else:
1608
+ break
1609
+
1610
+ permission = "read_only"
1611
+ if self.peek_token_is(IDENT) and self.peek_token.literal == "with":
1612
+ self.next_token()
1613
+ self.next_token()
1614
+
1615
+ if self.cur_token_is(STRING):
1616
+ permission = self.cur_token.literal
1617
+ else:
1618
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected permission string after 'with'")
1619
+ return None
1620
+
1621
+ return ExportStatement(names=names, allowed_files=allowed_files, permission=permission)
1622
+
1623
+ def parse_seal_statement(self):
1624
+ """Parse seal statement to mark a variable/object as immutable.
1625
+
1626
+ Syntax: seal identifier
1627
+ """
1628
+ token = self.cur_token
1629
+
1630
+ if not self.expect_peek(IDENT):
1631
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'seal'")
1632
+ return None
1633
+
1634
+ target = Identifier(self.cur_token.literal)
1635
+ return SealStatement(target=target)
1636
+
1637
+ def parse_audit_statement(self):
1638
+ """Parse audit statement for compliance logging.
1639
+
1640
+ Syntax: audit data_name, "action_type", [optional_timestamp];
1641
+ Examples:
1642
+ audit user_data, "access", timestamp;
1643
+ audit CONFIG, "modification", now;
1644
+ """
1645
+ token = self.cur_token
1646
+
1647
+ # Expect identifier (data to audit)
1648
+ if not self.expect_peek(IDENT):
1649
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'audit'")
1650
+ return None
1651
+
1652
+ data_name = Identifier(self.cur_token.literal)
1653
+
1654
+ # Expect comma
1655
+ if not self.expect_peek(COMMA):
1656
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected ',' after data identifier in audit statement")
1657
+ return None
1658
+
1659
+ # Expect action type (string literal)
1660
+ if not self.expect_peek(STRING):
1661
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected action type string in audit statement")
1662
+ return None
1663
+
1664
+ action_type = StringLiteral(self.cur_token.literal)
1665
+
1666
+ # Optional: timestamp
1667
+ timestamp = None
1668
+ if self.peek_token_is(COMMA):
1669
+ self.next_token() # consume comma
1670
+ if not self.expect_peek(IDENT):
1671
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected timestamp identifier after comma in audit statement")
1672
+ return None
1673
+ timestamp = Identifier(self.cur_token.literal)
1674
+
1675
+ # Expect semicolon
1676
+ if self.peek_token_is(SEMICOLON):
1677
+ self.next_token()
1678
+
1679
+ return AuditStatement(data_name=data_name, action_type=action_type, timestamp=timestamp)
1680
+
1681
+ def parse_restrict_statement(self):
1682
+ """Parse restrict statement for field-level access control.
1683
+
1684
+ Syntax: restrict obj.field = "restriction_type";
1685
+ Examples:
1686
+ restrict user.password = "deny";
1687
+ restrict config.api_key = "admin-only";
1688
+ restrict data.sensitive = "read-only";
1689
+ """
1690
+ token = self.cur_token
1691
+
1692
+ # Expect identifier.field pattern
1693
+ if not self.expect_peek(IDENT):
1694
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'restrict'")
1695
+ return None
1696
+
1697
+ obj_name = Identifier(self.cur_token.literal)
1698
+
1699
+ # Expect dot
1700
+ if not self.expect_peek(DOT):
1701
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '.' after identifier in restrict statement")
1702
+ return None
1703
+
1704
+ # Expect field name
1705
+ if not self.expect_peek(IDENT):
1706
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected field name after '.' in restrict statement")
1707
+ return None
1708
+
1709
+ field_name = Identifier(self.cur_token.literal)
1710
+ target = PropertyAccessExpression(obj_name, field_name)
1711
+
1712
+ # Expect assignment
1713
+ if not self.expect_peek(ASSIGN):
1714
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '=' in restrict statement")
1715
+ return None
1716
+
1717
+ # Expect restriction type (string literal)
1718
+ if not self.expect_peek(STRING):
1719
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected restriction type string in restrict statement")
1720
+ return None
1721
+
1722
+ restriction_type = StringLiteral(self.cur_token.literal)
1723
+
1724
+ # Expect semicolon
1725
+ if self.peek_token_is(SEMICOLON):
1726
+ self.next_token()
1727
+
1728
+ return RestrictStatement(target=target, restriction_type=restriction_type)
1729
+
1730
+ def parse_sandbox_statement(self):
1731
+ """Parse sandbox statement for isolated execution environments.
1732
+
1733
+ Syntax: sandbox { code }
1734
+ Example:
1735
+ sandbox {
1736
+ let result = unsafe_operation();
1737
+ let data = risky_function();
1738
+ }
1739
+ """
1740
+ token = self.cur_token
1741
+
1742
+ policy_name = None
1743
+
1744
+ # Optional policy in parentheses: sandbox (policy = "name") { ... } or sandbox ("name") { ... }
1745
+ if self.peek_token_is(LPAREN):
1746
+ self.next_token() # consume LPAREN
1747
+ # Accept either IDENT ASSIGN STRING or just STRING
1748
+ if self.peek_token_is(IDENT):
1749
+ self.next_token()
1750
+ if self.cur_token_is(IDENT) and self.peek_token_is(ASSIGN):
1751
+ # consume ASSIGN
1752
+ self.next_token()
1753
+ if not self.expect_peek(STRING):
1754
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected policy string in sandbox()")
1755
+ return None
1756
+ policy_name = self.cur_token.literal
1757
+ else:
1758
+ # treat the identifier as policy name
1759
+ policy_name = self.cur_token.literal
1760
+ elif self.peek_token_is(STRING):
1761
+ self.next_token()
1762
+ policy_name = self.cur_token.literal
1763
+ else:
1764
+ # tolerate empty or unexpected
1765
+ pass
1766
+
1767
+ # expect closing paren
1768
+ if not self.expect_peek(RPAREN):
1769
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after sandbox policy")
1770
+ return None
1771
+
1772
+ # Expect opening brace
1773
+ if not self.expect_peek(LBRACE):
1774
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '{{' after 'sandbox'")
1775
+ return None
1776
+
1777
+ # Parse block body
1778
+ body = self.parse_block("sandbox")
1779
+ if body is None:
1780
+ return None
1781
+
1782
+ return SandboxStatement(body=body, policy=policy_name)
1783
+
1784
+ def parse_trail_statement(self):
1785
+ """Parse trail statement for real-time audit/debug/print tracking.
1786
+
1787
+ Syntax:
1788
+ trail audit; // follow all audit events
1789
+ trail print; // follow all print statements
1790
+ trail debug; // follow all debug output
1791
+ trail *, "pattern"; // trail all with filter
1792
+ """
1793
+ token = self.cur_token
1794
+
1795
+ # Expect trail type (audit, print, debug, or *)
1796
+ if not self.expect_peek(IDENT):
1797
+ if not self.cur_token_is(STAR):
1798
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected trail type (audit, print, debug, or *)")
1799
+ return None
1800
+ trail_type = "*"
1801
+ else:
1802
+ trail_type = self.cur_token.literal
1803
+
1804
+ # Optional filter
1805
+ filter_key = None
1806
+ if self.peek_token_is(COMMA):
1807
+ self.next_token() # consume comma
1808
+ if not self.expect_peek(STRING):
1809
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected filter string after comma in trail statement")
1810
+ return None
1811
+ filter_key = StringLiteral(self.cur_token.literal)
1812
+
1813
+ # Expect semicolon
1814
+ if self.peek_token_is(SEMICOLON):
1815
+ self.next_token()
1816
+
1817
+ return TrailStatement(trail_type=trail_type, filter_key=filter_key)
1818
+
1819
+ def parse_tx_statement(self):
1820
+ """Parse transaction block statement.
1821
+
1822
+ Syntax:
1823
+ tx {
1824
+ balance = balance - amount;
1825
+ recipient_balance = recipient_balance + amount;
1826
+ }
1827
+ """
1828
+ # Consume 'tx' keyword
1829
+ self.next_token()
1830
+
1831
+ # Expect opening brace
1832
+ if not self.cur_token_is(LBRACE):
1833
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '{{' after 'tx'")
1834
+ return None
1835
+
1836
+ # Parse block body
1837
+ body = self.parse_block_statement()
1838
+
1839
+ if body is None:
1840
+ return None
1841
+
1842
+ return TxStatement(body=body)
1843
+
1844
+ def parse_native_statement(self):
1845
+ """Parse native statement for calling C/C++ code.
1846
+
1847
+ Syntax:
1848
+ native "libmath.so", "add_numbers"(x, y);
1849
+ native "libcrypto.so", "sha256"(data) as hash;
1850
+ """
1851
+ token = self.cur_token
1852
+
1853
+ # Expect library name (string)
1854
+ if not self.expect_peek(STRING):
1855
+ self.errors.append(f"Line {token.line}:{token.column} - Expected library name string after 'native'")
1856
+ return None
1857
+ library_name = self.cur_token.literal
1858
+
1859
+ # Expect comma
1860
+ if not self.expect_peek(COMMA):
1861
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ',' after library name in native statement")
1862
+ return None
1863
+
1864
+ # Expect function name (string)
1865
+ if not self.expect_peek(STRING):
1866
+ self.errors.append(f"Line {token.line}:{token.column} - Expected function name string after comma in native statement")
1867
+ return None
1868
+ function_name = self.cur_token.literal
1869
+
1870
+ # Expect opening paren for arguments
1871
+ if not self.expect_peek(LPAREN):
1872
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after function name in native statement")
1873
+ return None
1874
+
1875
+ # Parse arguments
1876
+ args = []
1877
+ if not self.peek_token_is(RPAREN):
1878
+ self.next_token()
1879
+ args.append(self.parse_expression(LOWEST))
1880
+ while self.peek_token_is(COMMA):
1881
+ self.next_token()
1882
+ self.next_token()
1883
+ args.append(self.parse_expression(LOWEST))
1884
+
1885
+ # Expect closing paren
1886
+ if not self.expect_peek(RPAREN):
1887
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after arguments in native statement")
1888
+ return None
1889
+
1890
+ # Optional: as alias
1891
+ alias = None
1892
+ if self.peek_token_is(AS):
1893
+ self.next_token()
1894
+ if not self.expect_peek(IDENT):
1895
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'as' in native statement")
1896
+ return None
1897
+ alias = self.cur_token.literal
1898
+
1899
+ # Optional semicolon
1900
+ if self.peek_token_is(SEMICOLON):
1901
+ self.next_token()
1902
+
1903
+ return NativeStatement(library_name, function_name, args, alias)
1904
+
1905
+ def parse_gc_statement(self):
1906
+ """Parse garbage collection statement.
1907
+
1908
+ Syntax:
1909
+ gc "collect";
1910
+ gc "pause";
1911
+ gc "resume";
1912
+ """
1913
+ token = self.cur_token
1914
+
1915
+ # Expect GC action (string)
1916
+ if not self.expect_peek(STRING):
1917
+ self.errors.append(f"Line {token.line}:{token.column} - Expected GC action string after 'gc'")
1918
+ return None
1919
+ action = self.cur_token.literal
1920
+
1921
+ # Optional semicolon
1922
+ if self.peek_token_is(SEMICOLON):
1923
+ self.next_token()
1924
+
1925
+ return GCStatement(action)
1926
+
1927
+ def parse_inline_statement(self):
1928
+ """Parse inline statement for function inlining optimization.
1929
+
1930
+ Syntax:
1931
+ inline my_function;
1932
+ inline critical_func;
1933
+ """
1934
+ token = self.cur_token
1935
+
1936
+ # Expect function name (identifier)
1937
+ if not self.expect_peek(IDENT):
1938
+ self.errors.append(f"Line {token.line}:{token.column} - Expected function name after 'inline'")
1939
+ return None
1940
+ function_name = self.cur_token.literal
1941
+
1942
+ # Optional semicolon
1943
+ if self.peek_token_is(SEMICOLON):
1944
+ self.next_token()
1945
+
1946
+ return InlineStatement(function_name)
1947
+
1948
+ def parse_buffer_statement(self):
1949
+ """Parse buffer statement for direct memory access.
1950
+
1951
+ Syntax:
1952
+ buffer my_mem = allocate(1024);
1953
+ buffer my_mem.write(0, [1, 2, 3]);
1954
+ buffer my_mem.read(0, 4);
1955
+ """
1956
+ token = self.cur_token
1957
+
1958
+ # Expect buffer name (identifier)
1959
+ if not self.expect_peek(IDENT):
1960
+ self.errors.append(f"Line {token.line}:{token.column} - Expected buffer name after 'buffer'")
1961
+ return None
1962
+ buffer_name = self.cur_token.literal
1963
+
1964
+ # Optional operation (= allocate, .write, .read, etc.)
1965
+ operation = None
1966
+ arguments = []
1967
+
1968
+ if self.peek_token_is(ASSIGN):
1969
+ self.next_token()
1970
+ # Expect allocate(...) or other operation
1971
+ if not self.expect_peek(IDENT):
1972
+ self.errors.append(f"Line {token.line}:{token.column} - Expected operation after '=' in buffer statement")
1973
+ return None
1974
+ operation = self.cur_token.literal
1975
+
1976
+ # Expect opening paren
1977
+ if not self.expect_peek(LPAREN):
1978
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after operation in buffer statement")
1979
+ return None
1980
+
1981
+ # Parse arguments
1982
+ if not self.peek_token_is(RPAREN):
1983
+ self.next_token()
1984
+ arguments.append(self.parse_expression(LOWEST))
1985
+ while self.peek_token_is(COMMA):
1986
+ self.next_token()
1987
+ self.next_token()
1988
+ arguments.append(self.parse_expression(LOWEST))
1989
+
1990
+ # Expect closing paren
1991
+ if not self.expect_peek(RPAREN):
1992
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after arguments in buffer statement")
1993
+ return None
1994
+
1995
+ elif self.peek_token_is(DOT):
1996
+ self.next_token()
1997
+ # Expect method name (read, write, free, etc.)
1998
+ if not self.expect_peek(IDENT):
1999
+ self.errors.append(f"Line {token.line}:{token.column} - Expected method name after '.' in buffer statement")
2000
+ return None
2001
+ operation = self.cur_token.literal
2002
+
2003
+ # Expect opening paren
2004
+ if not self.expect_peek(LPAREN):
2005
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after method in buffer statement")
2006
+ return None
2007
+
2008
+ # Parse arguments
2009
+ if not self.peek_token_is(RPAREN):
2010
+ self.next_token()
2011
+ arguments.append(self.parse_expression(LOWEST))
2012
+ while self.peek_token_is(COMMA):
2013
+ self.next_token()
2014
+ self.next_token()
2015
+ arguments.append(self.parse_expression(LOWEST))
2016
+
2017
+ # Expect closing paren
2018
+ if not self.expect_peek(RPAREN):
2019
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after arguments in buffer statement")
2020
+ return None
2021
+
2022
+ # Optional semicolon
2023
+ if self.peek_token_is(SEMICOLON):
2024
+ self.next_token()
2025
+
2026
+ return BufferStatement(buffer_name, operation, arguments)
2027
+
2028
+ def parse_simd_statement(self):
2029
+ """Parse SIMD statement for vector operations.
2030
+
2031
+ Syntax:
2032
+ simd vector1 + vector2;
2033
+ simd matrix_mul(A, B);
2034
+ simd dot_product([1,2,3], [4,5,6]);
2035
+ """
2036
+ token = self.cur_token
2037
+
2038
+ # Parse SIMD operation expression
2039
+ self.next_token()
2040
+ operation = self.parse_expression(LOWEST)
2041
+
2042
+ if operation is None:
2043
+ self.errors.append(f"Line {token.line}:{token.column} - Expected expression in SIMD statement")
2044
+ return None
2045
+
2046
+ # Optional semicolon
2047
+ if self.peek_token_is(SEMICOLON):
2048
+ self.next_token()
2049
+
2050
+ return SIMDStatement(operation)
2051
+
2052
+ def parse_defer_statement(self):
2053
+ """Parse defer statement - cleanup code execution.
2054
+
2055
+ Syntax:
2056
+ defer close_file();
2057
+ defer cleanup();
2058
+ defer { cleanup1(); cleanup2(); }
2059
+ """
2060
+ token = self.cur_token
2061
+
2062
+ # Parse deferred code (expression or block)
2063
+ if self.peek_token_is(LBRACE):
2064
+ self.next_token()
2065
+ code_block = self.parse_block("defer")
2066
+ else:
2067
+ self.next_token()
2068
+ code_block = self.parse_expression(LOWEST)
2069
+
2070
+ if code_block is None:
2071
+ self.errors.append(f"Line {token.line}:{token.column} - Expected code after 'defer'")
2072
+ return None
2073
+
2074
+ # Optional semicolon
2075
+ if self.peek_token_is(SEMICOLON):
2076
+ self.next_token()
2077
+
2078
+ return DeferStatement(code_block)
2079
+
2080
+ def parse_pattern_statement(self):
2081
+ """Parse pattern statement - pattern matching.
2082
+
2083
+ Syntax:
2084
+ pattern value {
2085
+ case 1 => print "one";
2086
+ case 2 => print "two";
2087
+ default => print "other";
2088
+ }
2089
+ """
2090
+ token = self.cur_token
2091
+
2092
+ # Parse expression to match against
2093
+ if not self.expect_peek(IDENT):
2094
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'pattern'")
2095
+ return None
2096
+ expression = Identifier(self.cur_token.literal)
2097
+
2098
+ # Expect opening brace
2099
+ if not self.expect_peek(LBRACE):
2100
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '{{' after pattern expression")
2101
+ return None
2102
+
2103
+ # Parse pattern cases
2104
+ cases = []
2105
+ self.next_token()
2106
+
2107
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
2108
+ # Expect 'case' or 'default'
2109
+ if self.cur_token.literal == "case":
2110
+ self.next_token()
2111
+ pattern = self.parse_expression(LOWEST)
2112
+
2113
+ # Expect '=>'
2114
+ if not self.expect_peek(ASSIGN): # Using = as stand-in for =>
2115
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '=>' in pattern case")
2116
+ return None
2117
+
2118
+ self.next_token()
2119
+ action = self.parse_expression(LOWEST)
2120
+
2121
+ cases.append(PatternCase(pattern, action))
2122
+
2123
+ # Optional semicolon
2124
+ if self.peek_token_is(SEMICOLON):
2125
+ self.next_token()
2126
+
2127
+ elif self.cur_token.literal == "default":
2128
+ self.next_token()
2129
+
2130
+ # Expect '=>'
2131
+ if not self.expect_peek(ASSIGN):
2132
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '=>' in default case")
2133
+ return None
2134
+
2135
+ self.next_token()
2136
+ action = self.parse_expression(LOWEST)
2137
+
2138
+ cases.append(PatternCase("default", action))
2139
+
2140
+ # Optional semicolon
2141
+ if self.peek_token_is(SEMICOLON):
2142
+ self.next_token()
2143
+
2144
+ break # Default should be last
2145
+
2146
+ self.next_token()
2147
+
2148
+ # Expect closing brace
2149
+ if not self.cur_token_is(RBRACE):
2150
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '}}' after pattern cases")
2151
+ return None
2152
+
2153
+ return PatternStatement(expression, cases)
2154
+
2155
+ def parse_enum_statement(self):
2156
+ """Parse enum statement - type-safe enumerations.
2157
+
2158
+ Syntax:
2159
+ enum Color {
2160
+ Red,
2161
+ Green,
2162
+ Blue
2163
+ }
2164
+
2165
+ enum Status {
2166
+ Active = 1,
2167
+ Inactive = 2
2168
+ }
2169
+ """
2170
+ token = self.cur_token
2171
+
2172
+ # Expect enum name
2173
+ if not self.expect_peek(IDENT):
2174
+ self.errors.append(f"Line {token.line}:{token.column} - Expected enum name after 'enum'")
2175
+ return None
2176
+ enum_name = self.cur_token.literal
2177
+
2178
+ # Expect opening brace
2179
+ if not self.expect_peek(LBRACE):
2180
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '{{' after enum name")
2181
+ return None
2182
+
2183
+ # Parse enum members
2184
+ members = []
2185
+ self.next_token()
2186
+
2187
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
2188
+ if self.cur_token_is(IDENT):
2189
+ member_name = self.cur_token.literal
2190
+ member_value = None
2191
+
2192
+ # Optional: = value
2193
+ if self.peek_token_is(ASSIGN):
2194
+ self.next_token()
2195
+ self.next_token()
2196
+ if self.cur_token_is(INT):
2197
+ member_value = int(self.cur_token.literal)
2198
+ elif self.cur_token_is(STRING):
2199
+ member_value = self.cur_token.literal
2200
+
2201
+ members.append(EnumMember(member_name, member_value))
2202
+
2203
+ # Skip comma and continue
2204
+ if self.peek_token_is(COMMA):
2205
+ self.next_token()
2206
+
2207
+ self.next_token()
2208
+
2209
+ # Expect closing brace
2210
+ if not self.cur_token_is(RBRACE):
2211
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '}}' after enum members")
2212
+ return None
2213
+
2214
+ return EnumStatement(enum_name, members)
2215
+
2216
+ def parse_stream_statement(self):
2217
+ """Parse stream statement - event streaming.
2218
+
2219
+ Syntax:
2220
+ stream clicks as event => {
2221
+ print "Clicked: " + event.x;
2222
+ }
2223
+ """
2224
+ token = self.cur_token
2225
+
2226
+ # Expect stream name
2227
+ if not self.expect_peek(IDENT):
2228
+ self.errors.append(f"Line {token.line}:{token.column} - Expected stream name after 'stream'")
2229
+ return None
2230
+ stream_name = self.cur_token.literal
2231
+
2232
+ # Expect 'as'
2233
+ if not self.expect_peek(AS):
2234
+ self.errors.append(f"Line {token.line}:{token.column} - Expected 'as' after stream name")
2235
+ return None
2236
+
2237
+ # Expect event variable name
2238
+ if not self.expect_peek(IDENT):
2239
+ self.errors.append(f"Line {token.line}:{token.column} - Expected event variable name")
2240
+ return None
2241
+ event_var = Identifier(self.cur_token.literal)
2242
+
2243
+ # Expect '=>'
2244
+ if not self.expect_peek(ASSIGN): # Using = as stand-in for =>
2245
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '=>' after event variable")
2246
+ return None
2247
+
2248
+ # Expect block
2249
+ if not self.expect_peek(LBRACE):
2250
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '{{' for stream handler")
2251
+ return None
2252
+
2253
+ handler = self.parse_block("stream")
2254
+ if handler is None:
2255
+ return None
2256
+
2257
+ return StreamStatement(stream_name, event_var, handler)
2258
+
2259
+ def parse_watch_statement(self):
2260
+ """Parse watch statement - reactive state management.
2261
+
2262
+ Syntax:
2263
+ watch user_name => {
2264
+ update_ui();
2265
+ }
2266
+
2267
+ watch count => print("Count: " + count);
2268
+ """
2269
+ token = self.cur_token
2270
+
2271
+ # Parse watched expression
2272
+ self.next_token()
2273
+ watched_expr = self.parse_expression(LOWEST)
2274
+
2275
+ if watched_expr is None:
2276
+ self.errors.append(f"Line {token.line}:{token.column} - Expected expression after 'watch'")
2277
+ return None
2278
+
2279
+ # Expect '=>' (LAMBDA token)
2280
+ if not self.expect_peek(LAMBDA):
2281
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '=>' in watch statement")
2282
+ return None
2283
+
2284
+ # Parse reaction (block or expression)
2285
+ if self.peek_token_is(LBRACE):
2286
+ self.next_token()
2287
+ reaction = self.parse_block("watch")
2288
+ else:
2289
+ self.next_token()
2290
+ reaction = self.parse_expression(LOWEST)
2291
+
2292
+ if reaction is None:
2293
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected reaction after '=>'")
2294
+ return None
2295
+
2296
+ return WatchStatement(reaction=reaction, watched_expr=watched_expr)
2297
+
2298
+ def parse_embedded_literal(self):
2299
+ if not self.expect_peek(LBRACE):
2300
+ return None
2301
+
2302
+ self.next_token()
2303
+ code_content = self.read_embedded_code_content()
2304
+ if code_content is None:
2305
+ return None
2306
+
2307
+ lines = code_content.strip().split('\n')
2308
+ if not lines:
2309
+ self.errors.append("Empty embedded code block")
2310
+ return None
2311
+
2312
+ language_line = lines[0].strip()
2313
+ language = language_line if language_line else "unknown"
2314
+ code = '\n'.join(lines[1:]).strip() if len(lines) > 1 else ""
2315
+ return EmbeddedLiteral(language=language, code=code)
2316
+
2317
+ def read_embedded_code_content(self):
2318
+ start_position = self.lexer.position
2319
+ brace_count = 1
2320
+
2321
+ while brace_count > 0 and not self.cur_token_is(EOF):
2322
+ self.next_token()
2323
+ if self.cur_token_is(LBRACE):
2324
+ brace_count += 1
2325
+ elif self.cur_token_is(RBRACE):
2326
+ brace_count -= 1
2327
+
2328
+ if self.cur_token_is(EOF):
2329
+ self.errors.append("Unclosed embedded code block")
2330
+ return None
2331
+
2332
+ end_position = self.lexer.position - len(self.cur_token.literal)
2333
+ content = self.lexer.input[start_position:end_position].strip()
2334
+ return content
2335
+
2336
+ def parse_exactly_statement(self):
2337
+ if not self.expect_peek(IDENT):
2338
+ return None
2339
+
2340
+ name = Identifier(self.cur_token.literal)
2341
+
2342
+ if not self.expect_peek(LBRACE):
2343
+ return None
2344
+
2345
+ body = self.parse_block_statement()
2346
+ return ExactlyStatement(name=name, body=body)
2347
+
2348
+ def parse_for_each_statement(self):
2349
+ stmt = ForEachStatement(item=None, iterable=None, body=None)
2350
+
2351
+ if not self.expect_peek(EACH):
2352
+ self.errors.append("Expected 'each' after 'for' in for-each loop")
2353
+ return None
2354
+
2355
+ if not self.expect_peek(IDENT):
2356
+ self.errors.append("Expected identifier after 'each' in for-each loop")
2357
+ return None
2358
+
2359
+ stmt.item = Identifier(value=self.cur_token.literal)
2360
+
2361
+ if not self.expect_peek(IN):
2362
+ self.errors.append("Expected 'in' after item identifier in for-each loop")
2363
+ return None
2364
+
2365
+ self.next_token()
2366
+ stmt.iterable = self.parse_expression(LOWEST)
2367
+
2368
+ body = self.parse_block("for-each")
2369
+ if not body:
2370
+ return None
2371
+
2372
+ stmt.body = body
2373
+ return stmt
2374
+
2375
+ def parse_action_parameters(self):
2376
+ params = []
2377
+
2378
+ # Normalize several possible entry points: caller may call this
2379
+ # with cur_token at LPAREN, at the first parameter, or at RPAREN.
2380
+ if self.cur_token_is(LPAREN):
2381
+ # advance to the token after '('
2382
+ self.next_token()
2383
+
2384
+ # If we are immediately at ')' then it's an empty parameter list
2385
+ if self.cur_token_is(RPAREN):
2386
+ self.next_token()
2387
+ return params
2388
+
2389
+ # Now expect an identifier for the first parameter
2390
+ if not self.cur_token_is(IDENT):
2391
+ self.errors.append("Expected parameter name")
2392
+ return None
2393
+
2394
+ params.append(Identifier(self.cur_token.literal))
2395
+
2396
+ # Skip optional type annotation: : type
2397
+ if self.peek_token_is(COLON):
2398
+ self.next_token() # Move to :
2399
+ self.next_token() # Move to type (skip it)
2400
+
2401
+ while self.peek_token_is(COMMA):
2402
+ self.next_token()
2403
+ self.next_token()
2404
+ if not self.cur_token_is(IDENT):
2405
+ self.errors.append("Expected parameter name after comma")
2406
+ return None
2407
+ params.append(Identifier(self.cur_token.literal))
2408
+
2409
+ # Skip optional type annotation: : type
2410
+ if self.peek_token_is(COLON):
2411
+ self.next_token() # Move to :
2412
+ self.next_token() # Move to type (skip it)
2413
+
2414
+ if not self.expect_peek(RPAREN):
2415
+ self.errors.append("Expected ')' after parameters")
2416
+ return None
2417
+
2418
+ return params
2419
+
2420
+ def parse_action_literal(self):
2421
+ if not self.expect_peek(LPAREN):
2422
+ return None
2423
+
2424
+ parameters = self.parse_action_parameters()
2425
+ if parameters is None:
2426
+ return None
2427
+
2428
+ if not self.expect_peek(COLON):
2429
+ return None
2430
+
2431
+ body = BlockStatement()
2432
+ self.next_token()
2433
+ stmt = self.parse_statement()
2434
+ if stmt:
2435
+ body.statements.append(stmt)
2436
+
2437
+ return ActionLiteral(parameters=parameters, body=body)
2438
+
2439
+ def parse_function_literal(self):
2440
+ """Parse function literal expression: function(params) { body } or function(params) : stmt
2441
+ Returns an ActionLiteral which is compatible with function execution"""
2442
+ if not self.expect_peek(LPAREN):
2443
+ return None
2444
+
2445
+ parameters = self.parse_action_parameters()
2446
+ if parameters is None:
2447
+ return None
2448
+
2449
+ # After parse_action_parameters, cur_token is already at the token after ')'
2450
+ # Check for colon (traditional action-style) or curly brace (function-style)
2451
+ if self.cur_token_is(COLON):
2452
+ # Traditional action style: function(x) : stmt
2453
+ body = BlockStatement()
2454
+ self.next_token()
2455
+ stmt = self.parse_statement()
2456
+ if stmt:
2457
+ body.statements.append(stmt)
2458
+
2459
+ return ActionLiteral(parameters=parameters, body=body)
2460
+
2461
+ elif self.cur_token_is(LBRACE):
2462
+ # Function-style with braces: function(x) { stmts }
2463
+ # cur_token is already at {, so parse the brace block directly
2464
+ body = self.parse_brace_block()
2465
+ if not body:
2466
+ return None
2467
+ return ActionLiteral(parameters=parameters, body=body)
2468
+
2469
+ # Also handle peek variants for backwards compatibility if cur_token is still at )
2470
+ elif self.peek_token_is(COLON):
2471
+ if not self.expect_peek(COLON):
2472
+ return None
2473
+ body = BlockStatement()
2474
+ self.next_token()
2475
+ stmt = self.parse_statement()
2476
+ if stmt:
2477
+ body.statements.append(stmt)
2478
+ return ActionLiteral(parameters=parameters, body=body)
2479
+
2480
+ elif self.peek_token_is(LBRACE):
2481
+ self.next_token() # Move to {
2482
+ body = self.parse_brace_block()
2483
+ if not body:
2484
+ return None
2485
+ return ActionLiteral(parameters=parameters, body=body)
2486
+
2487
+ else:
2488
+ self.errors.append("Expected ':' or '{' after function parameters")
2489
+ return None
2490
+
2491
+ def parse_while_statement(self):
2492
+ """Tolerant while statement parser (with or without parentheses)"""
2493
+ self.next_token() # Move past WHILE token
2494
+
2495
+ # Parse condition (with or without parentheses)
2496
+ if self.cur_token_is(LPAREN):
2497
+ self.next_token() # Skip (
2498
+ condition = self.parse_expression(LOWEST)
2499
+ # After parse_expression, check if RPAREN is current or peek token
2500
+ if self.cur_token_is(RPAREN):
2501
+ self.next_token() # Skip ) - it's already in cur_token
2502
+ elif self.peek_token_is(RPAREN):
2503
+ self.next_token() # Advance to )
2504
+ self.next_token() # Skip )
2505
+ else:
2506
+ self.errors.append("Expected ')' after while condition")
2507
+ return None
2508
+ else:
2509
+ # No parentheses - parse expression directly
2510
+ condition = self.parse_expression(LOWEST)
2511
+
2512
+ if not condition:
2513
+ self.errors.append("Expected condition after 'while'")
2514
+ return None
2515
+
2516
+ body = self.parse_block("while")
2517
+ if not body:
2518
+ return None
2519
+
2520
+ return WhileStatement(condition=condition, body=body)
2521
+
2522
+ def parse_use_statement(self):
2523
+ """Enhanced use statement parser that handles multiple syntax styles"""
2524
+ token = self.cur_token
2525
+
2526
+ # Check for brace syntax: use { Name1, Name2 } from './module.zx'
2527
+ if self.peek_token_is(LBRACE):
2528
+ return self.parse_use_with_braces()
2529
+ else:
2530
+ return self.parse_use_simple()
2531
+
2532
+ def parse_use_with_braces(self):
2533
+ """Parse use statement with brace syntax: use { Name1, Name2 } from './module.zx'"""
2534
+
2535
+ if not self.expect_peek(LBRACE):
2536
+ return None
2537
+
2538
+ names = []
2539
+
2540
+ # Parse names inside braces
2541
+ self.next_token() # Move past {
2542
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
2543
+ if self.cur_token_is(IDENT):
2544
+ names.append(Identifier(self.cur_token.literal))
2545
+
2546
+ # Handle commas
2547
+ if self.peek_token_is(COMMA):
2548
+ self.next_token() # Skip comma
2549
+ elif not self.peek_token_is(RBRACE):
2550
+ # If not comma or closing brace, it's probably an error but try to continue
2551
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected ',' or '}}' in use statement")
2552
+
2553
+ self.next_token()
2554
+
2555
+ if not self.cur_token_is(RBRACE):
2556
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '}}' in use statement")
2557
+ return None
2558
+
2559
+ # Expect 'from' after closing brace
2560
+ if not self.expect_peek(IDENT) or self.cur_token.literal != "from":
2561
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected 'from' after import names")
2562
+ return None
2563
+
2564
+ # Expect file path string
2565
+ if not self.expect_peek(STRING):
2566
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected file path after 'from'")
2567
+ return None
2568
+
2569
+ file_path = self.cur_token.literal
2570
+
2571
+ return UseStatement(file_path=file_path, names=names, is_named_import=True)
2572
+
2573
+ def parse_use_simple(self):
2574
+ """Parse simple use statement: use './file.zx' as alias"""
2575
+ if not self.expect_peek(STRING):
2576
+ self.errors.append("Expected file path after 'use'")
2577
+ return None
2578
+
2579
+ file_path = self.cur_token.literal
2580
+
2581
+ alias = None
2582
+ if self.peek_token_is(IDENT) and self.peek_token.literal == "as":
2583
+ self.next_token()
2584
+ self.next_token()
2585
+ if not self.expect_peek(IDENT):
2586
+ self.errors.append("Expected alias name after 'as'")
2587
+ return None
2588
+ alias = self.cur_token.literal
2589
+
2590
+ return UseStatement(file_path=file_path, alias=alias, is_named_import=False)
2591
+
2592
+ def parse_screen_statement(self):
2593
+ stmt = ScreenStatement(name=None, body=None)
2594
+ if not self.expect_peek(IDENT):
2595
+ self.errors.append("Expected screen name after 'screen'")
2596
+ return None
2597
+
2598
+ stmt.name = Identifier(value=self.cur_token.literal)
2599
+
2600
+ if not self.expect_peek(LBRACE):
2601
+ self.errors.append("Expected '{' after screen name")
2602
+ return None
2603
+
2604
+ stmt.body = self.parse_block_statement()
2605
+ return stmt
2606
+
2607
+ def parse_return_statement(self):
2608
+ stmt = ReturnStatement(return_value=None)
2609
+ self.next_token()
2610
+ stmt.return_value = self.parse_expression(LOWEST)
2611
+ return stmt
2612
+
2613
+ def parse_continue_statement(self):
2614
+ """Parse CONTINUE statement - enables error recovery mode."""
2615
+ stmt = ContinueStatement()
2616
+ self.next_token() # consume CONTINUE token
2617
+ return stmt
2618
+
2619
+ def parse_break_statement(self):
2620
+ """Parse BREAK statement - exits current loop."""
2621
+ stmt = BreakStatement()
2622
+ self.next_token() # consume BREAK token
2623
+ return stmt
2624
+
2625
+ def parse_throw_statement(self):
2626
+ """Parse THROW statement - throws an error."""
2627
+ self.next_token() # consume THROW token
2628
+ # Parse error message expression
2629
+ message = self.parse_expression(LOWEST)
2630
+ stmt = ThrowStatement(message=message)
2631
+ return stmt
2632
+
2633
+ def parse_expression_statement(self):
2634
+ stmt = ExpressionStatement(expression=self.parse_expression(LOWEST))
2635
+ if self.peek_token_is(SEMICOLON):
2636
+ self.next_token()
2637
+ return stmt
2638
+
2639
+ def parse_expression(self, precedence):
2640
+ # Special handling for DEBUG token in expression context
2641
+ # If DEBUG is followed by (, treat it as identifier for function call
2642
+ # Otherwise, it will be parsed as a debug statement
2643
+ if self.cur_token.type == DEBUG and self.peek_token_is(LPAREN):
2644
+ # Convert DEBUG token to identifier in function call context
2645
+ left_exp = Identifier(value="debug")
2646
+ elif self.cur_token.type not in self.prefix_parse_fns:
2647
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Unexpected token '{self.cur_token.literal}'")
2648
+ return None
2649
+ else:
2650
+ prefix = self.prefix_parse_fns[self.cur_token.type]
2651
+ left_exp = prefix()
2652
+
2653
+ if left_exp is None:
2654
+ return None
2655
+
2656
+ # Stop parsing when we hit closing delimiters or terminators
2657
+ # This prevents the parser from trying to parse beyond expression boundaries
2658
+ while (not self.peek_token_is(SEMICOLON) and
2659
+ not self.peek_token_is(EOF) and
2660
+ not self.peek_token_is(RPAREN) and
2661
+ not self.peek_token_is(RBRACE) and
2662
+ not self.peek_token_is(RBRACKET) and
2663
+ precedence <= self.peek_precedence()):
2664
+
2665
+ if self.peek_token.type not in self.infix_parse_fns:
2666
+ return left_exp
2667
+
2668
+ infix = self.infix_parse_fns[self.peek_token.type]
2669
+ self.next_token()
2670
+ left_exp = infix(left_exp)
2671
+
2672
+ if left_exp is None:
2673
+ return None
2674
+
2675
+ return left_exp
2676
+
2677
+ def parse_identifier(self):
2678
+ # Allow DEBUG keyword to be used as identifier in expression contexts
2679
+ # This enables debug(value) function calls while keeping debug value; statements
2680
+ if self.cur_token.type == DEBUG:
2681
+ return Identifier(value="debug")
2682
+ return Identifier(value=self.cur_token.literal)
2683
+
2684
+ def parse_integer_literal(self):
2685
+ try:
2686
+ return IntegerLiteral(value=int(self.cur_token.literal))
2687
+ except ValueError:
2688
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Could not parse {self.cur_token.literal} as integer")
2689
+ return None
2690
+
2691
+ def parse_float_literal(self):
2692
+ try:
2693
+ return FloatLiteral(value=float(self.cur_token.literal))
2694
+ except ValueError:
2695
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Could not parse {self.cur_token.literal} as float")
2696
+ return None
2697
+
2698
+ def parse_string_literal(self):
2699
+ return StringLiteral(value=self.cur_token.literal)
2700
+
2701
+ def parse_boolean(self):
2702
+ lit = getattr(self.cur_token, 'literal', '')
2703
+ val = True if isinstance(lit, str) and lit.lower() == 'true' else False
2704
+ # Transient trace to diagnose boolean parsing
2705
+ try:
2706
+ if lit.lower() == 'false':
2707
+ import traceback as _tb
2708
+ stack = ''.join(_tb.format_stack(limit=4)[-2:])
2709
+ print(f"[PARSE_BOOL_TRACE] false token at position {self.lexer.position}: literal={lit}, val={val}\n{stack}")
2710
+ except Exception:
2711
+ pass
2712
+ return Boolean(value=val)
2713
+
2714
+ def parse_null(self):
2715
+ """Parse null literal"""
2716
+ from ..zexus_ast import NullLiteral
2717
+ return NullLiteral()
2718
+
2719
+ def parse_list_literal(self):
2720
+ list_lit = ListLiteral(elements=[])
2721
+ list_lit.elements = self.parse_expression_list(RBRACKET)
2722
+ return list_lit
2723
+
2724
+ def parse_call_expression(self, function):
2725
+ exp = CallExpression(function=function, arguments=[])
2726
+ exp.arguments = self.parse_expression_list(RPAREN)
2727
+ return exp
2728
+
2729
+ def parse_prefix_expression(self):
2730
+ expression = PrefixExpression(operator=self.cur_token.literal, right=None)
2731
+ self.next_token()
2732
+ expression.right = self.parse_expression(PREFIX)
2733
+ return expression
2734
+
2735
+ def parse_async_expression(self):
2736
+ """Parse async expression: async <expression>
2737
+
2738
+ Example: async producer()
2739
+ This executes the expression asynchronously in a background thread.
2740
+ """
2741
+ from ..zexus_ast import AsyncExpression
2742
+ # Consume 'async' token
2743
+ self.next_token()
2744
+ # Parse the expression to execute asynchronously
2745
+ expr = self.parse_expression(PREFIX)
2746
+ return AsyncExpression(expression=expr)
2747
+
2748
+ def parse_infix_expression(self, left):
2749
+ expression = InfixExpression(left=left, operator=self.cur_token.literal, right=None)
2750
+ precedence = self.cur_precedence()
2751
+ self.next_token()
2752
+ expression.right = self.parse_expression(precedence)
2753
+ return expression
2754
+
2755
+ def parse_grouped_expression(self):
2756
+ # Special-case: if this parenthesized group is followed by a lambda arrow
2757
+ # treat its contents as a parameter list for an arrow-style lambda: (a, b) => ...
2758
+ # The lexer sets a hint flag when it detects a ')' followed by '=>'. Use
2759
+ # that as a fast-path check to parse the contents as parameter identifiers.
2760
+ if getattr(self.lexer, '_next_paren_has_lambda', False) or self._lookahead_token_after_matching_paren() == LAMBDA:
2761
+ # Consume '('
2762
+ self.next_token()
2763
+ self.lexer._next_paren_has_lambda = False # Clear lexer hint after consuming parenthesis
2764
+ params = []
2765
+ # If immediate RPAREN, empty params
2766
+ if self.cur_token_is(RPAREN):
2767
+ self.next_token()
2768
+ return ListLiteral(elements=params)
2769
+
2770
+ # Collect identifiers separated by commas
2771
+ if self.cur_token_is(IDENT):
2772
+ params.append(Identifier(self.cur_token.literal))
2773
+
2774
+ while self.peek_token_is(COMMA):
2775
+ self.next_token() # move to comma
2776
+ self.next_token() # move to next identifier
2777
+ if self.cur_token_is(IDENT):
2778
+ params.append(Identifier(self.cur_token.literal))
2779
+ else:
2780
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected parameter name")
2781
+ break
2782
+
2783
+
2784
+
2785
+ # Default grouped expression behavior
2786
+ self.next_token()
2787
+ exp = self.parse_expression(LOWEST)
2788
+ if not self.expect_peek(RPAREN):
2789
+ return None
2790
+ return exp
2791
+
2792
+ def parse_index_expression(self, left):
2793
+ """Parse index expressions like obj[expr] and convert them to PropertyAccessExpression."""
2794
+ # current token is LBRACKET (parser calls this after advancing to that token)
2795
+ # Move to the first token inside the brackets
2796
+ self.next_token()
2797
+ index_expr = self.parse_expression(LOWEST)
2798
+ # Expect closing bracket
2799
+ if not self.expect_peek(RBRACKET):
2800
+ return None
2801
+ return PropertyAccessExpression(object=left, property=index_expr)
2802
+
2803
+ def _lookahead_token_after_matching_paren(self):
2804
+ """Character-level lookahead: detect if the matching ')' is followed by '=>' (arrow).
2805
+
2806
+ This avoids consuming parser state by scanning the lexer's input string from the
2807
+ current position and counting parentheses. It's best-effort and ignores strings
2808
+ or escapes — suitable for parameter lists which are simple identifier lists.
2809
+ """
2810
+ lexer = self.lexer
2811
+ src = getattr(lexer, 'input', '')
2812
+ pos = getattr(lexer, 'position', 0)
2813
+
2814
+ i = pos
2815
+ depth = 0
2816
+ length = len(src)
2817
+
2818
+ while i < length:
2819
+ ch = src[i]
2820
+ if ch == '(':
2821
+ depth += 1
2822
+ elif ch == ')':
2823
+ depth -= 1
2824
+ if depth == 0:
2825
+ # look ahead for '=>' skipping whitespace
2826
+ j = i + 1
2827
+ while j < length and src[j].isspace():
2828
+ j += 1
2829
+ if j + 1 < length and src[j] == '=' and src[j + 1] == '>':
2830
+ return LAMBDA
2831
+ return None
2832
+ i += 1
2833
+
2834
+ return None
2835
+
2836
+ def parse_if_expression(self):
2837
+ """Parse if expression - handles both statement form and expression form
2838
+
2839
+ Statement form: if (condition) { ... } else { ... }
2840
+ Expression form: if condition then value else value
2841
+ """
2842
+ expression = IfExpression(condition=None, consequence=None, alternative=None)
2843
+
2844
+ # Check if next token is LPAREN (statement form) or not (expression form)
2845
+ if self.peek_token_is(LPAREN):
2846
+ # Statement form: if (condition) { ... }
2847
+ if not self.expect_peek(LPAREN):
2848
+ return None
2849
+
2850
+ self.next_token()
2851
+ expression.condition = self.parse_expression(LOWEST)
2852
+
2853
+ if not self.expect_peek(RPAREN):
2854
+ return None
2855
+
2856
+ if not self.expect_peek(LBRACE):
2857
+ return None
2858
+
2859
+ expression.consequence = self.parse_block_statement()
2860
+
2861
+ if self.peek_token_is(ELSE):
2862
+ self.next_token()
2863
+ if not self.expect_peek(LBRACE):
2864
+ return None
2865
+ expression.alternative = self.parse_block_statement()
2866
+
2867
+ return expression
2868
+ else:
2869
+ # Expression form: if condition then value else value
2870
+ self.next_token() # Move to condition
2871
+ expression.condition = self.parse_expression(LOWEST)
2872
+
2873
+ if expression.condition is None:
2874
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected condition after 'if'")
2875
+ return None
2876
+
2877
+ # Expect THEN
2878
+ if not self.expect_peek(THEN):
2879
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected 'then' after if condition")
2880
+ return None
2881
+
2882
+ # Parse consequence expression
2883
+ self.next_token()
2884
+ consequence_exp = self.parse_expression(LOWEST)
2885
+ if consequence_exp is None:
2886
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected expression after 'then'")
2887
+ return None
2888
+
2889
+ # Wrap the consequence expression in an ExpressionStatement for compatibility
2890
+ from ..zexus_ast import ExpressionStatement, BlockStatement
2891
+ consequence_stmt = ExpressionStatement(expression=consequence_exp)
2892
+ consequence_block = BlockStatement()
2893
+ consequence_block.statements = [consequence_stmt]
2894
+ expression.consequence = consequence_block
2895
+
2896
+ # Expect ELSE
2897
+ if not self.expect_peek(ELSE):
2898
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected 'else' in if-then-else expression")
2899
+ return None
2900
+
2901
+ # Parse alternative expression
2902
+ self.next_token()
2903
+ alternative_exp = self.parse_expression(LOWEST)
2904
+ if alternative_exp is None:
2905
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected expression after 'else'")
2906
+ return None
2907
+
2908
+ # Wrap the alternative expression in an ExpressionStatement
2909
+ alternative_stmt = ExpressionStatement(expression=alternative_exp)
2910
+ alternative_block = BlockStatement()
2911
+ alternative_block.statements = [alternative_stmt]
2912
+ expression.alternative = alternative_block
2913
+
2914
+ return expression
2915
+
2916
+ def parse_block_statement(self):
2917
+ return self.parse_brace_block()
2918
+
2919
+ def parse_entity_statement(self):
2920
+ """Parse entity declaration with maximum tolerance
2921
+
2922
+ Supports:
2923
+ entity ZiverNode {
2924
+ rpc_server: JSONRPCServer
2925
+ ws_server: WebSocketRPCServer
2926
+ // ... other properties
2927
+ }
2928
+ """
2929
+ token = self.cur_token
2930
+
2931
+ if not self.expect_peek(IDENT):
2932
+ self.errors.append(f"Line {token.line}:{token.column} - Expected entity name after 'entity'")
2933
+ return None
2934
+
2935
+ entity_name = Identifier(self.cur_token.literal)
2936
+
2937
+ # Check for inheritance: extends ParentEntity
2938
+ parent = None
2939
+ if self.peek_token_is(IDENT) and self.peek_token.literal == "extends":
2940
+ self.next_token() # Move to 'extends'
2941
+ if not self.expect_peek(IDENT):
2942
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected parent entity name after 'extends'")
2943
+ return None
2944
+ parent = Identifier(self.cur_token.literal)
2945
+
2946
+ if not self.expect_peek(LBRACE):
2947
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '{{' after entity name")
2948
+ return None
2949
+
2950
+ properties = []
2951
+ methods = []
2952
+
2953
+ # Parse properties and methods until we hit closing brace
2954
+ self.next_token() # Move past {
2955
+
2956
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
2957
+ # Check if this is an action/method definition
2958
+ if self.cur_token_is(ACTION) or self.cur_token_is(FUNCTION):
2959
+ method = self.parse_action_statement() if self.cur_token_is(ACTION) else self.parse_function_statement()
2960
+ if method:
2961
+ methods.append(method)
2962
+ continue
2963
+
2964
+ if self.cur_token_is(IDENT):
2965
+ prop_name = self.cur_token.literal
2966
+
2967
+ # Expect colon after property name
2968
+ if not self.expect_peek(COLON):
2969
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected ':' after property name '{prop_name}'")
2970
+ # Try to recover
2971
+ self.recover_to_next_property()
2972
+ continue
2973
+
2974
+ self.next_token() # Move past colon
2975
+
2976
+ # Parse property type (can be identifier or built-in type)
2977
+ if self.cur_token_is(IDENT):
2978
+ prop_type = self.cur_token.literal
2979
+
2980
+ properties.append({
2981
+ "name": prop_name,
2982
+ "type": prop_type
2983
+ })
2984
+
2985
+ # Check for comma or new property
2986
+ if self.peek_token_is(COMMA):
2987
+ self.next_token() # Skip comma
2988
+ elif not self.peek_token_is(RBRACE) and self.peek_token_is(IDENT):
2989
+ # Next property, no comma - tolerate this
2990
+ pass
2991
+
2992
+ else:
2993
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected type for property '{prop_name}'")
2994
+ self.recover_to_next_property()
2995
+ continue
2996
+
2997
+ self.next_token()
2998
+
2999
+ # Expect closing brace
3000
+ if not self.cur_token_is(RBRACE):
3001
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected '}}' to close entity definition")
3002
+ # Tolerant: continue anyway
3003
+ else:
3004
+ # Consume the closing brace
3005
+ self.next_token()
3006
+
3007
+ return EntityStatement(name=entity_name, properties=properties, parent=parent, methods=methods)
3008
+
3009
+ def recover_to_next_property(self):
3010
+ """Recover to the next property in entity definition"""
3011
+ while (not self.cur_token_is(RBRACE) and
3012
+ not self.cur_token_is(EOF) and
3013
+ not (self.cur_token_is(IDENT) and self.peek_token_is(COLON))):
3014
+ self.next_token()
3015
+
3016
+ def parse_verify_statement(self):
3017
+ """Parse verify statement
3018
+
3019
+ verify(transfer_funds, [
3020
+ check_authenticated(),
3021
+ check_balance(amount)
3022
+ ])
3023
+ """
3024
+ if not self.expect_peek(LPAREN):
3025
+ return None
3026
+
3027
+ self.next_token()
3028
+ target = self.parse_expression(LOWEST)
3029
+
3030
+ if not self.expect_peek(COMMA):
3031
+ return None
3032
+
3033
+ self.next_token()
3034
+ conditions = []
3035
+
3036
+ if self.cur_token_is(LBRACKET):
3037
+ conditions = self.parse_expression_list(RBRACKET)
3038
+ else:
3039
+ conditions.append(self.parse_expression(LOWEST))
3040
+
3041
+ if not self.expect_peek(RPAREN):
3042
+ return None
3043
+
3044
+ return VerifyStatement(target, conditions)
3045
+
3046
+ def parse_contract_statement(self):
3047
+ """Parse contract declaration
3048
+
3049
+ contract Token {
3050
+ persistent storage balances: Map<Address, integer>
3051
+
3052
+ action transfer(to: Address, amount: integer) -> boolean { ... }
3053
+ }
3054
+
3055
+ contract QuantumCrypto implements QuantumResistantCrypto { ... }
3056
+ """
3057
+ if not self.expect_peek(IDENT):
3058
+ return None
3059
+
3060
+ contract_name = Identifier(self.cur_token.literal)
3061
+
3062
+ # Check for implements clause
3063
+ implements = None
3064
+ if self.peek_token_is(IMPLEMENTS):
3065
+ self.next_token() # consume 'implements'
3066
+ if self.expect_peek(IDENT):
3067
+ implements = Identifier(self.cur_token.literal)
3068
+
3069
+ if not self.expect_peek(LBRACE):
3070
+ return None
3071
+
3072
+ storage_vars = []
3073
+ actions = []
3074
+
3075
+ while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
3076
+ self.next_token()
3077
+
3078
+ if self.cur_token_is(RBRACE):
3079
+ break
3080
+
3081
+ # Check for state variable declaration
3082
+ if self.cur_token_is(STATE):
3083
+ state_stmt = self.parse_state_statement()
3084
+ if state_stmt:
3085
+ storage_vars.append(state_stmt)
3086
+
3087
+ # Check for persistent storage declaration
3088
+ elif self.cur_token_is(IDENT) and self.cur_token.literal == "persistent":
3089
+ self.next_token()
3090
+ if self.cur_token_is(IDENT) and self.cur_token.literal == "storage":
3091
+ self.next_token()
3092
+ if self.cur_token_is(IDENT):
3093
+ storage_name = self.cur_token.literal
3094
+ storage_vars.append({"name": storage_name})
3095
+
3096
+ # Check for action definition
3097
+ elif self.cur_token_is(ACTION):
3098
+ action = self.parse_action_statement()
3099
+ if action:
3100
+ actions.append(action)
3101
+
3102
+ self.expect_peek(RBRACE)
3103
+
3104
+ # Create body block with storage vars and actions
3105
+ body = BlockStatement()
3106
+ body.statements = storage_vars + actions
3107
+
3108
+ return ContractStatement(contract_name, body, modifiers=[], implements=implements)
3109
+
3110
+ def parse_protect_statement(self):
3111
+ """Parse protect statement
3112
+
3113
+ protect(app, {
3114
+ rate_limit: 100,
3115
+ auth_required: true,
3116
+ require_https: true
3117
+ })
3118
+ """
3119
+ if not self.expect_peek(LPAREN):
3120
+ return None
3121
+
3122
+ self.next_token()
3123
+ target = self.parse_expression(LOWEST)
3124
+
3125
+ if not self.expect_peek(COMMA):
3126
+ return None
3127
+
3128
+ self.next_token()
3129
+ rules = self.parse_expression(LOWEST) # Expect a map literal
3130
+
3131
+ enforcement_level = "strict"
3132
+ if self.peek_token_is(COMMA):
3133
+ self.next_token()
3134
+ self.next_token()
3135
+ if self.cur_token_is(STRING):
3136
+ enforcement_level = self.cur_token.literal
3137
+
3138
+ if not self.expect_peek(RPAREN):
3139
+ return None
3140
+
3141
+ return ProtectStatement(target, rules, enforcement_level)
3142
+
3143
+ def parse_expression_list(self, end):
3144
+ elements = []
3145
+ if self.peek_token_is(end):
3146
+ self.next_token()
3147
+ return elements
3148
+
3149
+ self.next_token()
3150
+ elements.append(self.parse_expression(LOWEST))
3151
+
3152
+ while self.peek_token_is(COMMA):
3153
+ self.next_token()
3154
+ self.next_token()
3155
+ elements.append(self.parse_expression(LOWEST))
3156
+
3157
+ if not self.expect_peek(end):
3158
+ return elements
3159
+
3160
+ return elements
3161
+
3162
+ # === TOKEN UTILITIES ===
3163
+ def next_token(self):
3164
+ self.cur_token = self.peek_token
3165
+ self.peek_token = self.lexer.next_token()
3166
+
3167
+ def cur_token_is(self, t):
3168
+ return self.cur_token.type == t
3169
+
3170
+ def peek_token_is(self, t):
3171
+ return self.peek_token.type == t
3172
+
3173
+ def expect_peek(self, t):
3174
+ if self.peek_token_is(t):
3175
+ self.next_token()
3176
+ return True
3177
+ self.errors.append(f"Line {self.cur_token.line}:{self.cur_token.column} - Expected next token to be {t}, got {self.peek_token.type} instead")
3178
+ return False
3179
+
3180
+ def peek_precedence(self):
3181
+ return precedences.get(self.peek_token.type, LOWEST)
3182
+
3183
+ def cur_precedence(self):
3184
+ return precedences.get(self.cur_token.type, LOWEST)
3185
+
3186
+ def skip_type_annotation(self):
3187
+ """Skip a type annotation, handling generic types like List<T>, Map<K,V>, etc.
3188
+
3189
+ Assumes cur_token is on the first token of the type (e.g., 'List', 'string', etc.)
3190
+ After calling, cur_token will be on the token after the complete type annotation.
3191
+ """
3192
+ if not self.cur_token_is(IDENT):
3193
+ return
3194
+
3195
+ # Skip the type name
3196
+ self.next_token()
3197
+
3198
+ # Check for generic type parameters: <...>
3199
+ if self.cur_token_is(LT):
3200
+ # We have a generic type, need to skip the entire <...> part
3201
+ depth = 1
3202
+ self.next_token() # Skip <
3203
+
3204
+ while depth > 0 and not self.cur_token_is(EOF):
3205
+ if self.cur_token_is(LT):
3206
+ depth += 1
3207
+ elif self.cur_token_is(GT):
3208
+ depth -= 1
3209
+ self.next_token()
3210
+
3211
+ # Check if we exited due to EOF with unmatched brackets
3212
+ if depth > 0 and self.cur_token_is(EOF):
3213
+ self.errors.append(f"Malformed type annotation: unmatched '<' in generic type")
3214
+
3215
+ # cur_token is now on the token after the type annotation
3216
+
3217
+ # === SECURITY STATEMENT PARSERS ===
3218
+
3219
+ def parse_capability_statement(self):
3220
+ """Parse capability statement - grant/check capabilities"""
3221
+ token = self.cur_token
3222
+ self.next_token()
3223
+
3224
+ # capability name { ... }
3225
+ if not self.cur_token_is(IDENT):
3226
+ self.errors.append(f"Line {token.line}:{token.column} - Expected capability name")
3227
+ return None
3228
+
3229
+ cap_name = Identifier(self.cur_token.literal)
3230
+ self.next_token()
3231
+
3232
+ # Simple capability registration
3233
+ return CapabilityStatement(name=cap_name)
3234
+
3235
+ def parse_grant_statement(self):
3236
+ """Parse grant statement - grant capability to entity"""
3237
+ token = self.cur_token
3238
+ self.next_token()
3239
+
3240
+ # grant entity capability
3241
+ if not self.cur_token_is(IDENT):
3242
+ self.errors.append(f"Line {token.line}:{token.column} - Expected entity name")
3243
+ return None
3244
+
3245
+ entity_name = Identifier(self.cur_token.literal)
3246
+ self.next_token()
3247
+
3248
+ # Expect capability
3249
+ if not self.cur_token_is(IDENT):
3250
+ self.errors.append(f"Line {token.line}:{token.column} - Expected capability name")
3251
+ return None
3252
+
3253
+ capability = Identifier(self.cur_token.literal)
3254
+ self.next_token()
3255
+
3256
+ return GrantStatement(entity_name=entity_name, capability=capability)
3257
+
3258
+ def parse_revoke_statement(self):
3259
+ """Parse revoke statement - revoke capability from entity"""
3260
+ token = self.cur_token
3261
+ self.next_token()
3262
+
3263
+ # revoke entity capability
3264
+ if not self.cur_token_is(IDENT):
3265
+ self.errors.append(f"Line {token.line}:{token.column} - Expected entity name")
3266
+ return None
3267
+
3268
+ entity_name = Identifier(self.cur_token.literal)
3269
+ self.next_token()
3270
+
3271
+ # Expect capability
3272
+ if not self.cur_token_is(IDENT):
3273
+ self.errors.append(f"Line {token.line}:{token.column} - Expected capability name")
3274
+ return None
3275
+
3276
+ capability = Identifier(self.cur_token.literal)
3277
+ self.next_token()
3278
+
3279
+ return RevokeStatement(entity_name=entity_name, capability=capability)
3280
+
3281
+ def parse_validate_statement(self):
3282
+ """Parse validate statement - validate data"""
3283
+ token = self.cur_token
3284
+ self.next_token()
3285
+
3286
+ # validate data_expr using schema_expr
3287
+ data_expr = self.parse_expression(LOWEST)
3288
+ if data_expr is None:
3289
+ self.errors.append(f"Line {token.line}:{token.column} - Expected expression to validate")
3290
+ return None
3291
+
3292
+ # Expect 'using'
3293
+ if not (self.cur_token_is(IDENT) and self.cur_token.literal == 'using'):
3294
+ self.errors.append(f"Line {token.line}:{token.column} - Expected 'using' after validate")
3295
+ else:
3296
+ self.next_token()
3297
+
3298
+ schema_expr = self.parse_expression(LOWEST)
3299
+
3300
+ return ValidateStatement(data=data_expr, schema=schema_expr)
3301
+
3302
+ def parse_sanitize_statement(self):
3303
+ """Parse sanitize statement - sanitize data"""
3304
+ token = self.cur_token
3305
+ self.next_token()
3306
+
3307
+ # sanitize data_expr as encoding_type
3308
+ data_expr = self.parse_expression(LOWEST)
3309
+ if data_expr is None:
3310
+ self.errors.append(f"Line {token.line}:{token.column} - Expected expression to sanitize")
3311
+ return None
3312
+
3313
+ # Expect 'as'
3314
+ encoding_type = None
3315
+ if self.cur_token_is(IDENT) and self.cur_token.literal == 'as':
3316
+ self.next_token()
3317
+ if self.cur_token_is(IDENT):
3318
+ encoding_type = self.cur_token.literal
3319
+ self.next_token()
3320
+
3321
+ return SanitizeStatement(data=data_expr, encoding=encoding_type)
3322
+
3323
+ def parse_inject_statement(self):
3324
+ """Parse inject statement - dependency injection"""
3325
+ token = self.cur_token
3326
+ self.next_token()
3327
+
3328
+ # inject dependency_name
3329
+ if not self.cur_token_is(IDENT):
3330
+ self.errors.append(f"Line {token.line}:{token.column} - Expected dependency name after 'inject'")
3331
+ return None
3332
+
3333
+ dependency_name = self.cur_token.literal
3334
+ dependency = Identifier(value=dependency_name)
3335
+ self.next_token()
3336
+
3337
+ # Semicolon is optional
3338
+ if self.cur_token_is(SEMICOLON):
3339
+ self.next_token()
3340
+
3341
+ return InjectStatement(dependency=dependency)
3342
+
3343
+ def parse_immutable_statement(self):
3344
+ """Parse immutable statement - declare immutable variables"""
3345
+ token = self.cur_token
3346
+ self.next_token()
3347
+
3348
+ # immutable let/const name = value
3349
+ target = None
3350
+ if self.cur_token_is(LET):
3351
+ self.next_token()
3352
+ if self.cur_token_is(IDENT):
3353
+ target = Identifier(self.cur_token.literal)
3354
+ self.next_token()
3355
+ elif self.cur_token_is(CONST):
3356
+ self.next_token()
3357
+ if self.cur_token_is(IDENT):
3358
+ target = Identifier(self.cur_token.literal)
3359
+ self.next_token()
3360
+ elif self.cur_token_is(IDENT):
3361
+ target = Identifier(self.cur_token.literal)
3362
+ self.next_token()
3363
+
3364
+ if target is None:
3365
+ self.errors.append(f"Line {token.line}:{token.column} - Expected variable after immutable")
3366
+ return None
3367
+
3368
+ value = None
3369
+ if self.cur_token_is(ASSIGN):
3370
+ self.next_token()
3371
+ value = self.parse_expression(LOWEST)
3372
+
3373
+ return ImmutableStatement(target=target, value=value)
3374
+
3375
+ # === COMPLEXITY STATEMENT PARSERS ===
3376
+
3377
+ def parse_interface_statement(self):
3378
+ """Parse interface definition statement"""
3379
+ token = self.cur_token
3380
+ self.next_token()
3381
+
3382
+ # interface Name { method1; method2; }
3383
+ if not self.cur_token_is(IDENT):
3384
+ self.errors.append(f"Line {token.line}:{token.column} - Expected interface name")
3385
+ return None
3386
+
3387
+ interface_name = Identifier(self.cur_token.literal)
3388
+ self.next_token()
3389
+
3390
+ methods = []
3391
+ properties = {}
3392
+
3393
+ if self.cur_token_is(LBRACE):
3394
+ self.next_token()
3395
+ while not self.cur_token_is(RBRACE) and self.cur_token.type != EOF:
3396
+ if self.cur_token_is(IDENT):
3397
+ methods.append(self.cur_token.literal)
3398
+ self.next_token()
3399
+ # Skip to next method
3400
+ while not self.cur_token_is(SEMICOLON) and not self.cur_token_is(RBRACE):
3401
+ self.next_token()
3402
+ if self.cur_token_is(SEMICOLON):
3403
+ self.next_token()
3404
+ else:
3405
+ self.next_token()
3406
+
3407
+ return InterfaceStatement(name=interface_name, methods=methods, properties=properties)
3408
+
3409
+ def parse_type_alias_statement(self):
3410
+ """Parse type alias statement"""
3411
+ token = self.cur_token
3412
+ self.next_token()
3413
+
3414
+ # type_alias Name = type_expr
3415
+ if not self.cur_token_is(IDENT):
3416
+ self.errors.append(f"Line {token.line}:{token.column} - Expected type alias name")
3417
+ return None
3418
+
3419
+ alias_name = Identifier(self.cur_token.literal)
3420
+ self.next_token()
3421
+
3422
+ if not self.cur_token_is(ASSIGN):
3423
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '=' in type alias")
3424
+ return None
3425
+
3426
+ self.next_token()
3427
+ base_type = self.parse_expression(LOWEST)
3428
+
3429
+ return TypeAliasStatement(name=alias_name, base_type=base_type)
3430
+
3431
+ def parse_module_statement(self):
3432
+ """Parse module definition statement"""
3433
+ token = self.cur_token
3434
+ self.next_token()
3435
+
3436
+ # module Name { body }
3437
+ if not self.cur_token_is(IDENT):
3438
+ self.errors.append(f"Line {token.line}:{token.column} - Expected module name")
3439
+ return None
3440
+
3441
+ module_name = Identifier(self.cur_token.literal)
3442
+ self.next_token()
3443
+
3444
+ body = self.parse_block()
3445
+
3446
+ return ModuleStatement(name=module_name, body=body)
3447
+
3448
+ def parse_package_statement(self):
3449
+ """Parse package definition statement"""
3450
+ token = self.cur_token
3451
+ self.next_token()
3452
+
3453
+ # package name.path { body }
3454
+ package_parts = []
3455
+ while self.cur_token_is(IDENT):
3456
+ package_parts.append(self.cur_token.literal)
3457
+ self.next_token()
3458
+ if self.cur_token_is(DOT):
3459
+ self.next_token()
3460
+ else:
3461
+ break
3462
+
3463
+ if not package_parts:
3464
+ self.errors.append(f"Line {token.line}:{token.column} - Expected package name")
3465
+ return None
3466
+
3467
+ package_name = Identifier('.'.join(package_parts))
3468
+
3469
+ body = self.parse_block()
3470
+
3471
+ return PackageStatement(name=package_name, body=body)
3472
+
3473
+ def parse_using_statement(self):
3474
+ """Parse using statement for resource management"""
3475
+ token = self.cur_token
3476
+ self.next_token()
3477
+
3478
+ # using(resource = expr) { body }
3479
+ if not self.cur_token_is(LPAREN):
3480
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after using")
3481
+ return None
3482
+
3483
+ self.next_token()
3484
+
3485
+ # Parse resource assignment
3486
+ if not self.cur_token_is(IDENT):
3487
+ self.errors.append(f"Line {token.line}:{token.column} - Expected resource name")
3488
+ return None
3489
+
3490
+ resource_name = Identifier(self.cur_token.literal)
3491
+ self.next_token()
3492
+
3493
+ if not self.cur_token_is(ASSIGN):
3494
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '=' in using clause")
3495
+ return None
3496
+
3497
+ self.next_token()
3498
+ resource_expr = self.parse_expression(LOWEST)
3499
+
3500
+ if not self.cur_token_is(RPAREN):
3501
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after using clause")
3502
+ return None
3503
+
3504
+ self.next_token()
3505
+ body = self.parse_block()
3506
+
3507
+ return UsingStatement(resource_name=resource_name, resource_expr=resource_expr, body=body)
3508
+
3509
+ def parse_channel_statement(self):
3510
+ """Parse channel declaration: channel<type> name; or channel<type> name = expr;"""
3511
+ token = self.cur_token
3512
+ self.next_token() # consume CHANNEL
3513
+
3514
+ # Parse channel<type>
3515
+ if not self.cur_token_is(LT):
3516
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '<' after channel")
3517
+ return None
3518
+
3519
+ self.next_token()
3520
+ element_type = self.parse_type_expression()
3521
+
3522
+ if not self.cur_token_is(GT):
3523
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '>' in channel type")
3524
+ return None
3525
+
3526
+ self.next_token()
3527
+
3528
+ # Parse channel name
3529
+ if not self.cur_token_is(IDENT):
3530
+ self.errors.append(f"Line {token.line}:{token.column} - Expected channel name")
3531
+ return None
3532
+
3533
+ name = self.cur_token.literal
3534
+ self.next_token()
3535
+
3536
+ # Optional capacity specification
3537
+ capacity = None
3538
+ if self.cur_token_is(ASSIGN):
3539
+ self.next_token()
3540
+ capacity = self.parse_expression(LOWEST)
3541
+
3542
+ if not self.cur_token_is(SEMICOLON):
3543
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ';' after channel declaration")
3544
+ return None
3545
+
3546
+ self.next_token()
3547
+ return ChannelStatement(name=name, element_type=element_type, capacity=capacity)
3548
+
3549
+ def parse_send_statement(self):
3550
+ """Parse send statement: send(channel, value);"""
3551
+ token = self.cur_token
3552
+ self.next_token() # consume SEND
3553
+
3554
+ if not self.cur_token_is(LPAREN):
3555
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after send")
3556
+ return None
3557
+
3558
+ self.next_token()
3559
+ channel_expr = self.parse_expression(LOWEST)
3560
+
3561
+ if not self.cur_token_is(COMMA):
3562
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ',' after channel in send")
3563
+ return None
3564
+
3565
+ self.next_token()
3566
+ value_expr = self.parse_expression(LOWEST)
3567
+
3568
+ if not self.cur_token_is(RPAREN):
3569
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after send arguments")
3570
+ return None
3571
+
3572
+ self.next_token()
3573
+
3574
+ if not self.cur_token_is(SEMICOLON):
3575
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ';' after send statement")
3576
+ return None
3577
+
3578
+ self.next_token()
3579
+ return SendStatement(channel_expr=channel_expr, value_expr=value_expr)
3580
+
3581
+ def parse_receive_statement(self):
3582
+ """Parse receive statement: receive(channel); or var = receive(channel);"""
3583
+ token = self.cur_token
3584
+ self.next_token() # consume RECEIVE
3585
+
3586
+ if not self.cur_token_is(LPAREN):
3587
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after receive")
3588
+ return None
3589
+
3590
+ self.next_token()
3591
+ channel_expr = self.parse_expression(LOWEST)
3592
+
3593
+ if not self.cur_token_is(RPAREN):
3594
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after receive argument")
3595
+ return None
3596
+
3597
+ self.next_token()
3598
+
3599
+ # Optional target assignment
3600
+ target = None
3601
+
3602
+ if not self.cur_token_is(SEMICOLON):
3603
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ';' after receive statement")
3604
+ return None
3605
+
3606
+ self.next_token()
3607
+ return ReceiveStatement(channel_expr=channel_expr, target=target)
3608
+
3609
+ def parse_atomic_statement(self):
3610
+ """Parse atomic statement: atomic { body } or atomic(expr)"""
3611
+ token = self.cur_token
3612
+ self.next_token() # consume ATOMIC
3613
+
3614
+ body = None
3615
+ expr = None
3616
+
3617
+ if self.cur_token_is(LBRACE):
3618
+ # atomic { body }
3619
+ body = self.parse_block()
3620
+ elif self.cur_token_is(LPAREN):
3621
+ # atomic(expr)
3622
+ self.next_token()
3623
+ expr = self.parse_expression(LOWEST)
3624
+
3625
+ if not self.cur_token_is(RPAREN):
3626
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after atomic expression")
3627
+ return None
3628
+
3629
+ self.next_token()
3630
+
3631
+ if not self.cur_token_is(SEMICOLON):
3632
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ';' after atomic expression")
3633
+ return None
3634
+
3635
+ self.next_token()
3636
+ else:
3637
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '{{' or '(' after atomic")
3638
+ return None
3639
+
3640
+ return AtomicStatement(body=body, expr=expr)
3641
+
3642
+ # === BLOCKCHAIN STATEMENT PARSING ===
3643
+
3644
+ def parse_ledger_statement(self):
3645
+ """Parse ledger statement: ledger NAME = value;
3646
+
3647
+ Declares immutable ledger variable with version tracking.
3648
+ """
3649
+ token = self.cur_token
3650
+
3651
+ if not self.expect_peek(IDENT):
3652
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'ledger'")
3653
+ return None
3654
+
3655
+ name = Identifier(value=self.cur_token.literal)
3656
+
3657
+ if not self.expect_peek(ASSIGN):
3658
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '=' after ledger name")
3659
+ return None
3660
+
3661
+ self.next_token()
3662
+ initial_value = self.parse_expression(LOWEST)
3663
+
3664
+ # Semicolon is optional
3665
+ if self.peek_token_is(SEMICOLON):
3666
+ self.next_token()
3667
+
3668
+ return LedgerStatement(name=name, initial_value=initial_value)
3669
+
3670
+ def parse_state_statement(self):
3671
+ """Parse state statement: state NAME = value;
3672
+
3673
+ Declares mutable contract state variable.
3674
+ """
3675
+ token = self.cur_token
3676
+
3677
+ if not self.expect_peek(IDENT):
3678
+ self.errors.append(f"Line {token.line}:{token.column} - Expected identifier after 'state'")
3679
+ return None
3680
+
3681
+ name = Identifier(value=self.cur_token.literal)
3682
+
3683
+ if not self.expect_peek(ASSIGN):
3684
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '=' after state name")
3685
+ return None
3686
+
3687
+ self.next_token()
3688
+ initial_value = self.parse_expression(LOWEST)
3689
+
3690
+ # Semicolon is optional
3691
+ if self.peek_token_is(SEMICOLON):
3692
+ self.next_token()
3693
+
3694
+ return StateStatement(name=name, initial_value=initial_value)
3695
+
3696
+ def parse_require_statement(self):
3697
+ """Parse require statement: require(condition, message);
3698
+
3699
+ Asserts condition, reverts transaction if false.
3700
+ """
3701
+ token = self.cur_token
3702
+
3703
+ if not self.expect_peek(LPAREN):
3704
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after 'require'")
3705
+ return None
3706
+
3707
+ self.next_token()
3708
+ condition = self.parse_expression(LOWEST)
3709
+
3710
+ message = None
3711
+ if self.peek_token_is(COMMA):
3712
+ self.next_token()
3713
+ self.next_token()
3714
+ message = self.parse_expression(LOWEST)
3715
+
3716
+ if not self.expect_peek(RPAREN):
3717
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after require arguments")
3718
+ return None
3719
+
3720
+ # Semicolon is optional
3721
+ if self.peek_token_is(SEMICOLON):
3722
+ self.next_token()
3723
+
3724
+ return RequireStatement(condition=condition, message=message)
3725
+
3726
+ def parse_revert_statement(self):
3727
+ """Parse revert statement: revert(reason);
3728
+
3729
+ Reverts transaction with optional reason.
3730
+ """
3731
+ token = self.cur_token
3732
+
3733
+ reason = None
3734
+ if self.peek_token_is(LPAREN):
3735
+ self.next_token()
3736
+ self.next_token()
3737
+ reason = self.parse_expression(LOWEST)
3738
+
3739
+ if not self.expect_peek(RPAREN):
3740
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after revert reason")
3741
+ return None
3742
+
3743
+ # Semicolon is optional
3744
+ if self.peek_token_is(SEMICOLON):
3745
+ self.next_token()
3746
+
3747
+ return RevertStatement(reason=reason)
3748
+
3749
+ def parse_limit_statement(self):
3750
+ """Parse limit statement: limit(gas_amount);
3751
+
3752
+ Sets gas limit for operation.
3753
+ """
3754
+ token = self.cur_token
3755
+
3756
+ if not self.expect_peek(LPAREN):
3757
+ self.errors.append(f"Line {token.line}:{token.column} - Expected '(' after 'limit'")
3758
+ return None
3759
+
3760
+ self.next_token()
3761
+ gas_limit = self.parse_expression(LOWEST)
3762
+
3763
+ if not self.expect_peek(RPAREN):
3764
+ self.errors.append(f"Line {token.line}:{token.column} - Expected ')' after limit amount")
3765
+ return None
3766
+
3767
+ # Semicolon is optional
3768
+ if self.peek_token_is(SEMICOLON):
3769
+ self.next_token()
3770
+
3771
+ return LimitStatement(gas_limit=gas_limit)
3772
+
3773
+ # === BLOCKCHAIN EXPRESSION PARSING ===
3774
+
3775
+ def parse_tx_expression(self):
3776
+ """Parse TX expression: tx.caller, tx.timestamp, tx.gas_used, etc.
3777
+
3778
+ Access transaction context properties.
3779
+ """
3780
+ if not self.expect_peek(DOT):
3781
+ # Just 'tx' by itself returns the TX object
3782
+ return TXExpression(property_name=None)
3783
+
3784
+ if not self.expect_peek(IDENT):
3785
+ self.errors.append("Expected property name after 'tx.'")
3786
+ return None
3787
+
3788
+ property_name = self.cur_token.literal
3789
+ return TXExpression(property_name=property_name)
3790
+
3791
+ def parse_hash_expression(self):
3792
+ """Parse hash expression: hash(data, algorithm)
3793
+
3794
+ Cryptographic hash function.
3795
+ """
3796
+ if not self.expect_peek(LPAREN):
3797
+ self.errors.append("Expected '(' after 'hash'")
3798
+ return None
3799
+
3800
+ self.next_token()
3801
+ data = self.parse_expression(LOWEST)
3802
+
3803
+ algorithm = StringLiteral(value="SHA256") # Default algorithm
3804
+ if self.peek_token_is(COMMA):
3805
+ self.next_token()
3806
+ self.next_token()
3807
+ algorithm = self.parse_expression(LOWEST)
3808
+
3809
+ if not self.expect_peek(RPAREN):
3810
+ self.errors.append("Expected ')' after hash arguments")
3811
+ return None
3812
+
3813
+ return HashExpression(data=data, algorithm=algorithm)
3814
+
3815
+ def parse_signature_expression(self):
3816
+ """Parse signature expression: signature(data, private_key, algorithm)
3817
+
3818
+ Creates digital signature.
3819
+ """
3820
+ if not self.expect_peek(LPAREN):
3821
+ self.errors.append("Expected '(' after 'signature'")
3822
+ return None
3823
+
3824
+ self.next_token()
3825
+ data = self.parse_expression(LOWEST)
3826
+
3827
+ if not self.expect_peek(COMMA):
3828
+ self.errors.append("Expected ',' after data in signature")
3829
+ return None
3830
+
3831
+ self.next_token()
3832
+ private_key = self.parse_expression(LOWEST)
3833
+
3834
+ algorithm = StringLiteral(value="ECDSA") # Default algorithm
3835
+ if self.peek_token_is(COMMA):
3836
+ self.next_token()
3837
+ self.next_token()
3838
+ algorithm = self.parse_expression(LOWEST)
3839
+
3840
+ if not self.expect_peek(RPAREN):
3841
+ self.errors.append("Expected ')' after signature arguments")
3842
+ return None
3843
+
3844
+ return SignatureExpression(data=data, private_key=private_key, algorithm=algorithm)
3845
+
3846
+ def parse_verify_sig_expression(self):
3847
+ """Parse verify_sig expression: verify_sig(data, signature, public_key, algorithm)
3848
+
3849
+ Verifies digital signature.
3850
+ """
3851
+ if not self.expect_peek(LPAREN):
3852
+ self.errors.append("Expected '(' after 'verify_sig'")
3853
+ return None
3854
+
3855
+ self.next_token()
3856
+ data = self.parse_expression(LOWEST)
3857
+
3858
+ if not self.expect_peek(COMMA):
3859
+ self.errors.append("Expected ',' after data in verify_sig")
3860
+ return None
3861
+
3862
+ self.next_token()
3863
+ signature = self.parse_expression(LOWEST)
3864
+
3865
+ if not self.expect_peek(COMMA):
3866
+ self.errors.append("Expected ',' after signature in verify_sig")
3867
+ return None
3868
+
3869
+ self.next_token()
3870
+ public_key = self.parse_expression(LOWEST)
3871
+
3872
+ algorithm = StringLiteral(value="ECDSA") # Default algorithm
3873
+ if self.peek_token_is(COMMA):
3874
+ self.next_token()
3875
+ self.next_token()
3876
+ algorithm = self.parse_expression(LOWEST)
3877
+
3878
+ if not self.expect_peek(RPAREN):
3879
+ self.errors.append("Expected ')' after verify_sig arguments")
3880
+ return None
3881
+
3882
+ return VerifySignatureExpression(data=data, signature=signature, public_key=public_key, algorithm=algorithm)
3883
+
3884
+ def parse_gas_expression(self):
3885
+ """Parse gas expression: gas or gas.used or gas.remaining
3886
+
3887
+ Access gas tracking information.
3888
+ """
3889
+ if not self.peek_token_is(DOT):
3890
+ # Just 'gas' by itself - returns GasExpression with no property
3891
+ return GasExpression(property_name=None)
3892
+
3893
+ self.next_token() # consume DOT
3894
+
3895
+ if not self.expect_peek(IDENT):
3896
+ self.errors.append("Expected property name after 'gas.'")
3897
+ return None
3898
+
3899
+ property_name = self.cur_token.literal
3900
+ return GasExpression(property_name=property_name)
3901
+
3902
+ def parse_ternary_expression(self, condition):
3903
+ """Parse ternary expression: condition ? true_value : false_value"""
3904
+ from ..zexus_ast import TernaryExpression
3905
+
3906
+ self.next_token() # consume '?'
3907
+ true_value = self.parse_expression(LOWEST)
3908
+
3909
+ if not self.expect_peek(COLON):
3910
+ self.errors.append("Expected ':' in ternary expression")
3911
+ return None
3912
+
3913
+ self.next_token() # consume ':'
3914
+ false_value = self.parse_expression(LOWEST)
3915
+
3916
+ return TernaryExpression(condition, true_value, false_value)
3917
+
3918
+ def parse_nullish_expression(self, left):
3919
+ """Parse nullish coalescing: value ?? default"""
3920
+ from ..zexus_ast import NullishExpression
3921
+
3922
+ self.next_token() # consume '??'
3923
+ right = self.parse_expression(NULLISH_PREC)
3924
+
3925
+ return NullishExpression(left, right)
3926
+
3927
+ def parse_this(self):
3928
+ """Parse 'this' expression for contract self-reference"""
3929
+ return ThisExpression()
3930
+
3931
+ def parse_emit_statement(self):
3932
+ """Parse emit statement
3933
+
3934
+ emit Transfer(from, to, amount);
3935
+ emit StateChange(\"balance_updated\", new_balance);
3936
+ """
3937
+ if not self.expect_peek(IDENT):
3938
+ return None
3939
+
3940
+ event_name = Identifier(self.cur_token.literal)
3941
+
3942
+ # Parse optional arguments
3943
+ arguments = []
3944
+ if self.peek_token_is(LPAREN):
3945
+ self.next_token() # consume '('
3946
+ arguments = self.parse_expression_list(RPAREN)
3947
+
3948
+ return EmitStatement(event_name, arguments)
3949
+
3950
+ def parse_modifier_declaration(self):
3951
+ """Parse modifier declaration
3952
+
3953
+ modifier onlyOwner {
3954
+ require(TX.caller == owner, \"Not owner\");
3955
+ }
3956
+ """
3957
+ if not self.expect_peek(IDENT):
3958
+ return None
3959
+
3960
+ name = Identifier(self.cur_token.literal)
3961
+
3962
+ # Parse optional parameters
3963
+ parameters = []
3964
+ if self.peek_token_is(LPAREN):
3965
+ self.next_token() # consume '('
3966
+ parameters = self.parse_function_parameters()
3967
+
3968
+ # Parse body
3969
+ if not self.expect_peek(LBRACE):
3970
+ return None
3971
+
3972
+ body = self.parse_block_statement()
3973
+
3974
+ return ModifierDeclaration(name, parameters, body)
3975
+
3976
+ # Backward compatibility
3977
+ Parser = UltimateParser