zexus 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +2513 -0
- package/bin/zexus +2 -0
- package/bin/zpics +2 -0
- package/bin/zpm +2 -0
- package/bin/zx +2 -0
- package/bin/zx-deploy +2 -0
- package/bin/zx-dev +2 -0
- package/bin/zx-run +2 -0
- package/package.json +66 -0
- package/scripts/README.md +24 -0
- package/scripts/postinstall.js +44 -0
- package/shared_config.json +24 -0
- package/src/README.md +1525 -0
- package/src/tests/run_zexus_tests.py +117 -0
- package/src/tests/test_all_phases.zx +346 -0
- package/src/tests/test_blockchain_features.zx +306 -0
- package/src/tests/test_complexity_features.zx +321 -0
- package/src/tests/test_core_integration.py +185 -0
- package/src/tests/test_phase10_ecosystem.zx +177 -0
- package/src/tests/test_phase1_modifiers.zx +87 -0
- package/src/tests/test_phase2_plugins.zx +80 -0
- package/src/tests/test_phase3_security.zx +97 -0
- package/src/tests/test_phase4_vfs.zx +116 -0
- package/src/tests/test_phase5_types.zx +117 -0
- package/src/tests/test_phase6_metaprogramming.zx +125 -0
- package/src/tests/test_phase7_optimization.zx +132 -0
- package/src/tests/test_phase9_advanced_types.zx +157 -0
- package/src/tests/test_security_features.py +419 -0
- package/src/tests/test_security_features.zx +276 -0
- package/src/tests/test_simple_zx.zx +1 -0
- package/src/tests/test_verification_simple.zx +69 -0
- package/src/zexus/__init__.py +28 -0
- package/src/zexus/__main__.py +5 -0
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/ecosystem.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/hybrid_orchestrator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/virtual_filesystem.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +401 -0
- package/src/zexus/blockchain/__init__.py +40 -0
- package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/crypto.py +463 -0
- package/src/zexus/blockchain/ledger.py +255 -0
- package/src/zexus/blockchain/transaction.py +267 -0
- package/src/zexus/builtin_modules.py +284 -0
- package/src/zexus/builtin_plugins.py +317 -0
- package/src/zexus/capability_system.py +372 -0
- package/src/zexus/cli/__init__.py +2 -0
- package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +707 -0
- package/src/zexus/cli/zpm.py +203 -0
- package/src/zexus/compare_interpreter_compiler.py +146 -0
- package/src/zexus/compiler/__init__.py +169 -0
- package/src/zexus/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/bytecode.py +266 -0
- package/src/zexus/compiler/compat_runtime.py +277 -0
- package/src/zexus/compiler/lexer.py +257 -0
- package/src/zexus/compiler/parser.py +779 -0
- package/src/zexus/compiler/semantic.py +118 -0
- package/src/zexus/compiler/zexus_ast.py +454 -0
- package/src/zexus/complexity_system.py +575 -0
- package/src/zexus/concurrency_system.py +493 -0
- package/src/zexus/config.py +201 -0
- package/src/zexus/crypto_bridge.py +19 -0
- package/src/zexus/dependency_injection.py +423 -0
- package/src/zexus/ecosystem.py +434 -0
- package/src/zexus/environment.py +101 -0
- package/src/zexus/environment_manager.py +119 -0
- package/src/zexus/error_reporter.py +314 -0
- package/src/zexus/evaluator/__init__.py +12 -0
- package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/integration.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +700 -0
- package/src/zexus/evaluator/core.py +891 -0
- package/src/zexus/evaluator/expressions.py +827 -0
- package/src/zexus/evaluator/functions.py +3989 -0
- package/src/zexus/evaluator/integration.py +396 -0
- package/src/zexus/evaluator/statements.py +4303 -0
- package/src/zexus/evaluator/utils.py +126 -0
- package/src/zexus/evaluator_original.py +2041 -0
- package/src/zexus/external_bridge.py +16 -0
- package/src/zexus/find_affected_imports.sh +155 -0
- package/src/zexus/hybrid_orchestrator.py +152 -0
- package/src/zexus/input_validation.py +259 -0
- package/src/zexus/lexer.py +571 -0
- package/src/zexus/logging.py +89 -0
- package/src/zexus/lsp/__init__.py +9 -0
- package/src/zexus/lsp/completion_provider.py +207 -0
- package/src/zexus/lsp/definition_provider.py +22 -0
- package/src/zexus/lsp/hover_provider.py +71 -0
- package/src/zexus/lsp/server.py +269 -0
- package/src/zexus/lsp/symbol_provider.py +31 -0
- package/src/zexus/metaprogramming.py +321 -0
- package/src/zexus/module_cache.py +89 -0
- package/src/zexus/module_manager.py +107 -0
- package/src/zexus/object.py +973 -0
- package/src/zexus/optimization.py +424 -0
- package/src/zexus/parser/__init__.py +31 -0
- package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/integration.py +86 -0
- package/src/zexus/parser/parser.py +3977 -0
- package/src/zexus/parser/strategy_context.py +7254 -0
- package/src/zexus/parser/strategy_structural.py +1033 -0
- package/src/zexus/persistence.py +391 -0
- package/src/zexus/plugin_system.py +290 -0
- package/src/zexus/policy_engine.py +365 -0
- package/src/zexus/profiler/__init__.py +5 -0
- package/src/zexus/profiler/profiler.py +233 -0
- package/src/zexus/purity_system.py +398 -0
- package/src/zexus/runtime/__init__.py +20 -0
- package/src/zexus/runtime/async_runtime.py +324 -0
- package/src/zexus/search_old_imports.sh +65 -0
- package/src/zexus/security.py +1407 -0
- package/src/zexus/stack_trace.py +233 -0
- package/src/zexus/stdlib/__init__.py +27 -0
- package/src/zexus/stdlib/blockchain.py +341 -0
- package/src/zexus/stdlib/compression.py +167 -0
- package/src/zexus/stdlib/crypto.py +124 -0
- package/src/zexus/stdlib/datetime.py +163 -0
- package/src/zexus/stdlib/db_mongo.py +199 -0
- package/src/zexus/stdlib/db_mysql.py +162 -0
- package/src/zexus/stdlib/db_postgres.py +163 -0
- package/src/zexus/stdlib/db_sqlite.py +133 -0
- package/src/zexus/stdlib/encoding.py +230 -0
- package/src/zexus/stdlib/fs.py +195 -0
- package/src/zexus/stdlib/http.py +219 -0
- package/src/zexus/stdlib/http_server.py +248 -0
- package/src/zexus/stdlib/json_module.py +61 -0
- package/src/zexus/stdlib/math.py +360 -0
- package/src/zexus/stdlib/os_module.py +265 -0
- package/src/zexus/stdlib/regex.py +148 -0
- package/src/zexus/stdlib/sockets.py +253 -0
- package/src/zexus/stdlib/test_framework.zx +208 -0
- package/src/zexus/stdlib/test_runner.zx +119 -0
- package/src/zexus/stdlib_integration.py +341 -0
- package/src/zexus/strategy_recovery.py +256 -0
- package/src/zexus/syntax_validator.py +356 -0
- package/src/zexus/testing/zpics.py +407 -0
- package/src/zexus/testing/zpics_runtime.py +369 -0
- package/src/zexus/type_system.py +374 -0
- package/src/zexus/validation_system.py +569 -0
- package/src/zexus/virtual_filesystem.py +355 -0
- package/src/zexus/vm/__init__.py +8 -0
- package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +420 -0
- package/src/zexus/vm/bytecode.py +428 -0
- package/src/zexus/vm/bytecode_converter.py +297 -0
- package/src/zexus/vm/cache.py +532 -0
- package/src/zexus/vm/jit.py +720 -0
- package/src/zexus/vm/memory_manager.py +520 -0
- package/src/zexus/vm/memory_pool.py +511 -0
- package/src/zexus/vm/optimizer.py +478 -0
- package/src/zexus/vm/parallel_vm.py +899 -0
- package/src/zexus/vm/peephole_optimizer.py +452 -0
- package/src/zexus/vm/profiler.py +527 -0
- package/src/zexus/vm/register_allocator.py +462 -0
- package/src/zexus/vm/register_vm.py +520 -0
- package/src/zexus/vm/ssa_converter.py +757 -0
- package/src/zexus/vm/vm.py +1392 -0
- package/src/zexus/zexus_ast.py +1782 -0
- package/src/zexus/zexus_token.py +253 -0
- package/src/zexus/zpm/__init__.py +15 -0
- package/src/zexus/zpm/installer.py +116 -0
- package/src/zexus/zpm/package_manager.py +208 -0
- package/src/zexus/zpm/publisher.py +98 -0
- package/src/zexus/zpm/registry.py +110 -0
- package/src/zexus.egg-info/PKG-INFO +2235 -0
- package/src/zexus.egg-info/SOURCES.txt +876 -0
- package/src/zexus.egg-info/dependency_links.txt +1 -0
- package/src/zexus.egg-info/entry_points.txt +3 -0
- package/src/zexus.egg-info/not-zip-safe +1 -0
- package/src/zexus.egg-info/requires.txt +14 -0
- package/src/zexus.egg-info/top_level.txt +2 -0
- package/zexus.json +14 -0
|
@@ -0,0 +1,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
|