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,779 @@
|
|
|
1
|
+
# parser.py (TOLERANT MULTI-STRATEGY PARSER)
|
|
2
|
+
"""
|
|
3
|
+
Clean Production Parser for Zexus - Fixes Object Literal Issue
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ..zexus_token import *
|
|
7
|
+
from .lexer import Lexer
|
|
8
|
+
from .zexus_ast import *
|
|
9
|
+
|
|
10
|
+
# Precedence constants
|
|
11
|
+
LOWEST, ASSIGN_PREC, EQUALS, LESSGREATER, SUM, PRODUCT, PREFIX, CALL, LOGICAL = 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
12
|
+
|
|
13
|
+
precedences = {
|
|
14
|
+
EQ: EQUALS, NOT_EQ: EQUALS,
|
|
15
|
+
LT: LESSGREATER, GT: LESSGREATER, LTE: LESSGREATER, GTE: LESSGREATER,
|
|
16
|
+
PLUS: SUM, MINUS: SUM,
|
|
17
|
+
SLASH: PRODUCT, STAR: PRODUCT, MOD: PRODUCT,
|
|
18
|
+
AND: LOGICAL, OR: LOGICAL,
|
|
19
|
+
LPAREN: CALL,
|
|
20
|
+
DOT: CALL,
|
|
21
|
+
ASSIGN: ASSIGN_PREC,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class ProductionParser:
|
|
25
|
+
def __init__(self, lexer):
|
|
26
|
+
self.lexer = lexer
|
|
27
|
+
self.errors = []
|
|
28
|
+
self.cur_token = None
|
|
29
|
+
self.peek_token = None
|
|
30
|
+
|
|
31
|
+
# Parser function maps
|
|
32
|
+
self.prefix_parse_fns = {
|
|
33
|
+
IDENT: self.parse_identifier,
|
|
34
|
+
INT: self.parse_integer_literal,
|
|
35
|
+
FLOAT: self.parse_float_literal,
|
|
36
|
+
STRING: self.parse_string_literal,
|
|
37
|
+
BANG: self.parse_prefix_expression,
|
|
38
|
+
MINUS: self.parse_prefix_expression,
|
|
39
|
+
TRUE: self.parse_boolean,
|
|
40
|
+
FALSE: self.parse_boolean,
|
|
41
|
+
LPAREN: self.parse_grouped_expression,
|
|
42
|
+
IF: self.parse_if_expression,
|
|
43
|
+
LBRACKET: self.parse_list_literal,
|
|
44
|
+
LBRACE: self.parse_map_literal, # FIXED: Map literal parsing
|
|
45
|
+
ACTION: self.parse_action_literal,
|
|
46
|
+
EMBEDDED: self.parse_embedded_literal,
|
|
47
|
+
LAMBDA: self.parse_lambda_expression,
|
|
48
|
+
AWAIT: self.parse_await_expression, # NEW: Await expression
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
self.infix_parse_fns = {
|
|
52
|
+
PLUS: self.parse_infix_expression,
|
|
53
|
+
MINUS: self.parse_infix_expression,
|
|
54
|
+
SLASH: self.parse_infix_expression,
|
|
55
|
+
STAR: self.parse_infix_expression,
|
|
56
|
+
MOD: self.parse_infix_expression,
|
|
57
|
+
EQ: self.parse_infix_expression,
|
|
58
|
+
NOT_EQ: self.parse_infix_expression,
|
|
59
|
+
LT: self.parse_infix_expression,
|
|
60
|
+
GT: self.parse_infix_expression,
|
|
61
|
+
LTE: self.parse_infix_expression,
|
|
62
|
+
GTE: self.parse_infix_expression,
|
|
63
|
+
AND: self.parse_infix_expression,
|
|
64
|
+
OR: self.parse_infix_expression,
|
|
65
|
+
ASSIGN: self.parse_assignment_expression,
|
|
66
|
+
LPAREN: self.parse_call_expression,
|
|
67
|
+
DOT: self.parse_method_call_expression,
|
|
68
|
+
LAMBDA: self.parse_lambda_infix, # support arrow-style lambdas: params => body
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
self.next_token()
|
|
72
|
+
self.next_token()
|
|
73
|
+
|
|
74
|
+
def parse_program(self):
|
|
75
|
+
"""Clean, efficient program parsing"""
|
|
76
|
+
program = Program()
|
|
77
|
+
while not self.cur_token_is(EOF):
|
|
78
|
+
# Tolerant: skip stray semicolons between statements
|
|
79
|
+
if self.cur_token_is(SEMICOLON):
|
|
80
|
+
self.next_token()
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
stmt = self.parse_statement()
|
|
84
|
+
if stmt:
|
|
85
|
+
program.statements.append(stmt)
|
|
86
|
+
|
|
87
|
+
self.next_token()
|
|
88
|
+
return program
|
|
89
|
+
|
|
90
|
+
def parse_statement(self):
|
|
91
|
+
"""Parse statements with clear error reporting"""
|
|
92
|
+
try:
|
|
93
|
+
if self.cur_token_is(LET):
|
|
94
|
+
return self.parse_let_statement()
|
|
95
|
+
elif self.cur_token_is(RETURN):
|
|
96
|
+
return self.parse_return_statement()
|
|
97
|
+
elif self.cur_token_is(PRINT):
|
|
98
|
+
return self.parse_print_statement()
|
|
99
|
+
elif self.cur_token_is(FOR):
|
|
100
|
+
return self.parse_for_each_statement()
|
|
101
|
+
elif self.cur_token_is(ACTION):
|
|
102
|
+
return self.parse_action_statement()
|
|
103
|
+
elif self.cur_token_is(ASYNC):
|
|
104
|
+
# support "async action name ..." or "action async name ..."
|
|
105
|
+
# If "async action ..." then consume ASYNC and expect ACTION next
|
|
106
|
+
self.next_token()
|
|
107
|
+
if self.cur_token_is(ACTION):
|
|
108
|
+
return self.parse_action_statement(async_flag=True)
|
|
109
|
+
# otherwise error
|
|
110
|
+
self.errors.append(f"Line {self.cur_token.line}: Expected 'action' after 'async'")
|
|
111
|
+
return None
|
|
112
|
+
elif self.cur_token_is(EVENT):
|
|
113
|
+
return self.parse_event_declaration()
|
|
114
|
+
elif self.cur_token_is(EMIT):
|
|
115
|
+
return self.parse_emit_statement()
|
|
116
|
+
elif self.cur_token_is(ENUM):
|
|
117
|
+
return self.parse_enum_declaration()
|
|
118
|
+
elif self.cur_token_is(PROTOCOL):
|
|
119
|
+
return self.parse_protocol_declaration()
|
|
120
|
+
elif self.cur_token_is(IMPORT):
|
|
121
|
+
return self.parse_import_statement()
|
|
122
|
+
else:
|
|
123
|
+
return self.parse_expression_statement()
|
|
124
|
+
except Exception as e:
|
|
125
|
+
self.errors.append(f"Line {self.cur_token.line}: Parse error - {str(e)}")
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def parse_let_statement(self):
|
|
129
|
+
"""Fixed: Properly handles object literals"""
|
|
130
|
+
if not self.expect_peek(IDENT):
|
|
131
|
+
self.errors.append(f"Line {self.cur_token.line}: Expected variable name after 'let'")
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
name = Identifier(self.cur_token.literal)
|
|
135
|
+
|
|
136
|
+
if not self.expect_peek(ASSIGN):
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
self.next_token()
|
|
140
|
+
value = self.parse_expression(LOWEST)
|
|
141
|
+
|
|
142
|
+
return LetStatement(name=name, value=value)
|
|
143
|
+
|
|
144
|
+
def parse_map_literal(self):
|
|
145
|
+
"""FIXED: Proper map literal parsing - this was the core issue!"""
|
|
146
|
+
pairs = []
|
|
147
|
+
|
|
148
|
+
# Must be called when current token is LBRACE
|
|
149
|
+
if not self.cur_token_is(LBRACE):
|
|
150
|
+
self.errors.append(f"Line {getattr(self.cur_token, 'line', 'unknown')}: parse_map_literal called on non-brace token")
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
# Move inside the braces
|
|
154
|
+
self.next_token() # advance to token after '{'
|
|
155
|
+
|
|
156
|
+
# Handle empty object case: {}
|
|
157
|
+
if self.cur_token_is(RBRACE):
|
|
158
|
+
return MapLiteral(pairs)
|
|
159
|
+
|
|
160
|
+
# Parse key-value pairs
|
|
161
|
+
while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
162
|
+
# Parse key (can be string or identifier)
|
|
163
|
+
if self.cur_token_is(STRING):
|
|
164
|
+
key = StringLiteral(self.cur_token.literal)
|
|
165
|
+
elif self.cur_token_is(IDENT):
|
|
166
|
+
key = Identifier(self.cur_token.literal)
|
|
167
|
+
else:
|
|
168
|
+
self.errors.append(f"Line {getattr(self.cur_token, 'line', 'unknown')}: Object key must be string or identifier")
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
# Expect colon (current peek should be COLON)
|
|
172
|
+
if not self.expect_peek(COLON):
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
# Move to value token and parse it
|
|
176
|
+
self.next_token()
|
|
177
|
+
value = self.parse_expression(LOWEST)
|
|
178
|
+
if value is None:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
pairs.append((key, value))
|
|
182
|
+
|
|
183
|
+
# Accept comma OR semicolon as separators; tolerate trailing separators
|
|
184
|
+
if self.peek_token_is(COMMA) or self.peek_token_is(SEMICOLON):
|
|
185
|
+
self.next_token() # move to separator
|
|
186
|
+
# advance to next token after separator (or closing brace)
|
|
187
|
+
if self.peek_token_is(RBRACE):
|
|
188
|
+
self.next_token() # move to RBRACE and break next loop iteration
|
|
189
|
+
break
|
|
190
|
+
self.next_token()
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
# If closing brace is the next token, consume it and finish
|
|
194
|
+
if self.peek_token_is(RBRACE):
|
|
195
|
+
self.next_token() # advance to RBRACE
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
# Otherwise, try to advance; tolerant parsing
|
|
199
|
+
self.next_token()
|
|
200
|
+
|
|
201
|
+
# Final check: should be at a RBRACE token
|
|
202
|
+
if not self.cur_token_is(RBRACE):
|
|
203
|
+
self.errors.append(f"Line {getattr(self.cur_token, 'line', 'unknown')}: Expected '}}' to close object literal")
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
# Note: we keep the closing brace consumed (caller behavior consistent)
|
|
207
|
+
return MapLiteral(pairs)
|
|
208
|
+
|
|
209
|
+
# Rest of parser methods (simplified for production)
|
|
210
|
+
def parse_expression(self, precedence):
|
|
211
|
+
if self.cur_token.type not in self.prefix_parse_fns:
|
|
212
|
+
self.errors.append(f"Line {self.cur_token.line}: Unexpected token '{self.cur_token.literal}'")
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
prefix = self.prefix_parse_fns[self.cur_token.type]
|
|
216
|
+
left_exp = prefix()
|
|
217
|
+
|
|
218
|
+
if left_exp is None:
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
while (not self.peek_token_is(SEMICOLON) and
|
|
222
|
+
not self.peek_token_is(EOF) and
|
|
223
|
+
precedence <= self.peek_precedence()):
|
|
224
|
+
|
|
225
|
+
if self.peek_token.type not in self.infix_parse_fns:
|
|
226
|
+
return left_exp
|
|
227
|
+
|
|
228
|
+
infix = self.infix_parse_fns[self.peek_token.type]
|
|
229
|
+
self.next_token()
|
|
230
|
+
left_exp = infix(left_exp)
|
|
231
|
+
|
|
232
|
+
if left_exp is None:
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
return left_exp
|
|
236
|
+
|
|
237
|
+
def parse_identifier(self):
|
|
238
|
+
return Identifier(value=self.cur_token.literal)
|
|
239
|
+
|
|
240
|
+
def parse_integer_literal(self):
|
|
241
|
+
try:
|
|
242
|
+
return IntegerLiteral(value=int(self.cur_token.literal))
|
|
243
|
+
except ValueError:
|
|
244
|
+
self.errors.append(f"Line {self.cur_token.line}: Could not parse {self.cur_token.literal} as integer")
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
def parse_float_literal(self):
|
|
248
|
+
try:
|
|
249
|
+
return FloatLiteral(value=float(self.cur_token.literal))
|
|
250
|
+
except ValueError:
|
|
251
|
+
self.errors.append(f"Line {self.cur_token.line}: Could not parse {self.cur_token.literal} as float")
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
def parse_string_literal(self):
|
|
255
|
+
return StringLiteral(value=self.cur_token.literal)
|
|
256
|
+
|
|
257
|
+
def parse_boolean(self):
|
|
258
|
+
return Boolean(value=self.cur_token_is(TRUE))
|
|
259
|
+
|
|
260
|
+
def parse_list_literal(self):
|
|
261
|
+
elements = self.parse_expression_list(RBRACKET)
|
|
262
|
+
return ListLiteral(elements=elements)
|
|
263
|
+
|
|
264
|
+
def parse_grouped_expression(self):
|
|
265
|
+
self.next_token()
|
|
266
|
+
exp = self.parse_expression(LOWEST)
|
|
267
|
+
if not self.expect_peek(RPAREN):
|
|
268
|
+
return None
|
|
269
|
+
return exp
|
|
270
|
+
|
|
271
|
+
def parse_prefix_expression(self):
|
|
272
|
+
expression = PrefixExpression(operator=self.cur_token.literal, right=None)
|
|
273
|
+
self.next_token()
|
|
274
|
+
expression.right = self.parse_expression(PREFIX)
|
|
275
|
+
return expression
|
|
276
|
+
|
|
277
|
+
def parse_infix_expression(self, left):
|
|
278
|
+
expression = InfixExpression(left=left, operator=self.cur_token.literal, right=None)
|
|
279
|
+
precedence = self.cur_precedence()
|
|
280
|
+
self.next_token()
|
|
281
|
+
expression.right = self.parse_expression(precedence)
|
|
282
|
+
return expression
|
|
283
|
+
|
|
284
|
+
def parse_call_expression(self, function):
|
|
285
|
+
arguments = self.parse_expression_list(RPAREN)
|
|
286
|
+
return CallExpression(function=function, arguments=arguments)
|
|
287
|
+
|
|
288
|
+
def parse_assignment_expression(self, left):
|
|
289
|
+
if not isinstance(left, Identifier):
|
|
290
|
+
self.errors.append(f"Line {self.cur_token.line}: Cannot assign to {type(left).__name__}")
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
expression = AssignmentExpression(name=left, value=None)
|
|
294
|
+
self.next_token()
|
|
295
|
+
expression.value = self.parse_expression(LOWEST)
|
|
296
|
+
return expression
|
|
297
|
+
|
|
298
|
+
def parse_method_call_expression(self, left):
|
|
299
|
+
if not self.expect_peek(IDENT):
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
method = Identifier(self.cur_token.literal)
|
|
303
|
+
|
|
304
|
+
if self.peek_token_is(LPAREN):
|
|
305
|
+
self.next_token()
|
|
306
|
+
arguments = self.parse_expression_list(RPAREN)
|
|
307
|
+
return MethodCallExpression(object=left, method=method, arguments=arguments)
|
|
308
|
+
else:
|
|
309
|
+
return PropertyAccessExpression(object=left, property=method)
|
|
310
|
+
|
|
311
|
+
def parse_expression_list(self, end):
|
|
312
|
+
elements = []
|
|
313
|
+
if self.peek_token_is(end):
|
|
314
|
+
self.next_token()
|
|
315
|
+
return elements
|
|
316
|
+
|
|
317
|
+
self.next_token()
|
|
318
|
+
elements.append(self.parse_expression(LOWEST))
|
|
319
|
+
|
|
320
|
+
while self.peek_token_is(COMMA):
|
|
321
|
+
self.next_token()
|
|
322
|
+
self.next_token()
|
|
323
|
+
elements.append(self.parse_expression(LOWEST))
|
|
324
|
+
|
|
325
|
+
if not self.expect_peek(end):
|
|
326
|
+
return elements
|
|
327
|
+
|
|
328
|
+
return elements
|
|
329
|
+
|
|
330
|
+
# Statement parsing methods
|
|
331
|
+
def parse_expression_statement(self):
|
|
332
|
+
"""Parse expression as a statement"""
|
|
333
|
+
stmt = ExpressionStatement(expression=self.parse_expression(LOWEST))
|
|
334
|
+
if self.peek_token_is(SEMICOLON):
|
|
335
|
+
self.next_token()
|
|
336
|
+
return stmt
|
|
337
|
+
|
|
338
|
+
def parse_return_statement(self):
|
|
339
|
+
stmt = ReturnStatement(return_value=None)
|
|
340
|
+
self.next_token()
|
|
341
|
+
stmt.return_value = self.parse_expression(LOWEST)
|
|
342
|
+
return stmt
|
|
343
|
+
|
|
344
|
+
def parse_print_statement(self):
|
|
345
|
+
stmt = PrintStatement(value=None)
|
|
346
|
+
self.next_token()
|
|
347
|
+
stmt.value = self.parse_expression(LOWEST)
|
|
348
|
+
return stmt
|
|
349
|
+
|
|
350
|
+
def parse_if_statement(self):
|
|
351
|
+
self.next_token() # Skip IF
|
|
352
|
+
|
|
353
|
+
# Parse condition (with or without parentheses)
|
|
354
|
+
if self.cur_token_is(LPAREN):
|
|
355
|
+
self.next_token()
|
|
356
|
+
condition = self.parse_expression(LOWEST)
|
|
357
|
+
if self.cur_token_is(RPAREN):
|
|
358
|
+
self.next_token()
|
|
359
|
+
else:
|
|
360
|
+
condition = self.parse_expression(LOWEST)
|
|
361
|
+
|
|
362
|
+
if not condition:
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
# Parse consequence
|
|
366
|
+
consequence = self.parse_block()
|
|
367
|
+
if not consequence:
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
alternative = None
|
|
371
|
+
if self.cur_token_is(ELSE):
|
|
372
|
+
self.next_token()
|
|
373
|
+
alternative = self.parse_block()
|
|
374
|
+
|
|
375
|
+
return IfStatement(condition=condition, consequence=consequence, alternative=alternative)
|
|
376
|
+
|
|
377
|
+
def parse_block(self):
|
|
378
|
+
block = BlockStatement()
|
|
379
|
+
|
|
380
|
+
# Handle different block styles
|
|
381
|
+
if self.cur_token_is(LBRACE):
|
|
382
|
+
self.next_token() # Skip {
|
|
383
|
+
|
|
384
|
+
while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
385
|
+
# Skip stray semicolons between statements inside a block
|
|
386
|
+
if self.cur_token_is(SEMICOLON):
|
|
387
|
+
self.next_token()
|
|
388
|
+
continue
|
|
389
|
+
|
|
390
|
+
stmt = self.parse_statement()
|
|
391
|
+
if stmt:
|
|
392
|
+
block.statements.append(stmt)
|
|
393
|
+
|
|
394
|
+
# After parsing a statement, consume any trailing semicolons so they don't become unexpected tokens
|
|
395
|
+
while self.peek_token_is(SEMICOLON):
|
|
396
|
+
self.next_token() # move to semicolon
|
|
397
|
+
self.next_token() # move past semicolon
|
|
398
|
+
|
|
399
|
+
# Advance to next token if parser hasn't advanced to EOF or closing brace
|
|
400
|
+
if not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
401
|
+
self.next_token()
|
|
402
|
+
|
|
403
|
+
if self.cur_token_is(EOF):
|
|
404
|
+
self.errors.append("Unclosed block (reached EOF)")
|
|
405
|
+
else:
|
|
406
|
+
# Single statement block
|
|
407
|
+
# Tolerant: allow a trailing semicolon after single statement
|
|
408
|
+
stmt = self.parse_statement()
|
|
409
|
+
if stmt:
|
|
410
|
+
block.statements.append(stmt)
|
|
411
|
+
if self.peek_token_is(SEMICOLON):
|
|
412
|
+
self.next_token() # consume semicolon
|
|
413
|
+
|
|
414
|
+
return block
|
|
415
|
+
|
|
416
|
+
def parse_for_each_statement(self):
|
|
417
|
+
if not self.expect_peek(EACH):
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
if not self.expect_peek(IDENT):
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
item = Identifier(self.cur_token.literal)
|
|
424
|
+
|
|
425
|
+
if not self.expect_peek(IN):
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
self.next_token()
|
|
429
|
+
iterable = self.parse_expression(LOWEST)
|
|
430
|
+
|
|
431
|
+
body = self.parse_block()
|
|
432
|
+
|
|
433
|
+
return ForEachStatement(item=item, iterable=iterable, body=body)
|
|
434
|
+
|
|
435
|
+
def parse_action_statement(self, async_flag=False):
|
|
436
|
+
"""Parse action declaration; supports optional async modifier (action async name(...) { ... } or async action ...)"""
|
|
437
|
+
# current token is ACTION (or we arrived here after consuming ASYNC)
|
|
438
|
+
is_async = async_flag
|
|
439
|
+
|
|
440
|
+
# Handle optional 'async' immediately after 'action': action async name ...
|
|
441
|
+
if self.peek_token_is(IDENT) and self.peek_token.literal == "async":
|
|
442
|
+
# unusual: peek is IDENT with literal "async" — but lexer maps "async" to ASYNC token;
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
# If next token is ASYNC (action async name ...)
|
|
446
|
+
if self.peek_token_is(ASYNC):
|
|
447
|
+
self.next_token()
|
|
448
|
+
is_async = True
|
|
449
|
+
|
|
450
|
+
# Continue normal action parse
|
|
451
|
+
if not self.expect_peek(IDENT):
|
|
452
|
+
return None
|
|
453
|
+
name = Identifier(self.cur_token.literal)
|
|
454
|
+
|
|
455
|
+
parameters = []
|
|
456
|
+
if self.peek_token_is(LPAREN):
|
|
457
|
+
# Advance into the parameter list: consume '(' then move to first inner token
|
|
458
|
+
self.next_token() # move to LPAREN
|
|
459
|
+
self.next_token() # move to token after '('
|
|
460
|
+
parameters = self.parse_parameter_list()
|
|
461
|
+
# If current token is ')' (empty params case), advance past it.
|
|
462
|
+
if self.cur_token_is(RPAREN):
|
|
463
|
+
self.next_token()
|
|
464
|
+
else:
|
|
465
|
+
# Otherwise expect a closing paren next and consume it
|
|
466
|
+
if not self.expect_peek(RPAREN):
|
|
467
|
+
return None
|
|
468
|
+
# consume the ')'
|
|
469
|
+
self.next_token()
|
|
470
|
+
|
|
471
|
+
body = self.parse_block()
|
|
472
|
+
# Create ActionStatement with is_async flag (add attribute)
|
|
473
|
+
stmt = ActionStatement(name=name, parameters=parameters, body=body)
|
|
474
|
+
# attach async flag if supported by AST
|
|
475
|
+
setattr(stmt, "is_async", is_async)
|
|
476
|
+
return stmt
|
|
477
|
+
|
|
478
|
+
def parse_action_literal(self):
|
|
479
|
+
if not self.expect_peek(LPAREN):
|
|
480
|
+
return None
|
|
481
|
+
|
|
482
|
+
parameters = self.parse_parameter_list()
|
|
483
|
+
if parameters is None:
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
body = self.parse_expression(LOWEST)
|
|
487
|
+
# If action literal is used as an expression, callers may expect a function-like node.
|
|
488
|
+
# Mark the ActionLiteral node to indicate expression-level function (helps lowering).
|
|
489
|
+
action_lit = ActionLiteral(parameters=parameters, body=body)
|
|
490
|
+
setattr(action_lit, "is_expression", True)
|
|
491
|
+
return action_lit
|
|
492
|
+
|
|
493
|
+
def parse_await_expression(self):
|
|
494
|
+
# current token is AWAIT
|
|
495
|
+
self.next_token()
|
|
496
|
+
value = self.parse_expression(LOWEST)
|
|
497
|
+
return AwaitExpression(expression=value)
|
|
498
|
+
|
|
499
|
+
def parse_if_expression(self):
|
|
500
|
+
"""Parse if expression: if (condition) { consequence } else { alternative }"""
|
|
501
|
+
expression = IfExpression(condition=None, consequence=None, alternative=None)
|
|
502
|
+
|
|
503
|
+
if not self.expect_peek(LPAREN):
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
self.next_token()
|
|
507
|
+
expression.condition = self.parse_expression(LOWEST)
|
|
508
|
+
|
|
509
|
+
if not self.expect_peek(RPAREN):
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
if not self.expect_peek(LBRACE):
|
|
513
|
+
return None
|
|
514
|
+
|
|
515
|
+
expression.consequence = self.parse_block()
|
|
516
|
+
|
|
517
|
+
if self.peek_token_is(ELSE):
|
|
518
|
+
self.next_token()
|
|
519
|
+
if not self.expect_peek(LBRACE):
|
|
520
|
+
return None
|
|
521
|
+
expression.alternative = self.parse_block()
|
|
522
|
+
|
|
523
|
+
return expression
|
|
524
|
+
|
|
525
|
+
def parse_embedded_literal(self):
|
|
526
|
+
"""Parse embedded code block: @{ language ... code ... }"""
|
|
527
|
+
if not self.expect_peek(LBRACE):
|
|
528
|
+
return None
|
|
529
|
+
|
|
530
|
+
self.next_token()
|
|
531
|
+
code_lines = []
|
|
532
|
+
|
|
533
|
+
# Read until closing brace
|
|
534
|
+
while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
535
|
+
code_lines.append(self.cur_token.literal)
|
|
536
|
+
self.next_token()
|
|
537
|
+
|
|
538
|
+
if not self.cur_token_is(RBRACE):
|
|
539
|
+
self.errors.append("Expected } after embedded code block")
|
|
540
|
+
return None
|
|
541
|
+
|
|
542
|
+
self.next_token() # Skip closing brace
|
|
543
|
+
|
|
544
|
+
code_content = ' '.join(code_lines)
|
|
545
|
+
lines = code_content.strip().split('\n')
|
|
546
|
+
|
|
547
|
+
if not lines:
|
|
548
|
+
self.errors.append("Empty embedded code block")
|
|
549
|
+
return None
|
|
550
|
+
|
|
551
|
+
# First line is language, rest is code
|
|
552
|
+
language_line = lines[0].strip() if lines else "unknown"
|
|
553
|
+
language = language_line if language_line else "unknown"
|
|
554
|
+
code = '\n'.join(lines[1:]).strip() if len(lines) > 1 else ""
|
|
555
|
+
|
|
556
|
+
return EmbeddedLiteral(language=language, code=code)
|
|
557
|
+
|
|
558
|
+
def parse_lambda_expression(self):
|
|
559
|
+
"""Parse lambda/arrow function: x => body or lambda(x): body"""
|
|
560
|
+
token = self.cur_token
|
|
561
|
+
parameters = []
|
|
562
|
+
|
|
563
|
+
self.next_token()
|
|
564
|
+
|
|
565
|
+
if self.cur_token_is(LPAREN):
|
|
566
|
+
self.next_token()
|
|
567
|
+
parameters = self.parse_parameter_list()
|
|
568
|
+
if not self.expect_peek(RPAREN):
|
|
569
|
+
return None
|
|
570
|
+
elif self.cur_token_is(IDENT):
|
|
571
|
+
# Single parameter without parens
|
|
572
|
+
parameters = [Identifier(self.cur_token.literal)]
|
|
573
|
+
self.next_token()
|
|
574
|
+
|
|
575
|
+
# Handle arrow or colon separator
|
|
576
|
+
if self.cur_token_is(COLON):
|
|
577
|
+
self.next_token()
|
|
578
|
+
elif self.cur_token_is(LAMBDA): # => token
|
|
579
|
+
self.next_token()
|
|
580
|
+
elif self.cur_token_is(MINUS) and self.peek_token_is(GT):
|
|
581
|
+
self.next_token() # Skip -
|
|
582
|
+
self.next_token() # Skip >
|
|
583
|
+
|
|
584
|
+
body = self.parse_expression(LOWEST)
|
|
585
|
+
return LambdaExpression(parameters=parameters, body=body)
|
|
586
|
+
|
|
587
|
+
def parse_lambda_infix(self, left):
|
|
588
|
+
"""Parse arrow-style lambda when encountering leftside 'params' followed by =>
|
|
589
|
+
|
|
590
|
+
Examples:
|
|
591
|
+
x => x + 1
|
|
592
|
+
(a, b) => a + b
|
|
593
|
+
"""
|
|
594
|
+
# Build parameter list from `left` expression
|
|
595
|
+
params = []
|
|
596
|
+
if isinstance(left, Identifier):
|
|
597
|
+
params = [left]
|
|
598
|
+
else:
|
|
599
|
+
try:
|
|
600
|
+
if hasattr(left, 'elements'):
|
|
601
|
+
for el in left.elements:
|
|
602
|
+
if isinstance(el, Identifier):
|
|
603
|
+
params.append(el)
|
|
604
|
+
except Exception:
|
|
605
|
+
pass
|
|
606
|
+
|
|
607
|
+
# Current token is LAMBDA; advance to body
|
|
608
|
+
self.next_token()
|
|
609
|
+
|
|
610
|
+
if self.cur_token_is(COLON):
|
|
611
|
+
self.next_token()
|
|
612
|
+
|
|
613
|
+
body = self.parse_expression(LOWEST)
|
|
614
|
+
return LambdaExpression(parameters=params, body=body)
|
|
615
|
+
|
|
616
|
+
def parse_action_literal(self):
|
|
617
|
+
"""Parse action literal: action (params) { body } or action (params) => expr"""
|
|
618
|
+
if not self.expect_peek(LPAREN):
|
|
619
|
+
return None
|
|
620
|
+
|
|
621
|
+
parameters = self.parse_parameter_list()
|
|
622
|
+
if parameters is None:
|
|
623
|
+
return None
|
|
624
|
+
|
|
625
|
+
# Expect closing paren
|
|
626
|
+
if not self.expect_peek(RPAREN):
|
|
627
|
+
return None
|
|
628
|
+
|
|
629
|
+
self.next_token()
|
|
630
|
+
|
|
631
|
+
# Action body can be a block or expression
|
|
632
|
+
if self.cur_token_is(LBRACE):
|
|
633
|
+
body = self.parse_block()
|
|
634
|
+
else:
|
|
635
|
+
# Expression body (shorthand)
|
|
636
|
+
body = self.parse_expression(LOWEST)
|
|
637
|
+
|
|
638
|
+
action_lit = ActionLiteral(parameters=parameters, body=body)
|
|
639
|
+
return action_lit
|
|
640
|
+
|
|
641
|
+
def parse_parameter_list(self):
|
|
642
|
+
"""Parse parameter list for functions"""
|
|
643
|
+
parameters = []
|
|
644
|
+
|
|
645
|
+
if self.cur_token_is(RPAREN):
|
|
646
|
+
return parameters
|
|
647
|
+
|
|
648
|
+
while not self.cur_token_is(RPAREN) and not self.cur_token_is(EOF):
|
|
649
|
+
if self.cur_token_is(IDENT):
|
|
650
|
+
parameters.append(Identifier(self.cur_token.literal))
|
|
651
|
+
self.next_token()
|
|
652
|
+
else:
|
|
653
|
+
break
|
|
654
|
+
|
|
655
|
+
# Handle comma separator
|
|
656
|
+
if self.cur_token_is(COMMA):
|
|
657
|
+
self.next_token()
|
|
658
|
+
elif self.cur_token_is(RPAREN):
|
|
659
|
+
break
|
|
660
|
+
else:
|
|
661
|
+
break
|
|
662
|
+
|
|
663
|
+
return parameters
|
|
664
|
+
|
|
665
|
+
def parse_event_declaration(self):
|
|
666
|
+
if not self.expect_peek(IDENT):
|
|
667
|
+
return None
|
|
668
|
+
name = Identifier(self.cur_token.literal)
|
|
669
|
+
body = self.parse_block()
|
|
670
|
+
return EventDeclaration(name=name, properties=body)
|
|
671
|
+
|
|
672
|
+
def parse_emit_statement(self):
|
|
673
|
+
if not self.expect_peek(IDENT):
|
|
674
|
+
return None
|
|
675
|
+
name = Identifier(self.cur_token.literal)
|
|
676
|
+
payload = None
|
|
677
|
+
if self.peek_token_is(LPAREN):
|
|
678
|
+
self.next_token()
|
|
679
|
+
self.next_token()
|
|
680
|
+
payload = self.parse_expression(LOWEST)
|
|
681
|
+
if not self.expect_peek(RPAREN):
|
|
682
|
+
return None
|
|
683
|
+
elif self.peek_token_is(LBRACE):
|
|
684
|
+
self.next_token()
|
|
685
|
+
payload = self.parse_block()
|
|
686
|
+
return EmitStatement(name=name, payload=payload)
|
|
687
|
+
|
|
688
|
+
def parse_enum_declaration(self):
|
|
689
|
+
if not self.expect_peek(IDENT):
|
|
690
|
+
return None
|
|
691
|
+
name = Identifier(self.cur_token.literal)
|
|
692
|
+
if not self.expect_peek(LBRACE):
|
|
693
|
+
return None
|
|
694
|
+
# parse simple comma-separated identifiers or key:value pairs
|
|
695
|
+
members = {}
|
|
696
|
+
self.next_token()
|
|
697
|
+
while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
698
|
+
if self.cur_token_is(IDENT) or self.cur_token_is(STRING):
|
|
699
|
+
key = self.cur_token.literal
|
|
700
|
+
# optional colon value
|
|
701
|
+
val = None
|
|
702
|
+
if self.peek_token_is(COLON):
|
|
703
|
+
self.next_token()
|
|
704
|
+
self.next_token()
|
|
705
|
+
if self.cur_token_is(INT):
|
|
706
|
+
val = int(self.cur_token.literal)
|
|
707
|
+
members[key] = val
|
|
708
|
+
if self.peek_token_is(COMMA):
|
|
709
|
+
self.next_token()
|
|
710
|
+
self.next_token()
|
|
711
|
+
continue
|
|
712
|
+
self.next_token()
|
|
713
|
+
if not self.cur_token_is(RBRACE):
|
|
714
|
+
self.errors.append("Unclosed enum declaration")
|
|
715
|
+
return None
|
|
716
|
+
return EnumDeclaration(name=name, members=members)
|
|
717
|
+
|
|
718
|
+
def parse_protocol_declaration(self):
|
|
719
|
+
if not self.expect_peek(IDENT):
|
|
720
|
+
return None
|
|
721
|
+
name = Identifier(self.cur_token.literal)
|
|
722
|
+
if not self.expect_peek(LBRACE):
|
|
723
|
+
return None
|
|
724
|
+
# parse simple list of method signatures (as identifiers)
|
|
725
|
+
spec = {"methods": []}
|
|
726
|
+
self.next_token()
|
|
727
|
+
while not self.cur_token_is(RBRACE) and not self.cur_token_is(EOF):
|
|
728
|
+
if self.cur_token_is(IDENT):
|
|
729
|
+
spec["methods"].append(self.cur_token.literal)
|
|
730
|
+
if self.peek_token_is(COMMA):
|
|
731
|
+
self.next_token()
|
|
732
|
+
self.next_token()
|
|
733
|
+
continue
|
|
734
|
+
self.next_token()
|
|
735
|
+
if not self.cur_token_is(RBRACE):
|
|
736
|
+
self.errors.append("Unclosed protocol declaration")
|
|
737
|
+
return None
|
|
738
|
+
return ProtocolDeclaration(name=name, spec=spec)
|
|
739
|
+
|
|
740
|
+
def parse_import_statement(self):
|
|
741
|
+
if not self.expect_peek(STRING):
|
|
742
|
+
return None
|
|
743
|
+
module_path = self.cur_token.literal
|
|
744
|
+
alias = None
|
|
745
|
+
if self.peek_token_is(IDENT) and self.peek_token.literal == "as":
|
|
746
|
+
self.next_token()
|
|
747
|
+
self.next_token()
|
|
748
|
+
if self.cur_token_is(IDENT):
|
|
749
|
+
alias = self.cur_token.literal
|
|
750
|
+
return ImportStatement(module_path=module_path, alias=alias)
|
|
751
|
+
|
|
752
|
+
# Token utilities
|
|
753
|
+
def next_token(self):
|
|
754
|
+
self.cur_token = self.peek_token
|
|
755
|
+
self.peek_token = self.lexer.next_token()
|
|
756
|
+
|
|
757
|
+
def cur_token_is(self, t):
|
|
758
|
+
return self.cur_token.type == t
|
|
759
|
+
|
|
760
|
+
def peek_token_is(self, t):
|
|
761
|
+
return self.peek_token.type == t
|
|
762
|
+
|
|
763
|
+
def expect_peek(self, t):
|
|
764
|
+
if self.peek_token_is(t):
|
|
765
|
+
self.next_token()
|
|
766
|
+
return True
|
|
767
|
+
self.errors.append(f"Line {self.cur_token.line}: Expected '{t}', got '{self.peek_token.type}'")
|
|
768
|
+
return False
|
|
769
|
+
|
|
770
|
+
def peek_precedence(self):
|
|
771
|
+
return precedences.get(self.peek_token.type, LOWEST)
|
|
772
|
+
|
|
773
|
+
def cur_precedence(self):
|
|
774
|
+
return precedences.get(self.cur_token.type, LOWEST)
|
|
775
|
+
|
|
776
|
+
# --- Compatibility alias ----------------------------------------------------
|
|
777
|
+
# Provide the common name `Parser` for code that imports the compiler parser
|
|
778
|
+
# using older/alternate names.
|
|
779
|
+
Parser = ProductionParser
|