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,256 @@
|
|
|
1
|
+
# strategy_recovery.py
|
|
2
|
+
from .zexus_token import *
|
|
3
|
+
from .zexus_ast import (
|
|
4
|
+
EntityStatement,
|
|
5
|
+
UseStatement,
|
|
6
|
+
ExportStatement,
|
|
7
|
+
TryCatchStatement,
|
|
8
|
+
BlockStatement,
|
|
9
|
+
ExpressionStatement,
|
|
10
|
+
Identifier,
|
|
11
|
+
StringLiteral,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
class ErrorRecoveryEngine:
|
|
15
|
+
def __init__(self, structural_analyzer, context_parser):
|
|
16
|
+
self.structural_analyzer = structural_analyzer
|
|
17
|
+
self.context_parser = context_parser
|
|
18
|
+
self.recovery_strategies = {
|
|
19
|
+
'expected_catch': self._recover_expected_catch,
|
|
20
|
+
'unexpected_token': self._recover_unexpected_token,
|
|
21
|
+
'missing_parenthesis': self._recover_missing_parenthesis,
|
|
22
|
+
'missing_brace': self._recover_missing_brace,
|
|
23
|
+
'syntax_error': self._recover_generic_syntax
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def create_recovery_plan(self, error, current_block_info, current_tokens, token_index):
|
|
27
|
+
"""Create intelligent recovery plan based on error type and context"""
|
|
28
|
+
print(f"🛠️ [Recovery] Analyzing error: {error}")
|
|
29
|
+
|
|
30
|
+
error_type = self._classify_error(error)
|
|
31
|
+
current_context = self.context_parser.get_current_context()
|
|
32
|
+
|
|
33
|
+
if error_type in self.recovery_strategies:
|
|
34
|
+
recovery_plan = self.recovery_strategies[error_type](
|
|
35
|
+
error, current_block_info, current_context, current_tokens, token_index
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if recovery_plan['can_recover']:
|
|
39
|
+
print(f"✅ [Recovery] {recovery_plan['message']}")
|
|
40
|
+
return recovery_plan
|
|
41
|
+
|
|
42
|
+
# Final fallback
|
|
43
|
+
return self._create_fallback_recovery(error, current_block_info, current_context)
|
|
44
|
+
|
|
45
|
+
def _classify_error(self, error):
|
|
46
|
+
"""Classify the error type for appropriate recovery strategy"""
|
|
47
|
+
error_msg = str(error).lower()
|
|
48
|
+
|
|
49
|
+
if "expected catch" in error_msg or "catch" in error_msg:
|
|
50
|
+
return 'expected_catch'
|
|
51
|
+
elif "unexpected token" in error_msg:
|
|
52
|
+
return 'unexpected_token'
|
|
53
|
+
elif "missing )" in error_msg or "parenthesis" in error_msg:
|
|
54
|
+
return 'missing_parenthesis'
|
|
55
|
+
elif "missing }" in error_msg or "brace" in error_msg:
|
|
56
|
+
return 'missing_brace'
|
|
57
|
+
else:
|
|
58
|
+
return 'syntax_error'
|
|
59
|
+
|
|
60
|
+
def _recover_expected_catch(self, error, block_info, context, tokens, token_index):
|
|
61
|
+
"""Recover from 'expected catch' errors using structural analysis"""
|
|
62
|
+
print("🛠️ [Recovery] Handling 'expected catch' error")
|
|
63
|
+
|
|
64
|
+
# Check if structural analysis shows a catch block exists
|
|
65
|
+
current_block_id = block_info.get('id') if block_info else None
|
|
66
|
+
catch_block = self._find_catch_block_in_structure(current_block_id)
|
|
67
|
+
|
|
68
|
+
if catch_block:
|
|
69
|
+
return {
|
|
70
|
+
'can_recover': True,
|
|
71
|
+
'recovered_statement': self._create_dummy_try_catch(),
|
|
72
|
+
'next_action': 'skip_to_block',
|
|
73
|
+
'target_block': catch_block['id'],
|
|
74
|
+
'message': 'Structural analysis shows catch block exists later - skipping to catch'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# If no catch block found, create a synthetic one
|
|
78
|
+
return {
|
|
79
|
+
'can_recover': True,
|
|
80
|
+
'recovered_statement': self._create_synthetic_catch_block(),
|
|
81
|
+
'next_action': 'continue',
|
|
82
|
+
'message': 'Created synthetic catch block for recovery'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def _recover_unexpected_token(self, error, block_info, context, tokens, token_index):
|
|
86
|
+
"""Recover from unexpected token errors"""
|
|
87
|
+
print(f"🛠️ [Recovery] Handling unexpected token at index {token_index}")
|
|
88
|
+
|
|
89
|
+
# Extract the problematic token from error message or use current token
|
|
90
|
+
problematic_token = self._extract_problematic_token(error, tokens, token_index)
|
|
91
|
+
|
|
92
|
+
if problematic_token:
|
|
93
|
+
return {
|
|
94
|
+
'can_recover': True,
|
|
95
|
+
'recovered_statement': self._create_skip_statement(problematic_token),
|
|
96
|
+
'next_action': 'skip_tokens',
|
|
97
|
+
'skip_count': 1,
|
|
98
|
+
'message': f'Skipped problematic token: {problematic_token}'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {'can_recover': False}
|
|
102
|
+
|
|
103
|
+
def _recover_missing_parenthesis(self, error, block_info, context, tokens, token_index):
|
|
104
|
+
"""Recover from missing parenthesis errors"""
|
|
105
|
+
print("🛠️ [Recovery] Handling missing parenthesis")
|
|
106
|
+
|
|
107
|
+
# Look ahead to find a likely closing parenthesis
|
|
108
|
+
for i in range(token_index, min(token_index + 10, len(tokens))):
|
|
109
|
+
if tokens[i].type == RPAREN:
|
|
110
|
+
return {
|
|
111
|
+
'can_recover': True,
|
|
112
|
+
'recovered_statement': self._create_dummy_expression(),
|
|
113
|
+
'next_action': 'skip_to_token',
|
|
114
|
+
'target_token_index': i + 1,
|
|
115
|
+
'message': 'Found potential closing parenthesis ahead'
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Insert synthetic closing parenthesis
|
|
119
|
+
return {
|
|
120
|
+
'can_recover': True,
|
|
121
|
+
'recovered_statement': self._create_dummy_expression(),
|
|
122
|
+
'next_action': 'insert_token',
|
|
123
|
+
'insert_token': Token(RPAREN, ')', line=tokens[token_index].line, column=tokens[token_index].column + 1),
|
|
124
|
+
'message': 'Inserted synthetic closing parenthesis'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def _recover_missing_brace(self, error, block_info, context, tokens, token_index):
|
|
128
|
+
"""Recover from missing brace errors using structural analysis"""
|
|
129
|
+
print("🛠️ [Recovery] Handling missing brace")
|
|
130
|
+
|
|
131
|
+
# Use structural analysis to find matching brace
|
|
132
|
+
if block_info:
|
|
133
|
+
# Structural analyzer already found the block boundaries
|
|
134
|
+
return {
|
|
135
|
+
'can_recover': True,
|
|
136
|
+
'recovered_statement': BlockStatement(),
|
|
137
|
+
'next_action': 'skip_to_block_end',
|
|
138
|
+
'target_block': block_info['id'],
|
|
139
|
+
'message': 'Using structural analysis to find block end'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {'can_recover': False}
|
|
143
|
+
|
|
144
|
+
def _recover_generic_syntax(self, error, block_info, context, tokens, token_index):
|
|
145
|
+
"""Generic syntax error recovery"""
|
|
146
|
+
print("🛠️ [Recovery] Handling generic syntax error")
|
|
147
|
+
|
|
148
|
+
# Try to skip a few tokens and continue
|
|
149
|
+
return {
|
|
150
|
+
'can_recover': True,
|
|
151
|
+
'recovered_statement': self._create_dummy_statement(),
|
|
152
|
+
'next_action': 'skip_tokens',
|
|
153
|
+
'skip_count': 3, # Skip next 3 tokens
|
|
154
|
+
'message': 'Skipping ahead to recover from syntax error'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
def _find_catch_block_in_structure(self, current_block_id):
|
|
158
|
+
"""Find a catch block in the structural analysis"""
|
|
159
|
+
blocks = self.structural_analyzer.blocks
|
|
160
|
+
|
|
161
|
+
# Look for catch blocks in current block or parent blocks
|
|
162
|
+
for block_id, block in blocks.items():
|
|
163
|
+
if block.get('subtype') == 'try_catch':
|
|
164
|
+
if block.get('catch_section'):
|
|
165
|
+
return block
|
|
166
|
+
# Check nested blocks
|
|
167
|
+
for nested in block.get('nested_blocks', []):
|
|
168
|
+
if nested.get('subtype') == 'try_catch' and nested.get('catch_section'):
|
|
169
|
+
return nested
|
|
170
|
+
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
def _extract_problematic_token(self, error, tokens, token_index):
|
|
174
|
+
"""Extract the problematic token from error message or token stream"""
|
|
175
|
+
if token_index < len(tokens):
|
|
176
|
+
return tokens[token_index]
|
|
177
|
+
|
|
178
|
+
# Try to extract from error message
|
|
179
|
+
error_str = str(error)
|
|
180
|
+
if "'" in error_str:
|
|
181
|
+
# Extract token literal from error message like "Unexpected token 'catch'"
|
|
182
|
+
start = error_str.find("'") + 1
|
|
183
|
+
end = error_str.find("'", start)
|
|
184
|
+
if start > 0 and end > start:
|
|
185
|
+
token_literal = error_str[start:end]
|
|
186
|
+
# Find matching token in stream
|
|
187
|
+
for token in tokens:
|
|
188
|
+
if token.literal == token_literal:
|
|
189
|
+
return token
|
|
190
|
+
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
def _create_dummy_try_catch(self):
|
|
194
|
+
"""Create a placeholder try-catch statement for recovery"""
|
|
195
|
+
return TryCatchStatement(
|
|
196
|
+
try_block=BlockStatement(),
|
|
197
|
+
error_variable=Identifier("error"),
|
|
198
|
+
catch_block=BlockStatement()
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def _create_synthetic_catch_block(self):
|
|
202
|
+
"""Create a synthetic catch block when none exists"""
|
|
203
|
+
return TryCatchStatement(
|
|
204
|
+
try_block=BlockStatement(),
|
|
205
|
+
error_variable=Identifier("error"),
|
|
206
|
+
catch_block=BlockStatement([ExpressionStatement(StringLiteral("Recovery catch block"))])
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _create_skip_statement(self, skipped_token):
|
|
210
|
+
"""Create a statement that represents skipping a token"""
|
|
211
|
+
return ExpressionStatement(StringLiteral(f"Skipped: {skipped_token.literal}"))
|
|
212
|
+
|
|
213
|
+
def _create_dummy_expression(self):
|
|
214
|
+
"""Create a placeholder expression"""
|
|
215
|
+
return ExpressionStatement(Identifier("recovery_expression"))
|
|
216
|
+
|
|
217
|
+
def _create_dummy_statement(self):
|
|
218
|
+
"""Create a placeholder statement"""
|
|
219
|
+
return ExpressionStatement(StringLiteral("Recovery statement"))
|
|
220
|
+
|
|
221
|
+
def _create_fallback_recovery(self, error, block_info, context):
|
|
222
|
+
"""Final fallback recovery strategy"""
|
|
223
|
+
return {
|
|
224
|
+
'can_recover': True,
|
|
225
|
+
'recovered_statement': self._create_dummy_statement(),
|
|
226
|
+
'next_action': 'skip_tokens',
|
|
227
|
+
'skip_count': 5,
|
|
228
|
+
'message': 'Using fallback recovery - skipping ahead'
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
def apply_recovery_plan(self, recovery_plan, current_tokens, current_index):
|
|
232
|
+
"""Apply the recovery plan to adjust parsing state"""
|
|
233
|
+
if not recovery_plan['can_recover']:
|
|
234
|
+
return current_index
|
|
235
|
+
|
|
236
|
+
action = recovery_plan.get('next_action', 'continue')
|
|
237
|
+
|
|
238
|
+
if action == 'skip_tokens':
|
|
239
|
+
skip_count = recovery_plan.get('skip_count', 1)
|
|
240
|
+
return min(current_index + skip_count, len(current_tokens) - 1)
|
|
241
|
+
|
|
242
|
+
elif action == 'skip_to_token':
|
|
243
|
+
target_index = recovery_plan.get('target_token_index', current_index)
|
|
244
|
+
return min(target_index, len(current_tokens) - 1)
|
|
245
|
+
|
|
246
|
+
elif action == 'skip_to_block':
|
|
247
|
+
# This would require coordination with the main parser
|
|
248
|
+
print("⚠️ [Recovery] Block skipping requires parser integration")
|
|
249
|
+
return current_index + 1
|
|
250
|
+
|
|
251
|
+
elif action == 'insert_token':
|
|
252
|
+
# This would modify the token stream
|
|
253
|
+
print("⚠️ [Recovery] Token insertion requires stream modification")
|
|
254
|
+
return current_index
|
|
255
|
+
|
|
256
|
+
return current_index + 1 # Default: move to next token
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# syntax_validator.py
|
|
2
|
+
# Updated to match actual parser capabilities (parser.py, strategy_context.py, strategy_structural.py)
|
|
3
|
+
# The parsers support flexible syntax: both with and without parentheses, both {} and : block styles
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class SyntaxValidator:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.suggestions = []
|
|
9
|
+
self.warnings = []
|
|
10
|
+
|
|
11
|
+
def validate_code(self, code, desired_style="flexible"):
|
|
12
|
+
"""Validate code and suggest improvements
|
|
13
|
+
|
|
14
|
+
Styles:
|
|
15
|
+
- flexible: Accept both syntaxes (matches parser behavior)
|
|
16
|
+
- universal: Prefer braces {} and parentheses
|
|
17
|
+
- tolerable: More relaxed, warn only on actual errors
|
|
18
|
+
"""
|
|
19
|
+
self.suggestions = []
|
|
20
|
+
self.warnings = []
|
|
21
|
+
|
|
22
|
+
lines = code.split('\n')
|
|
23
|
+
|
|
24
|
+
for i, line in enumerate(lines):
|
|
25
|
+
line_num = i + 1
|
|
26
|
+
self._validate_line(line, line_num, desired_style)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
'is_valid': len(self.suggestions) == 0,
|
|
30
|
+
'suggestions': self.suggestions,
|
|
31
|
+
'warnings': self.warnings,
|
|
32
|
+
'error_count': len(self.suggestions)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def _validate_line(self, line, line_num, style):
|
|
36
|
+
"""Validate a single line against the desired style
|
|
37
|
+
|
|
38
|
+
Parser accepts BOTH styles - with/without parentheses, {}/: blocks
|
|
39
|
+
Validation should only flag actual syntax errors, not style preferences
|
|
40
|
+
"""
|
|
41
|
+
stripped_line = line.strip()
|
|
42
|
+
if not stripped_line or stripped_line.startswith('#'):
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
# Flexible validation (default - matches parser behavior)
|
|
46
|
+
if style == "flexible":
|
|
47
|
+
self._validate_flexible_syntax(stripped_line, line_num, line)
|
|
48
|
+
# Universal style validations (prefer specific style but accept both)
|
|
49
|
+
elif style == "universal":
|
|
50
|
+
self._validate_universal_syntax(stripped_line, line_num, line)
|
|
51
|
+
# Tolerable style validations
|
|
52
|
+
elif style == "tolerable":
|
|
53
|
+
self._validate_tolerable_syntax(stripped_line, line_num, line)
|
|
54
|
+
|
|
55
|
+
# Common validations for all styles (actual errors only)
|
|
56
|
+
self._validate_common_syntax(stripped_line, line_num, line)
|
|
57
|
+
|
|
58
|
+
def _validate_flexible_syntax(self, stripped_line, line_num, original_line):
|
|
59
|
+
"""Validate for actual syntax errors only - accept both styles
|
|
60
|
+
|
|
61
|
+
The parser supports:
|
|
62
|
+
- if (condition) { } AND if condition { } AND if condition:
|
|
63
|
+
- while (condition) { } AND while condition { } AND while condition:
|
|
64
|
+
- action name(params) { } AND action name params { }
|
|
65
|
+
- catch(error) { } AND catch (error) { }
|
|
66
|
+
- print(value) AND print (value) - both are valid
|
|
67
|
+
"""
|
|
68
|
+
# Only flag actual errors that parser would reject
|
|
69
|
+
|
|
70
|
+
# Check for space between function name and parenthesis (commonly flagged but actually valid)
|
|
71
|
+
# DON'T warn about "print (" - parser accepts this
|
|
72
|
+
|
|
73
|
+
# Check for unmatched braces (actual error)
|
|
74
|
+
open_braces = stripped_line.count('{') - stripped_line.count('}')
|
|
75
|
+
open_parens = stripped_line.count('(') - stripped_line.count(')')
|
|
76
|
+
open_brackets = stripped_line.count('[') - stripped_line.count(']')
|
|
77
|
+
|
|
78
|
+
if abs(open_braces) > 2 or abs(open_parens) > 2 or abs(open_brackets) > 2:
|
|
79
|
+
# Only warn if significantly unbalanced (might span multiple lines)
|
|
80
|
+
self.warnings.append({
|
|
81
|
+
'line': line_num,
|
|
82
|
+
'message': "Potentially unmatched delimiters - check braces/parentheses/brackets",
|
|
83
|
+
'severity': 'warning'
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
# Check for obvious typos in keywords
|
|
87
|
+
common_typos = {
|
|
88
|
+
'fucntion': 'function',
|
|
89
|
+
'acton': 'action',
|
|
90
|
+
'retrun': 'return',
|
|
91
|
+
'esle': 'else',
|
|
92
|
+
'wile': 'while',
|
|
93
|
+
'forach': 'foreach',
|
|
94
|
+
}
|
|
95
|
+
for typo, correct in common_typos.items():
|
|
96
|
+
if typo in stripped_line:
|
|
97
|
+
self.suggestions.append({
|
|
98
|
+
'line': line_num,
|
|
99
|
+
'message': f"Possible typo: '{typo}' should be '{correct}'",
|
|
100
|
+
'fix': original_line.replace(typo, correct),
|
|
101
|
+
'severity': 'error'
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
def _validate_universal_syntax(self, stripped_line, line_num, original_line):
|
|
105
|
+
"""Validate and suggest universal syntax style (preference, not requirement)
|
|
106
|
+
|
|
107
|
+
NOTE: Parser accepts BOTH styles, so these are style suggestions, not errors
|
|
108
|
+
"""
|
|
109
|
+
# REMOVED: Colon block check - parser fully supports colon blocks
|
|
110
|
+
# The parser accepts both: if condition { } AND if condition:
|
|
111
|
+
# So we should NOT suggest changing : to {}
|
|
112
|
+
|
|
113
|
+
# REMOVED: Parentheses suggestions for if/while
|
|
114
|
+
# Parser accepts: if (x) { }, if x { }, if x:
|
|
115
|
+
# All are valid, so don't suggest changes
|
|
116
|
+
|
|
117
|
+
# Check for double parentheses in catch (actual syntax issue)
|
|
118
|
+
if 'catch' in stripped_line and 'catch((' in stripped_line and '))' in stripped_line:
|
|
119
|
+
self.suggestions.append({
|
|
120
|
+
'line': line_num,
|
|
121
|
+
'message': "Remove extra parentheses in catch: catch(error) { }",
|
|
122
|
+
'fix': self._fix_double_parentheses_catch(original_line),
|
|
123
|
+
'severity': 'error'
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
def _validate_tolerable_syntax(self, stripped_line, line_num, original_line):
|
|
127
|
+
"""Validate against tolerable syntax rules"""
|
|
128
|
+
# Check for potentially confusing syntax
|
|
129
|
+
if stripped_line.count('{') != stripped_line.count('}'):
|
|
130
|
+
self.warnings.append({
|
|
131
|
+
'line': line_num,
|
|
132
|
+
'message': "Mismatched braces - this can cause parsing issues",
|
|
133
|
+
'severity': 'warning'
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
# Check for mixed block styles in same context
|
|
137
|
+
if (any(stripped_line.startswith(keyword) for keyword in ['if', 'for each', 'while'])
|
|
138
|
+
and ':' in stripped_line and '{' in stripped_line):
|
|
139
|
+
self.suggestions.append({
|
|
140
|
+
'line': line_num,
|
|
141
|
+
'message': "Mixed block syntax - prefer consistent use of : or {}",
|
|
142
|
+
'fix': original_line,
|
|
143
|
+
'severity': 'warning'
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
def _validate_common_syntax(self, stripped_line, line_num, original_line):
|
|
147
|
+
"""Common validations for all syntax styles - only flag real errors"""
|
|
148
|
+
|
|
149
|
+
# DON'T check for missing parentheses - parser accepts both styles
|
|
150
|
+
# The old check was:
|
|
151
|
+
# if 'if' in line and '(' not in line: suggest parentheses
|
|
152
|
+
# This is WRONG - parser accepts: if condition { }
|
|
153
|
+
|
|
154
|
+
# Check for assignment in conditions (common bug - actual error)
|
|
155
|
+
if 'if' in stripped_line and ' = ' in stripped_line and ' == ' not in stripped_line:
|
|
156
|
+
# Make sure it's not a string literal
|
|
157
|
+
if stripped_line.count('"') % 2 == 0 and stripped_line.count("'") % 2 == 0:
|
|
158
|
+
self.warnings.append({
|
|
159
|
+
'line': line_num,
|
|
160
|
+
'message': "Possible assignment in condition - did you mean '=='?",
|
|
161
|
+
'severity': 'warning'
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
# Check try-catch structure
|
|
165
|
+
self._validate_try_catch_structure(stripped_line, line_num, original_line)
|
|
166
|
+
|
|
167
|
+
def _validate_try_catch_structure(self, stripped_line, line_num, original_line):
|
|
168
|
+
"""Validate try-catch block structure
|
|
169
|
+
|
|
170
|
+
Parser accepts multiple catch formats:
|
|
171
|
+
- catch(error) { }
|
|
172
|
+
- catch (error) { } (space before paren is OK)
|
|
173
|
+
- catch error { } (no parens in some contexts)
|
|
174
|
+
"""
|
|
175
|
+
# Only check for catch without ANY error parameter
|
|
176
|
+
if 'catch' in stripped_line:
|
|
177
|
+
# Check for catch immediately followed by { (no error variable at all)
|
|
178
|
+
if re.search(r'catch\s*\{', stripped_line):
|
|
179
|
+
self.suggestions.append({
|
|
180
|
+
'line': line_num,
|
|
181
|
+
'message': "Catch block should include error parameter: catch(error) { }",
|
|
182
|
+
'fix': original_line.replace('catch{', 'catch(error) {').replace('catch {', 'catch(error) {'),
|
|
183
|
+
'severity': 'warning'
|
|
184
|
+
})
|
|
185
|
+
# Check for double parentheses (actual error)
|
|
186
|
+
elif 'catch((' in stripped_line and '))' in stripped_line:
|
|
187
|
+
self.suggestions.append({
|
|
188
|
+
'line': line_num,
|
|
189
|
+
'message': "Remove extra parentheses in catch: catch(error) { }",
|
|
190
|
+
'fix': self._fix_double_parentheses_catch(original_line),
|
|
191
|
+
'severity': 'error'
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
def _fix_catch_syntax(self, line):
|
|
195
|
+
"""Fix catch syntax from 'catch error { }' to 'catch(error) { }'"""
|
|
196
|
+
# Pattern: catch followed by identifier then {
|
|
197
|
+
pattern = r'catch\s+(\w+)\s*\{'
|
|
198
|
+
replacement = r'catch(\1) {'
|
|
199
|
+
fixed_line = re.sub(pattern, replacement, line)
|
|
200
|
+
|
|
201
|
+
if fixed_line != line:
|
|
202
|
+
return fixed_line
|
|
203
|
+
|
|
204
|
+
# Fallback: simple string replacement
|
|
205
|
+
if 'catch ' in line and '{' in line:
|
|
206
|
+
# Extract the error variable name
|
|
207
|
+
parts = line.split('catch ', 1)[1].split('{', 1)
|
|
208
|
+
if len(parts) == 2:
|
|
209
|
+
error_var = parts[0].strip()
|
|
210
|
+
rest = parts[1]
|
|
211
|
+
return line.split('catch ', 1)[0] + f'catch({error_var}) {{' + rest
|
|
212
|
+
|
|
213
|
+
return line
|
|
214
|
+
|
|
215
|
+
def _fix_double_parentheses_catch(self, line):
|
|
216
|
+
"""Fix catch with double parentheses: catch((error)) to catch(error)"""
|
|
217
|
+
# Pattern: catch((...))
|
|
218
|
+
pattern = r'catch\(\s*\(\s*(\w+)\s*\)\s*\)'
|
|
219
|
+
replacement = r'catch(\1)'
|
|
220
|
+
fixed_line = re.sub(pattern, replacement, line)
|
|
221
|
+
|
|
222
|
+
if fixed_line != line:
|
|
223
|
+
return fixed_line
|
|
224
|
+
|
|
225
|
+
# Fallback: simple string replacement
|
|
226
|
+
if 'catch((' in line and '))' in line:
|
|
227
|
+
start = line.find('catch((') + 7 # after 'catch(('
|
|
228
|
+
end = line.find('))', start)
|
|
229
|
+
if start > 0 and end > start:
|
|
230
|
+
error_var = line[start:end].strip()
|
|
231
|
+
return line.replace(f'catch(({error_var}))', f'catch({error_var})')
|
|
232
|
+
|
|
233
|
+
return line
|
|
234
|
+
|
|
235
|
+
def _fix_lambda_syntax(self, line):
|
|
236
|
+
"""Fix lambda syntax to universal style"""
|
|
237
|
+
if 'lambda ' in line:
|
|
238
|
+
# Simple case: lambda x: expr -> lambda(x) -> expr
|
|
239
|
+
if ':' in line:
|
|
240
|
+
return line.replace('lambda ', 'lambda(', 1).replace(':', ') ->', 1)
|
|
241
|
+
# Case with arrow: lambda x -> expr -> lambda(x) -> expr
|
|
242
|
+
elif '->' in line:
|
|
243
|
+
return line.replace('lambda ', 'lambda(', 1).replace(' ->', ') ->', 1)
|
|
244
|
+
return line
|
|
245
|
+
|
|
246
|
+
def _add_parentheses_to_condition(self, line):
|
|
247
|
+
"""Add parentheses around condition"""
|
|
248
|
+
if line.startswith('if '):
|
|
249
|
+
return line.replace('if ', 'if (', 1) + ')'
|
|
250
|
+
elif line.startswith('while '):
|
|
251
|
+
return line.replace('while ', 'while (', 1) + ')'
|
|
252
|
+
return line
|
|
253
|
+
|
|
254
|
+
def auto_fix(self, code, desired_style="flexible"):
|
|
255
|
+
"""Attempt to automatically fix syntax issues
|
|
256
|
+
|
|
257
|
+
Only fixes actual errors, not style preferences (when using flexible mode)
|
|
258
|
+
"""
|
|
259
|
+
validation = self.validate_code(code, desired_style)
|
|
260
|
+
|
|
261
|
+
if validation['is_valid']:
|
|
262
|
+
return code, validation
|
|
263
|
+
|
|
264
|
+
lines = code.split('\n')
|
|
265
|
+
fixed_lines = lines.copy()
|
|
266
|
+
applied_fixes = 0
|
|
267
|
+
|
|
268
|
+
# Group suggestions by line to avoid conflicts
|
|
269
|
+
line_suggestions = {}
|
|
270
|
+
for suggestion in validation['suggestions']:
|
|
271
|
+
# In flexible mode, only auto-fix errors, not suggestions/warnings
|
|
272
|
+
if desired_style == "flexible" and suggestion['severity'] not in ['error']:
|
|
273
|
+
continue
|
|
274
|
+
|
|
275
|
+
if suggestion['severity'] in ['error', 'warning']:
|
|
276
|
+
line_num = suggestion['line'] - 1
|
|
277
|
+
if line_num not in line_suggestions:
|
|
278
|
+
line_suggestions[line_num] = []
|
|
279
|
+
line_suggestions[line_num].append(suggestion)
|
|
280
|
+
|
|
281
|
+
# Apply fixes line by line
|
|
282
|
+
for line_num, suggestions in line_suggestions.items():
|
|
283
|
+
if line_num < len(fixed_lines):
|
|
284
|
+
current_line = fixed_lines[line_num]
|
|
285
|
+
fixed_line = current_line
|
|
286
|
+
|
|
287
|
+
# Apply fixes in order
|
|
288
|
+
for suggestion in suggestions:
|
|
289
|
+
if 'fix' in suggestion:
|
|
290
|
+
fixed_line = suggestion['fix']
|
|
291
|
+
applied_fixes += 1
|
|
292
|
+
|
|
293
|
+
fixed_lines[line_num] = fixed_line
|
|
294
|
+
|
|
295
|
+
fixed_code = '\n'.join(fixed_lines)
|
|
296
|
+
|
|
297
|
+
# Re-validate after fixes
|
|
298
|
+
final_validation = self.validate_code(fixed_code, desired_style)
|
|
299
|
+
final_validation['applied_fixes'] = applied_fixes
|
|
300
|
+
|
|
301
|
+
return fixed_code, final_validation
|
|
302
|
+
|
|
303
|
+
def suggest_syntax_style(self, code):
|
|
304
|
+
"""Analyze code and suggest which syntax style it follows"""
|
|
305
|
+
lines = code.split('\n')
|
|
306
|
+
|
|
307
|
+
universal_indicators = 0
|
|
308
|
+
tolerable_indicators = 0
|
|
309
|
+
|
|
310
|
+
for line in lines:
|
|
311
|
+
stripped = line.strip()
|
|
312
|
+
if not stripped or stripped.startswith('#'):
|
|
313
|
+
continue
|
|
314
|
+
|
|
315
|
+
# Universal indicators
|
|
316
|
+
if any(stripped.startswith(kw + '(') for kw in ['if', 'while', 'debug']):
|
|
317
|
+
universal_indicators += 1
|
|
318
|
+
if 'lambda(' in stripped:
|
|
319
|
+
universal_indicators += 1
|
|
320
|
+
if stripped.endswith('{'):
|
|
321
|
+
universal_indicators += 1
|
|
322
|
+
if 'catch(' in stripped and 'catch((' not in stripped:
|
|
323
|
+
universal_indicators += 1
|
|
324
|
+
|
|
325
|
+
# Tolerable indicators
|
|
326
|
+
if any(stripped.startswith(kw + ' ') and stripped.endswith(':')
|
|
327
|
+
for kw in ['if', 'for each', 'while', 'action']):
|
|
328
|
+
tolerable_indicators += 1
|
|
329
|
+
if 'debug ' in stripped and not stripped.startswith('debug('):
|
|
330
|
+
tolerable_indicators += 1
|
|
331
|
+
if 'lambda ' in stripped and not 'lambda(' in stripped:
|
|
332
|
+
tolerable_indicators += 1
|
|
333
|
+
if 'catch ' in stripped and not 'catch(' in stripped:
|
|
334
|
+
tolerable_indicators += 1
|
|
335
|
+
if 'catch((' in stripped:
|
|
336
|
+
tolerable_indicators += 1
|
|
337
|
+
|
|
338
|
+
if universal_indicators > tolerable_indicators:
|
|
339
|
+
return "universal"
|
|
340
|
+
elif tolerable_indicators > universal_indicators:
|
|
341
|
+
return "tolerable"
|
|
342
|
+
else:
|
|
343
|
+
return "mixed"
|
|
344
|
+
|
|
345
|
+
def get_fix_summary(self, validation_result):
|
|
346
|
+
"""Get a summary of fixes that will be applied"""
|
|
347
|
+
error_fixes = [s for s in validation_result['suggestions'] if s['severity'] == 'error']
|
|
348
|
+
warning_fixes = [s for s in validation_result['suggestions'] if s['severity'] == 'warning']
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
'total_fixes': len(validation_result['suggestions']),
|
|
352
|
+
'error_fixes': len(error_fixes),
|
|
353
|
+
'warning_fixes': len(warning_fixes),
|
|
354
|
+
'will_fix_errors': len(error_fixes) > 0,
|
|
355
|
+
'will_fix_warnings': len(warning_fixes) > 0
|
|
356
|
+
}
|