IncludeCPP 4.5.2__py3-none-any.whl → 4.9.3__py3-none-any.whl
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.
- includecpp/CHANGELOG.md +241 -0
- includecpp/__init__.py +89 -3
- includecpp/__init__.pyi +2 -1
- includecpp/cli/commands.py +1747 -266
- includecpp/cli/config_parser.py +1 -1
- includecpp/core/build_manager.py +64 -13
- includecpp/core/cpp_api_extensions.pyi +43 -270
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +1799 -1445
- includecpp/core/cssl/cpp/build/api.pyd +0 -0
- includecpp/core/cssl/cpp/build/api.pyi +274 -0
- includecpp/core/cssl/cpp/build/cssl_core.pyi +0 -99
- includecpp/core/cssl/cpp/cssl_core.cp +2 -23
- includecpp/core/cssl/cssl_builtins.py +2116 -171
- includecpp/core/cssl/cssl_builtins.pyi +1324 -104
- includecpp/core/cssl/cssl_compiler.py +4 -1
- includecpp/core/cssl/cssl_modules.py +605 -6
- includecpp/core/cssl/cssl_optimizer.py +12 -1
- includecpp/core/cssl/cssl_parser.py +1048 -52
- includecpp/core/cssl/cssl_runtime.py +2041 -131
- includecpp/core/cssl/cssl_syntax.py +405 -277
- includecpp/core/cssl/cssl_types.py +5891 -1655
- includecpp/core/cssl_bridge.py +429 -3
- includecpp/core/error_catalog.py +54 -10
- includecpp/core/homeserver.py +1037 -0
- includecpp/generator/parser.cpp +203 -39
- includecpp/generator/parser.h +15 -1
- includecpp/templates/cpp.proj.template +1 -1
- includecpp/vscode/cssl/snippets/cssl.snippets.json +163 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +87 -12
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/METADATA +81 -10
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/RECORD +35 -33
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/WHEEL +1 -1
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/entry_points.txt +0 -0
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/top_level.txt +0 -0
|
@@ -84,6 +84,9 @@ class TokenType(Enum):
|
|
|
84
84
|
AND = auto()
|
|
85
85
|
OR = auto()
|
|
86
86
|
NOT = auto()
|
|
87
|
+
TILDE = auto() # ~ for destructors (v4.8.8)
|
|
88
|
+
CARET = auto() # ^ for byte notation (v4.9.0)
|
|
89
|
+
QUESTION = auto() # ? for pointer references (v4.9.0)
|
|
87
90
|
AMPERSAND = auto() # & for references
|
|
88
91
|
BLOCK_START = auto()
|
|
89
92
|
BLOCK_END = auto()
|
|
@@ -101,7 +104,9 @@ class TokenType(Enum):
|
|
|
101
104
|
SELF_REF = auto() # s@<name> self-reference to global struct
|
|
102
105
|
SHARED_REF = auto() # $<name> shared object reference
|
|
103
106
|
CAPTURED_REF = auto() # %<name> captured reference (for infusion)
|
|
107
|
+
POINTER_REF = auto() # ?<name> pointer reference (v4.9.0)
|
|
104
108
|
THIS_REF = auto() # this-><name> class member reference
|
|
109
|
+
LOCAL_REF = auto() # local::<name> hooked function local variable access (v4.9.2)
|
|
105
110
|
PACKAGE = auto()
|
|
106
111
|
PACKAGE_INCLUDES = auto()
|
|
107
112
|
AS = auto()
|
|
@@ -115,15 +120,20 @@ class TokenType(Enum):
|
|
|
115
120
|
MINUS_MINUS = auto() # -- for potential future use
|
|
116
121
|
# Multi-language support (v4.1.0)
|
|
117
122
|
LANG_INSTANCE_REF = auto() # cpp$InstanceName, py$Object - cross-language instance access
|
|
123
|
+
# v4.8.4: Pipe operator and stream operators
|
|
124
|
+
PIPE = auto() # | for data piping
|
|
125
|
+
STREAM_OUT = auto() # << for stream output (cout << x)
|
|
126
|
+
STREAM_IN = auto() # >> for stream input (cin >> x)
|
|
127
|
+
IN = auto() # 'in' keyword for containment check
|
|
118
128
|
|
|
119
129
|
|
|
120
130
|
KEYWORDS = {
|
|
121
131
|
# Service structure
|
|
122
|
-
'service-init', 'service-run', 'service-include', 'struct', 'define', '
|
|
132
|
+
'service-init', 'service-run', 'service-include', 'struct', 'define', 'class', 'namespace', 'constr', 'extends', 'overwrites', 'new', 'this', 'super', 'enum',
|
|
123
133
|
# Control flow
|
|
124
134
|
'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
|
|
125
135
|
'switch', 'case', 'default', 'break', 'continue', 'return',
|
|
126
|
-
'try', 'catch', 'finally', 'throw',
|
|
136
|
+
'try', 'catch', 'finally', 'throw', 'raise',
|
|
127
137
|
'except', 'always', # v4.2.5: param switch keywords
|
|
128
138
|
# Literals
|
|
129
139
|
'True', 'False', 'null', 'None', 'true', 'false',
|
|
@@ -131,11 +141,14 @@ KEYWORDS = {
|
|
|
131
141
|
'and', 'or', 'not',
|
|
132
142
|
# Async/Events
|
|
133
143
|
'start', 'stop', 'wait_for', 'on_event', 'emit_event', 'await',
|
|
144
|
+
'async', 'yield', 'generator', 'future', # v4.9.3: Full async/generator support
|
|
134
145
|
# Package system
|
|
135
146
|
'package', 'package-includes', 'exec', 'as', 'global',
|
|
136
147
|
# CSSL Type Keywords
|
|
137
148
|
'int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
138
|
-
'list', 'dictionary', 'dict', 'instance', 'map', # Python-like types
|
|
149
|
+
'list', 'dictionary', 'dict', 'instance', 'map', 'queue', # Python-like types + queue (v4.7)
|
|
150
|
+
'bit', 'byte', 'address', 'ptr', 'pointer', # v4.9.0: Binary types, address, ptr/pointer
|
|
151
|
+
'local', # v4.9.2: Hook local variable access (local::varname)
|
|
139
152
|
'dynamic', # No type declaration (slow but flexible)
|
|
140
153
|
'undefined', # Function errors ignored
|
|
141
154
|
'open', # Accept any parameter type
|
|
@@ -159,17 +172,25 @@ KEYWORDS = {
|
|
|
159
172
|
'static', # Static method/function
|
|
160
173
|
'embedded', # Immediate &target replacement at registration (v4.2.5)
|
|
161
174
|
'native', # Force C++ execution (no Python fallback)
|
|
175
|
+
'unative', # Force Python execution (no C++ - opposite of native)
|
|
176
|
+
'secure', # v4.8.8: Constructor runs only on exception
|
|
177
|
+
'callable', # v4.8.8: Constructor must be manually called
|
|
162
178
|
# CSSL Include Keywords
|
|
163
|
-
'include', 'get',
|
|
179
|
+
'include', # v4.8.6: Removed 'get' from keywords - it's a builtin function, not keyword
|
|
164
180
|
# Multi-language support (v4.1.0)
|
|
165
181
|
'supports', 'libinclude',
|
|
182
|
+
# Memory binding (v4.9.0)
|
|
183
|
+
'uses', 'memory',
|
|
166
184
|
}
|
|
167
185
|
|
|
168
186
|
# Function modifiers that can appear in any order before function name
|
|
169
187
|
FUNCTION_MODIFIERS = {
|
|
170
188
|
'undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual',
|
|
171
189
|
'sqlbased', 'const', 'public', 'static', 'global', 'shuffled', 'embedded',
|
|
172
|
-
'native' # Force C++ execution
|
|
190
|
+
'native', # Force C++ execution
|
|
191
|
+
'unative', # Force Python execution (opposite of native)
|
|
192
|
+
'bytearrayed', # v4.7: Function-to-byte mapping with pattern matching
|
|
193
|
+
'async', # v4.9.3: Async function modifier
|
|
173
194
|
}
|
|
174
195
|
|
|
175
196
|
# Type literals that create empty instances
|
|
@@ -178,7 +199,9 @@ TYPE_LITERALS = {'list', 'dict'}
|
|
|
178
199
|
# Generic type keywords that use <T> syntax
|
|
179
200
|
TYPE_GENERICS = {
|
|
180
201
|
'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo',
|
|
181
|
-
'vector', 'stack', 'array', 'openquote', 'list', 'dictionary', 'map'
|
|
202
|
+
'vector', 'stack', 'array', 'openquote', 'list', 'dictionary', 'map',
|
|
203
|
+
'queue', # v4.7: Thread-safe queue with multi-iterator support
|
|
204
|
+
'generator', 'future', # v4.9.3: Async types
|
|
182
205
|
}
|
|
183
206
|
|
|
184
207
|
# Functions that accept type parameters: FuncName<type>(args)
|
|
@@ -271,6 +294,14 @@ class CSSLLexer:
|
|
|
271
294
|
# s@<name> self-reference to global struct
|
|
272
295
|
self._read_self_ref()
|
|
273
296
|
elif char.isalpha() or char == '_':
|
|
297
|
+
# v4.8.6: Detect f-string syntax and throw clear error
|
|
298
|
+
if char == 'f' and self._peek(1) in ('"', "'"):
|
|
299
|
+
raise CSSLSyntaxError(
|
|
300
|
+
f"f-string syntax is not supported in CSSL. Use string concatenation instead:\n"
|
|
301
|
+
f" Instead of: f\"Hello {{name}}\"\n"
|
|
302
|
+
f" Use: \"Hello \" + name",
|
|
303
|
+
line=self.line
|
|
304
|
+
)
|
|
274
305
|
self._read_identifier()
|
|
275
306
|
elif char == '@':
|
|
276
307
|
self._add_token(TokenType.AT, '@')
|
|
@@ -379,6 +410,26 @@ class CSSLLexer:
|
|
|
379
410
|
self._advance()
|
|
380
411
|
self._advance()
|
|
381
412
|
else:
|
|
413
|
+
# v4.8.4: Single | is pipe operator
|
|
414
|
+
self._add_token(TokenType.PIPE, '|')
|
|
415
|
+
self._advance()
|
|
416
|
+
elif char == '~':
|
|
417
|
+
# v4.8.8: Tilde for destructors (constr ~Name())
|
|
418
|
+
self._add_token(TokenType.TILDE, '~')
|
|
419
|
+
self._advance()
|
|
420
|
+
elif char == '^':
|
|
421
|
+
# v4.9.0: Caret for byte notation (1^250, 0^102)
|
|
422
|
+
self._add_token(TokenType.CARET, '^')
|
|
423
|
+
self._advance()
|
|
424
|
+
elif char == '?':
|
|
425
|
+
# v4.9.0: Check if this is ?<name> pointer reference
|
|
426
|
+
next_char = self._peek(1)
|
|
427
|
+
if next_char and (next_char.isalpha() or next_char == '_'):
|
|
428
|
+
# ?<name> pointer reference
|
|
429
|
+
self._read_pointer_ref()
|
|
430
|
+
else:
|
|
431
|
+
# Just ? (ternary operator or other use)
|
|
432
|
+
self._add_token(TokenType.QUESTION, '?')
|
|
382
433
|
self._advance()
|
|
383
434
|
else:
|
|
384
435
|
self._advance()
|
|
@@ -540,6 +591,20 @@ class CSSLLexer:
|
|
|
540
591
|
# NEW: 'as' keyword for foreach ... as ... syntax
|
|
541
592
|
self._add_token(TokenType.AS, value)
|
|
542
593
|
elif value in KEYWORDS:
|
|
594
|
+
# v4.9.2: Check for local:: syntax for hook local variable access
|
|
595
|
+
if value == 'local' and self.pos + 1 < len(self.source) and self.source[self.pos:self.pos+2] == '::':
|
|
596
|
+
self._advance() # skip first :
|
|
597
|
+
self._advance() # skip second :
|
|
598
|
+
# Read the local variable/function name
|
|
599
|
+
local_start = self.pos
|
|
600
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
601
|
+
self._advance()
|
|
602
|
+
local_name = self.source[local_start:self.pos]
|
|
603
|
+
if local_name:
|
|
604
|
+
self._add_token(TokenType.LOCAL_REF, local_name)
|
|
605
|
+
return
|
|
606
|
+
# If no name, revert to keyword
|
|
607
|
+
self.pos = start + len(value)
|
|
543
608
|
self._add_token(TokenType.KEYWORD, value)
|
|
544
609
|
else:
|
|
545
610
|
self._add_token(TokenType.IDENTIFIER, value)
|
|
@@ -636,11 +701,30 @@ class CSSLLexer:
|
|
|
636
701
|
self.error("Expected identifier after '%'")
|
|
637
702
|
self._add_token(TokenType.CAPTURED_REF, value)
|
|
638
703
|
|
|
704
|
+
def _read_pointer_ref(self):
|
|
705
|
+
"""Read ?<name> pointer reference (v4.9.0)"""
|
|
706
|
+
self._advance() # skip '?'
|
|
707
|
+
|
|
708
|
+
# Read the identifier (pointer reference name)
|
|
709
|
+
name_start = self.pos
|
|
710
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
711
|
+
self._advance()
|
|
712
|
+
|
|
713
|
+
value = self.source[name_start:self.pos]
|
|
714
|
+
if not value:
|
|
715
|
+
self.error("Expected identifier after '?'")
|
|
716
|
+
self._add_token(TokenType.POINTER_REF, value)
|
|
717
|
+
|
|
639
718
|
def _read_less_than(self):
|
|
640
719
|
# Check for <<== (code infusion left)
|
|
641
720
|
if self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
|
|
642
721
|
self._add_token(TokenType.INFUSE_LEFT, '<<==')
|
|
643
722
|
for _ in range(4): self._advance()
|
|
723
|
+
# v4.8.4: Check for << (stream output operator) - but NOT <<==
|
|
724
|
+
elif self._peek(1) == '<' and self._peek(2) != '=':
|
|
725
|
+
self._add_token(TokenType.STREAM_OUT, '<<')
|
|
726
|
+
self._advance()
|
|
727
|
+
self._advance()
|
|
644
728
|
# Check for <== (basic injection left)
|
|
645
729
|
elif self._peek(1) == '=' and self._peek(2) == '=':
|
|
646
730
|
self._add_token(TokenType.INJECT_LEFT, '<==')
|
|
@@ -662,6 +746,11 @@ class CSSLLexer:
|
|
|
662
746
|
self._add_token(TokenType.COMPARE_GE, '>=')
|
|
663
747
|
self._advance()
|
|
664
748
|
self._advance()
|
|
749
|
+
# v4.8.4: Check for >> (stream input operator)
|
|
750
|
+
elif self._peek(1) == '>':
|
|
751
|
+
self._add_token(TokenType.STREAM_IN, '>>')
|
|
752
|
+
self._advance()
|
|
753
|
+
self._advance()
|
|
665
754
|
else:
|
|
666
755
|
self._add_token(TokenType.COMPARE_GT, '>')
|
|
667
756
|
self._advance()
|
|
@@ -805,7 +894,9 @@ class CSSLParser:
|
|
|
805
894
|
"""Check if a keyword is a type declaration"""
|
|
806
895
|
return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
807
896
|
'list', 'dictionary', 'dict', 'instance', 'map', 'openquote', 'parameter',
|
|
808
|
-
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure'
|
|
897
|
+
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure',
|
|
898
|
+
'queue', # v4.7: Thread-safe queue
|
|
899
|
+
'bit', 'byte', 'address', 'ptr', 'pointer') # v4.9.0: Binary types, address, ptr/pointer
|
|
809
900
|
|
|
810
901
|
def _parse_generic_type_content(self) -> str:
|
|
811
902
|
"""Parse generic type content including nested generics.
|
|
@@ -833,6 +924,9 @@ class CSSLParser:
|
|
|
833
924
|
self._advance()
|
|
834
925
|
elif self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
835
926
|
parts.append(self._advance().value)
|
|
927
|
+
elif self._check(TokenType.NUMBER):
|
|
928
|
+
# v4.7: For queue<type, size> where size is a number
|
|
929
|
+
parts.append(str(int(self._advance().value)))
|
|
836
930
|
elif self._check(TokenType.STRING):
|
|
837
931
|
# For instance<"name">
|
|
838
932
|
parts.append(f'"{self._advance().value}"')
|
|
@@ -880,7 +974,9 @@ class CSSLParser:
|
|
|
880
974
|
return False # Let _parse_define handle this
|
|
881
975
|
|
|
882
976
|
# Check for type keyword (int, string, void, vector, datastruct, etc.)
|
|
883
|
-
|
|
977
|
+
# v4.8: Also handle TYPE_LITERAL (dict, list) and custom class types (IDENTIFIER)
|
|
978
|
+
if (self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or \
|
|
979
|
+
self._check(TokenType.TYPE_LITERAL):
|
|
884
980
|
self._advance()
|
|
885
981
|
has_type = True
|
|
886
982
|
|
|
@@ -895,6 +991,31 @@ class CSSLParser:
|
|
|
895
991
|
depth -= 1
|
|
896
992
|
self._advance()
|
|
897
993
|
|
|
994
|
+
# v4.8: Check for custom class type (IDENTIFIER followed by another IDENTIFIER)
|
|
995
|
+
# Pattern: CustomClass funcName() { } where CustomClass is a user-defined class
|
|
996
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
997
|
+
# Save position and check if this is "Type Name(" pattern
|
|
998
|
+
inner_saved = self.pos
|
|
999
|
+
self._advance() # Skip potential type
|
|
1000
|
+
|
|
1001
|
+
# Skip generic type parameters if present
|
|
1002
|
+
if self._check(TokenType.COMPARE_LT):
|
|
1003
|
+
depth = 1
|
|
1004
|
+
self._advance()
|
|
1005
|
+
while depth > 0 and not self._is_at_end():
|
|
1006
|
+
if self._check(TokenType.COMPARE_LT):
|
|
1007
|
+
depth += 1
|
|
1008
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
1009
|
+
depth -= 1
|
|
1010
|
+
self._advance()
|
|
1011
|
+
|
|
1012
|
+
# Check if followed by another identifier (function name)
|
|
1013
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1014
|
+
has_type = True
|
|
1015
|
+
else:
|
|
1016
|
+
# Not a "Type Name" pattern, restore position
|
|
1017
|
+
self.pos = inner_saved
|
|
1018
|
+
|
|
898
1019
|
# Check for * prefix (non-null) or *[type] (type exclusion)
|
|
899
1020
|
if self._check(TokenType.MULTIPLY):
|
|
900
1021
|
self._advance()
|
|
@@ -945,13 +1066,17 @@ class CSSLParser:
|
|
|
945
1066
|
- int x;
|
|
946
1067
|
- stack<string> myStack;
|
|
947
1068
|
- vector<int> nums = [1,2,3];
|
|
1069
|
+
- list<int> myList; (v4.8.7: list/dict are TYPE_LITERAL tokens)
|
|
948
1070
|
|
|
949
1071
|
Distinguishes from function declarations by checking for '(' after identifier.
|
|
950
1072
|
"""
|
|
951
1073
|
saved_pos = self.pos
|
|
952
1074
|
|
|
953
1075
|
# Check for type keyword
|
|
954
|
-
|
|
1076
|
+
# v4.8.7: Also check TYPE_LITERAL (list, dict) which are tokenized differently
|
|
1077
|
+
is_type_token = ((self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or
|
|
1078
|
+
(self._check(TokenType.TYPE_LITERAL) and self._current().value in ('list', 'dict')))
|
|
1079
|
+
if is_type_token:
|
|
955
1080
|
self._advance()
|
|
956
1081
|
|
|
957
1082
|
# Skip generic type parameters <T>
|
|
@@ -976,7 +1101,7 @@ class CSSLParser:
|
|
|
976
1101
|
self.pos = saved_pos
|
|
977
1102
|
return False
|
|
978
1103
|
|
|
979
|
-
def _parse_typed_function(self, is_global: bool = False, is_embedded: bool = False) -> ASTNode:
|
|
1104
|
+
def _parse_typed_function(self, is_global: bool = False, is_embedded: bool = False, modifiers: list = None) -> ASTNode:
|
|
980
1105
|
"""Parse C-style typed function declaration with flexible modifier ordering.
|
|
981
1106
|
|
|
982
1107
|
Supports any order of modifiers, types, non-null (*), and global (@):
|
|
@@ -1000,7 +1125,7 @@ class CSSLParser:
|
|
|
1000
1125
|
Functions with 'meta' modifier can return any type regardless of declaration.
|
|
1001
1126
|
Functions with 'define' are dynamic (any return type allowed).
|
|
1002
1127
|
"""
|
|
1003
|
-
modifiers = []
|
|
1128
|
+
modifiers = modifiers if modifiers is not None else []
|
|
1004
1129
|
return_type = None
|
|
1005
1130
|
generic_type = None
|
|
1006
1131
|
non_null = False
|
|
@@ -1030,7 +1155,9 @@ class CSSLParser:
|
|
|
1030
1155
|
continue
|
|
1031
1156
|
|
|
1032
1157
|
# Check for type keyword (int, string, void, vector, datastruct, etc.)
|
|
1033
|
-
|
|
1158
|
+
# v4.8: Also handle TYPE_LITERAL (dict, list) as return types
|
|
1159
|
+
if ((self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or
|
|
1160
|
+
self._check(TokenType.TYPE_LITERAL)) and return_type is None:
|
|
1034
1161
|
return_type = self._advance().value
|
|
1035
1162
|
|
|
1036
1163
|
# Check for generic type <T> or <T, U>
|
|
@@ -1054,6 +1181,32 @@ class CSSLParser:
|
|
|
1054
1181
|
generic_type = ''.join(generic_parts)
|
|
1055
1182
|
continue
|
|
1056
1183
|
|
|
1184
|
+
# v4.8: Check for custom class type as return type (IDENTIFIER followed by another IDENTIFIER)
|
|
1185
|
+
# Pattern: CustomClass funcName() { } where CustomClass is a user-defined class
|
|
1186
|
+
if self._check(TokenType.IDENTIFIER) and return_type is None:
|
|
1187
|
+
# Look ahead to see if this is "Type Name(" pattern
|
|
1188
|
+
saved_inner = self.pos
|
|
1189
|
+
potential_type = self._advance().value
|
|
1190
|
+
|
|
1191
|
+
# Skip generic type parameters if present
|
|
1192
|
+
if self._check(TokenType.COMPARE_LT):
|
|
1193
|
+
self._advance()
|
|
1194
|
+
depth = 1
|
|
1195
|
+
while depth > 0 and not self._is_at_end():
|
|
1196
|
+
if self._check(TokenType.COMPARE_LT):
|
|
1197
|
+
depth += 1
|
|
1198
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
1199
|
+
depth -= 1
|
|
1200
|
+
self._advance()
|
|
1201
|
+
|
|
1202
|
+
# Check if followed by another identifier (function name)
|
|
1203
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1204
|
+
return_type = potential_type
|
|
1205
|
+
continue
|
|
1206
|
+
else:
|
|
1207
|
+
# Not a "Type Name" pattern, restore position
|
|
1208
|
+
self.pos = saved_inner
|
|
1209
|
+
|
|
1057
1210
|
# Check for * prefix (non-null) or *[type] (type exclusion)
|
|
1058
1211
|
if self._check(TokenType.MULTIPLY):
|
|
1059
1212
|
self._advance()
|
|
@@ -1100,7 +1253,9 @@ class CSSLParser:
|
|
|
1100
1253
|
param_info['const'] = True
|
|
1101
1254
|
|
|
1102
1255
|
# Handle type annotations (builtin types like int, string, etc.)
|
|
1103
|
-
|
|
1256
|
+
# v4.8: Also handle TYPE_LITERAL (dict, list) which are tokenized differently
|
|
1257
|
+
if (self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or \
|
|
1258
|
+
self._check(TokenType.TYPE_LITERAL):
|
|
1104
1259
|
param_info['type'] = self._advance().value
|
|
1105
1260
|
|
|
1106
1261
|
# Check for generic type parameter <T>
|
|
@@ -1325,7 +1480,8 @@ class CSSLParser:
|
|
|
1325
1480
|
saved_pos = self.pos
|
|
1326
1481
|
|
|
1327
1482
|
# Must start with a type keyword (int, string, stack, vector, etc.)
|
|
1328
|
-
|
|
1483
|
+
# v4.8.7: Also check TYPE_LITERAL (list, dict) which are tokenized differently
|
|
1484
|
+
if not self._check(TokenType.KEYWORD) and not self._check(TokenType.TYPE_LITERAL):
|
|
1329
1485
|
return False
|
|
1330
1486
|
|
|
1331
1487
|
type_name = self._current().value
|
|
@@ -1334,7 +1490,9 @@ class CSSLParser:
|
|
|
1334
1490
|
type_keywords = {'int', 'string', 'float', 'bool', 'dynamic', 'void',
|
|
1335
1491
|
'stack', 'vector', 'datastruct', 'dataspace', 'shuffled',
|
|
1336
1492
|
'iterator', 'combo', 'array', 'openquote', 'json',
|
|
1337
|
-
'list', 'dictionary', 'dict', 'instance', 'map'
|
|
1493
|
+
'list', 'dictionary', 'dict', 'instance', 'map',
|
|
1494
|
+
'queue', # v4.7: Added queue
|
|
1495
|
+
'bit', 'byte', 'address', 'ptr', 'pointer'} # v4.9.0: Binary types, address, ptr/pointer
|
|
1338
1496
|
if type_name not in type_keywords:
|
|
1339
1497
|
return False
|
|
1340
1498
|
|
|
@@ -1351,6 +1509,10 @@ class CSSLParser:
|
|
|
1351
1509
|
depth -= 1
|
|
1352
1510
|
self._advance()
|
|
1353
1511
|
|
|
1512
|
+
# v4.8.6: Check for @ prefix (global variable marker)
|
|
1513
|
+
if self._check(TokenType.AT):
|
|
1514
|
+
self._advance()
|
|
1515
|
+
|
|
1354
1516
|
# Next should be an identifier (variable name), not '(' (function) or ';'
|
|
1355
1517
|
result = self._check(TokenType.IDENTIFIER)
|
|
1356
1518
|
|
|
@@ -1379,10 +1541,32 @@ class CSSLParser:
|
|
|
1379
1541
|
if self._match(TokenType.MULTIPLY):
|
|
1380
1542
|
non_null = True
|
|
1381
1543
|
|
|
1382
|
-
#
|
|
1383
|
-
|
|
1544
|
+
# v4.8.6: Check for @ prefix (global variable marker)
|
|
1545
|
+
is_global_ref = False
|
|
1546
|
+
if self._match(TokenType.AT):
|
|
1547
|
+
is_global_ref = True
|
|
1548
|
+
|
|
1549
|
+
# Get variable name - can be identifier or this->member
|
|
1550
|
+
# v4.9.2: Support this->member syntax for class member declarations
|
|
1551
|
+
is_this_member = False
|
|
1552
|
+
if self._check(TokenType.KEYWORD) and self._current().value == 'this':
|
|
1553
|
+
self._advance() # consume 'this'
|
|
1554
|
+
if self._match(TokenType.FLOW_RIGHT):
|
|
1555
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1556
|
+
var_name = self._advance().value
|
|
1557
|
+
is_this_member = True
|
|
1558
|
+
else:
|
|
1559
|
+
return None
|
|
1560
|
+
else:
|
|
1561
|
+
return None
|
|
1562
|
+
elif not self._check(TokenType.IDENTIFIER):
|
|
1384
1563
|
return None
|
|
1385
|
-
|
|
1564
|
+
else:
|
|
1565
|
+
var_name = self._advance().value
|
|
1566
|
+
|
|
1567
|
+
# v4.8.6: Store @ prefix in var_name for global reference
|
|
1568
|
+
if is_global_ref:
|
|
1569
|
+
var_name = '@' + var_name
|
|
1386
1570
|
|
|
1387
1571
|
# Check for assignment or just declaration
|
|
1388
1572
|
value = None
|
|
@@ -1391,21 +1575,38 @@ class CSSLParser:
|
|
|
1391
1575
|
|
|
1392
1576
|
self._match(TokenType.SEMICOLON)
|
|
1393
1577
|
|
|
1394
|
-
# For instance<"name">, create a special node
|
|
1395
|
-
|
|
1578
|
+
# For instance<"name">, create a special UniversalInstance node
|
|
1579
|
+
# But for plain "instance varName", treat as regular type annotation (like dynamic)
|
|
1580
|
+
if type_name == 'instance' and element_type:
|
|
1581
|
+
# instance<"containerName"> - creates/gets UniversalInstance
|
|
1396
1582
|
return ASTNode('instance_declaration', value={
|
|
1397
1583
|
'instance_name': element_type,
|
|
1398
1584
|
'name': var_name,
|
|
1399
1585
|
'value': value,
|
|
1400
1586
|
'non_null': non_null
|
|
1401
1587
|
})
|
|
1588
|
+
# v4.8.8: Plain "instance varName = new Class()" is a type annotation
|
|
1589
|
+
# allowing any CSSLInstance to be stored (like dynamic but for objects)
|
|
1590
|
+
|
|
1591
|
+
# v4.7: For queue<type, size>, extract size from element_type string
|
|
1592
|
+
queue_size = 'dynamic'
|
|
1593
|
+
if type_name == 'queue' and element_type and ', ' in element_type:
|
|
1594
|
+
parts = element_type.split(', ', 1)
|
|
1595
|
+
element_type = parts[0].strip()
|
|
1596
|
+
size_str = parts[1].strip()
|
|
1597
|
+
if size_str.isdigit():
|
|
1598
|
+
queue_size = int(size_str)
|
|
1599
|
+
else:
|
|
1600
|
+
queue_size = size_str # 'dynamic' or other
|
|
1402
1601
|
|
|
1403
1602
|
return ASTNode('typed_declaration', value={
|
|
1404
1603
|
'type': type_name,
|
|
1405
1604
|
'element_type': element_type,
|
|
1406
1605
|
'name': var_name,
|
|
1407
1606
|
'value': value,
|
|
1408
|
-
'non_null': non_null
|
|
1607
|
+
'non_null': non_null,
|
|
1608
|
+
'size': queue_size, # v4.7: For queue<T, size>
|
|
1609
|
+
'is_this_member': is_this_member # v4.9.2: this->member declaration
|
|
1409
1610
|
})
|
|
1410
1611
|
|
|
1411
1612
|
def parse_program(self) -> ASTNode:
|
|
@@ -1417,10 +1618,22 @@ class CSSLParser:
|
|
|
1417
1618
|
root.children.append(self._parse_struct())
|
|
1418
1619
|
elif self._match_keyword('class'):
|
|
1419
1620
|
root.children.append(self._parse_class())
|
|
1621
|
+
elif self._match_keyword('namespace'):
|
|
1622
|
+
root.children.append(self._parse_namespace())
|
|
1420
1623
|
elif self._match_keyword('enum'):
|
|
1421
1624
|
root.children.append(self._parse_enum())
|
|
1422
|
-
|
|
1423
|
-
|
|
1625
|
+
# v4.7: bytearrayed can be a modifier (bytearrayed define) or standalone block
|
|
1626
|
+
elif self._check(TokenType.KEYWORD) and self._current().value == 'bytearrayed':
|
|
1627
|
+
# Peek ahead: if next token is 'define', treat as modifier and handle in-place
|
|
1628
|
+
next_tok = self._peek(1)
|
|
1629
|
+
if next_tok.type == TokenType.KEYWORD and next_tok.value == 'define':
|
|
1630
|
+
# Handle bytearrayed define directly
|
|
1631
|
+
self._advance() # consume 'bytearrayed'
|
|
1632
|
+
self._advance() # consume 'define'
|
|
1633
|
+
root.children.append(self._parse_define(modifiers=['bytearrayed']))
|
|
1634
|
+
else:
|
|
1635
|
+
self._advance() # consume 'bytearrayed'
|
|
1636
|
+
root.children.append(self._parse_bytearrayed())
|
|
1424
1637
|
elif self._match_keyword('define'):
|
|
1425
1638
|
root.children.append(self._parse_define())
|
|
1426
1639
|
# v4.5.1: Handle function modifiers (private, const, static, etc.) before define
|
|
@@ -1444,10 +1657,28 @@ class CSSLParser:
|
|
|
1444
1657
|
is_global=is_global, is_embedded=is_embedded,
|
|
1445
1658
|
has_open_params=has_open_params, modifiers=modifiers
|
|
1446
1659
|
))
|
|
1660
|
+
elif self._match_keyword('class'):
|
|
1661
|
+
# v4.8.6: Support 'global class @ClassName' syntax
|
|
1662
|
+
root.children.append(self._parse_class(is_global=is_global, is_embedded=is_embedded))
|
|
1447
1663
|
elif self._looks_like_function_declaration():
|
|
1448
1664
|
root.children.append(self._parse_typed_function(modifiers=modifiers))
|
|
1665
|
+
elif self._looks_like_typed_variable():
|
|
1666
|
+
# v4.8.6: Support 'global int @varname = value' syntax
|
|
1667
|
+
decl = self._parse_typed_variable()
|
|
1668
|
+
if decl and is_global:
|
|
1669
|
+
# Wrap in global_assignment to mark as global variable
|
|
1670
|
+
global_stmt = ASTNode('global_assignment', value=decl)
|
|
1671
|
+
root.children.append(global_stmt)
|
|
1672
|
+
elif decl:
|
|
1673
|
+
root.children.append(decl)
|
|
1674
|
+
elif is_global:
|
|
1675
|
+
# v4.8.6: Handle 'global @varname = value' or 'global varname = value'
|
|
1676
|
+
stmt = self._parse_expression_statement()
|
|
1677
|
+
if stmt:
|
|
1678
|
+
global_stmt = ASTNode('global_assignment', value=stmt)
|
|
1679
|
+
root.children.append(global_stmt)
|
|
1449
1680
|
else:
|
|
1450
|
-
self.error(f"Expected 'define'
|
|
1681
|
+
self.error(f"Expected 'define', 'class', function, or variable declaration after modifiers: {modifiers}")
|
|
1451
1682
|
# Check for C-style typed function declarations
|
|
1452
1683
|
elif self._looks_like_function_declaration():
|
|
1453
1684
|
root.children.append(self._parse_typed_function())
|
|
@@ -1544,9 +1775,12 @@ class CSSLParser:
|
|
|
1544
1775
|
root.children.append(self._parse_switch())
|
|
1545
1776
|
# Handle statements - keywords like 'instance', 'list', 'map' can be variable names
|
|
1546
1777
|
# v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements (js$GameData.score = 1337)
|
|
1778
|
+
# v4.8.8: Added CAPTURED_REF for %snapshot(args) function calls
|
|
1779
|
+
# v4.9.0: Added POINTER_REF for ?name = value pointer assignments
|
|
1547
1780
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1548
1781
|
self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
|
|
1549
|
-
self._check(TokenType.KEYWORD) or self._check(TokenType.LANG_INSTANCE_REF)
|
|
1782
|
+
self._check(TokenType.KEYWORD) or self._check(TokenType.LANG_INSTANCE_REF) or
|
|
1783
|
+
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.POINTER_REF)):
|
|
1550
1784
|
stmt = self._parse_expression_statement()
|
|
1551
1785
|
if stmt:
|
|
1552
1786
|
root.children.append(stmt)
|
|
@@ -2136,6 +2370,225 @@ class CSSLParser:
|
|
|
2136
2370
|
'default': default_block
|
|
2137
2371
|
})
|
|
2138
2372
|
|
|
2373
|
+
def _parse_bytearrayed_body(self) -> List[ASTNode]:
|
|
2374
|
+
"""Parse body of a bytearrayed define function (v4.7).
|
|
2375
|
+
|
|
2376
|
+
Syntax:
|
|
2377
|
+
bytearrayed define Localize() {
|
|
2378
|
+
case "en": &GetLang() {
|
|
2379
|
+
return "Hello";
|
|
2380
|
+
}
|
|
2381
|
+
case "de": &GetLang() {
|
|
2382
|
+
return "Hallo";
|
|
2383
|
+
}
|
|
2384
|
+
default: {
|
|
2385
|
+
return "Unknown";
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
Returns list of AST nodes: case nodes with func_refs, and default node.
|
|
2390
|
+
"""
|
|
2391
|
+
children = []
|
|
2392
|
+
|
|
2393
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2394
|
+
# Handle case blocks: case "value": &FuncRef() { body }
|
|
2395
|
+
if self._match_keyword('case'):
|
|
2396
|
+
case_values = []
|
|
2397
|
+
func_refs = []
|
|
2398
|
+
|
|
2399
|
+
# Parse case values (comma-separated): case "en", "de": ...
|
|
2400
|
+
# v4.8.8: Also support tuple patterns: case {0, 0}: for matching multiple byte values
|
|
2401
|
+
while True:
|
|
2402
|
+
if self._check(TokenType.STRING):
|
|
2403
|
+
case_values.append(self._advance().value)
|
|
2404
|
+
elif self._check(TokenType.NUMBER):
|
|
2405
|
+
case_values.append(self._advance().value)
|
|
2406
|
+
elif self._check(TokenType.BOOLEAN):
|
|
2407
|
+
case_values.append(self._advance().value)
|
|
2408
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2409
|
+
case_values.append(self._advance().value)
|
|
2410
|
+
elif self._check(TokenType.BLOCK_START):
|
|
2411
|
+
# v4.8.8: Tuple pattern {val1, val2, ...} for matching multiple byte values
|
|
2412
|
+
self._advance() # consume {
|
|
2413
|
+
tuple_values = []
|
|
2414
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2415
|
+
if self._check(TokenType.NUMBER):
|
|
2416
|
+
tuple_values.append(self._advance().value)
|
|
2417
|
+
elif self._check(TokenType.STRING):
|
|
2418
|
+
tuple_values.append(self._advance().value)
|
|
2419
|
+
elif self._check(TokenType.BOOLEAN):
|
|
2420
|
+
tuple_values.append(self._advance().value)
|
|
2421
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2422
|
+
tuple_values.append(self._advance().value)
|
|
2423
|
+
elif self._check(TokenType.COMMA):
|
|
2424
|
+
self._advance() # skip comma
|
|
2425
|
+
else:
|
|
2426
|
+
break
|
|
2427
|
+
self._expect(TokenType.BLOCK_END) # consume }
|
|
2428
|
+
case_values.append(tuple(tuple_values)) # Store as tuple
|
|
2429
|
+
else:
|
|
2430
|
+
break
|
|
2431
|
+
if not self._match(TokenType.COMMA):
|
|
2432
|
+
break
|
|
2433
|
+
|
|
2434
|
+
# Expect colon after case values
|
|
2435
|
+
self._expect(TokenType.COLON)
|
|
2436
|
+
|
|
2437
|
+
# Parse function references: &FuncRef(), &FuncRef2(), ...
|
|
2438
|
+
while self._check(TokenType.AMPERSAND):
|
|
2439
|
+
self._advance() # consume &
|
|
2440
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2441
|
+
func_name = self._advance().value
|
|
2442
|
+
func_args = []
|
|
2443
|
+
if self._match(TokenType.PAREN_START):
|
|
2444
|
+
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
2445
|
+
if self._check(TokenType.COMMA):
|
|
2446
|
+
self._advance()
|
|
2447
|
+
continue
|
|
2448
|
+
arg = self._parse_expression()
|
|
2449
|
+
func_args.append(arg)
|
|
2450
|
+
self._expect(TokenType.PAREN_END)
|
|
2451
|
+
func_refs.append({'name': func_name, 'args': func_args})
|
|
2452
|
+
if not self._match(TokenType.COMMA):
|
|
2453
|
+
break
|
|
2454
|
+
|
|
2455
|
+
# Parse case body - either block { } or inline statements until next case/default
|
|
2456
|
+
body_children = []
|
|
2457
|
+
if self._check(TokenType.BLOCK_START):
|
|
2458
|
+
# Block syntax: case val: { ... }
|
|
2459
|
+
self._advance() # consume {
|
|
2460
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2461
|
+
stmt = self._parse_statement()
|
|
2462
|
+
if stmt:
|
|
2463
|
+
body_children.append(stmt)
|
|
2464
|
+
self._expect(TokenType.BLOCK_END)
|
|
2465
|
+
else:
|
|
2466
|
+
# v4.8.8: Inline syntax: case val: stmt1; stmt2; return x; (until next case/default/})
|
|
2467
|
+
while not self._is_at_end():
|
|
2468
|
+
# Stop at next case, default, or block end
|
|
2469
|
+
if self._check(TokenType.KEYWORD) and self._current().value in ('case', 'default'):
|
|
2470
|
+
break
|
|
2471
|
+
if self._check(TokenType.BLOCK_END):
|
|
2472
|
+
break
|
|
2473
|
+
stmt = self._parse_statement()
|
|
2474
|
+
if stmt:
|
|
2475
|
+
body_children.append(stmt)
|
|
2476
|
+
|
|
2477
|
+
children.append(ASTNode('case', value={
|
|
2478
|
+
'patterns': case_values,
|
|
2479
|
+
'func_refs': func_refs,
|
|
2480
|
+
'body': body_children
|
|
2481
|
+
}))
|
|
2482
|
+
|
|
2483
|
+
# Handle default block: default: { body } or default: stmt (inline)
|
|
2484
|
+
elif self._match_keyword('default'):
|
|
2485
|
+
self._match(TokenType.COLON) # Optional colon
|
|
2486
|
+
body_children = []
|
|
2487
|
+
if self._check(TokenType.BLOCK_START):
|
|
2488
|
+
# Block syntax
|
|
2489
|
+
self._advance() # consume {
|
|
2490
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2491
|
+
stmt = self._parse_statement()
|
|
2492
|
+
if stmt:
|
|
2493
|
+
body_children.append(stmt)
|
|
2494
|
+
self._expect(TokenType.BLOCK_END)
|
|
2495
|
+
else:
|
|
2496
|
+
# v4.8.8: Inline syntax: default: stmt1; return x; (until block end)
|
|
2497
|
+
while not self._is_at_end():
|
|
2498
|
+
if self._check(TokenType.BLOCK_END):
|
|
2499
|
+
break
|
|
2500
|
+
# Also stop at next case (shouldn't happen after default, but be safe)
|
|
2501
|
+
if self._check(TokenType.KEYWORD) and self._current().value == 'case':
|
|
2502
|
+
break
|
|
2503
|
+
stmt = self._parse_statement()
|
|
2504
|
+
if stmt:
|
|
2505
|
+
body_children.append(stmt)
|
|
2506
|
+
|
|
2507
|
+
children.append(ASTNode('default', value={
|
|
2508
|
+
'body': body_children
|
|
2509
|
+
}))
|
|
2510
|
+
|
|
2511
|
+
# v4.8.8: Handle func_ref statements (&func; or &func(args);)
|
|
2512
|
+
elif self._check(TokenType.AMPERSAND):
|
|
2513
|
+
self._advance() # consume &
|
|
2514
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2515
|
+
func_name = self._advance().value
|
|
2516
|
+
func_args = []
|
|
2517
|
+
# Check for arguments
|
|
2518
|
+
if self._match(TokenType.PAREN_START):
|
|
2519
|
+
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
2520
|
+
if self._check(TokenType.COMMA):
|
|
2521
|
+
self._advance()
|
|
2522
|
+
continue
|
|
2523
|
+
arg = self._parse_expression()
|
|
2524
|
+
func_args.append(arg)
|
|
2525
|
+
self._expect(TokenType.PAREN_END)
|
|
2526
|
+
self._match(TokenType.SEMICOLON) # Optional semicolon
|
|
2527
|
+
children.append(ASTNode('func_ref', value={
|
|
2528
|
+
'name': func_name,
|
|
2529
|
+
'args': func_args
|
|
2530
|
+
}))
|
|
2531
|
+
else:
|
|
2532
|
+
self.error("Expected identifier after '&' in bytearrayed body")
|
|
2533
|
+
|
|
2534
|
+
# Handle other statements (e.g., expressions, function calls)
|
|
2535
|
+
else:
|
|
2536
|
+
stmt = self._parse_statement()
|
|
2537
|
+
if stmt:
|
|
2538
|
+
children.append(stmt)
|
|
2539
|
+
|
|
2540
|
+
return children
|
|
2541
|
+
|
|
2542
|
+
def _parse_namespace(self) -> ASTNode:
|
|
2543
|
+
"""Parse namespace definition.
|
|
2544
|
+
|
|
2545
|
+
Syntax:
|
|
2546
|
+
namespace mylib {
|
|
2547
|
+
void myFunc() { ... }
|
|
2548
|
+
class MyClass { ... }
|
|
2549
|
+
namespace nested { ... }
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
Access: mylib::myFunc(), mylib::MyClass, mylib::nested::innerFunc()
|
|
2553
|
+
"""
|
|
2554
|
+
# Get namespace name
|
|
2555
|
+
if not self._check(TokenType.IDENTIFIER):
|
|
2556
|
+
self.error("Expected namespace name after 'namespace'")
|
|
2557
|
+
name = self._advance().value
|
|
2558
|
+
|
|
2559
|
+
# Expect opening brace
|
|
2560
|
+
if not self._match(TokenType.BLOCK_START):
|
|
2561
|
+
self.error(f"Expected '{{' after 'namespace {name}'")
|
|
2562
|
+
|
|
2563
|
+
# Parse namespace contents
|
|
2564
|
+
members = []
|
|
2565
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2566
|
+
if self._match_keyword('namespace'):
|
|
2567
|
+
# Nested namespace
|
|
2568
|
+
members.append(self._parse_namespace())
|
|
2569
|
+
elif self._match_keyword('class'):
|
|
2570
|
+
members.append(self._parse_class())
|
|
2571
|
+
elif self._match_keyword('struct'):
|
|
2572
|
+
members.append(self._parse_struct())
|
|
2573
|
+
elif self._match_keyword('enum'):
|
|
2574
|
+
members.append(self._parse_enum())
|
|
2575
|
+
elif self._match_keyword('define'):
|
|
2576
|
+
members.append(self._parse_define())
|
|
2577
|
+
elif self._looks_like_function_declaration():
|
|
2578
|
+
members.append(self._parse_typed_function())
|
|
2579
|
+
elif self._check(TokenType.COMMENT):
|
|
2580
|
+
self._advance() # Skip comments
|
|
2581
|
+
else:
|
|
2582
|
+
stmt = self._parse_expression_statement()
|
|
2583
|
+
if stmt:
|
|
2584
|
+
members.append(stmt)
|
|
2585
|
+
|
|
2586
|
+
# Expect closing brace
|
|
2587
|
+
if not self._match(TokenType.BLOCK_END):
|
|
2588
|
+
self.error(f"Expected '}}' to close namespace '{name}'")
|
|
2589
|
+
|
|
2590
|
+
return ASTNode('namespace', value={'name': name}, children=members)
|
|
2591
|
+
|
|
2139
2592
|
def _parse_class(self, is_global: bool = False, is_embedded: bool = False) -> ASTNode:
|
|
2140
2593
|
"""Parse class declaration with members and methods.
|
|
2141
2594
|
|
|
@@ -2201,8 +2654,30 @@ class CSSLParser:
|
|
|
2201
2654
|
overwrites_class = None
|
|
2202
2655
|
overwrites_is_python = False
|
|
2203
2656
|
supports_language = None # v4.1.0: Multi-language syntax support
|
|
2204
|
-
|
|
2205
|
-
|
|
2657
|
+
uses_memory = None # v4.9.0: Memory binding for deferred execution
|
|
2658
|
+
|
|
2659
|
+
# v4.8.8: Support C++ style "class Child extends Parent" without colon
|
|
2660
|
+
if self._match_keyword('extends'):
|
|
2661
|
+
# Direct extends without colon: class Dog extends Animal
|
|
2662
|
+
if self._check(TokenType.LANG_INSTANCE_REF):
|
|
2663
|
+
ref = self._advance().value
|
|
2664
|
+
extends_lang_ref = ref
|
|
2665
|
+
extends_class = ref['instance']
|
|
2666
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2667
|
+
extends_class = self._advance().value
|
|
2668
|
+
elif self._check(TokenType.SHARED_REF):
|
|
2669
|
+
extends_class = self._advance().value
|
|
2670
|
+
extends_is_python = True
|
|
2671
|
+
else:
|
|
2672
|
+
raise CSSLSyntaxError("Expected parent class name after 'extends'")
|
|
2673
|
+
# Check for constructor arguments: extends Parent(arg1, arg2)
|
|
2674
|
+
if self._match(TokenType.PAREN_START):
|
|
2675
|
+
while not self._check(TokenType.PAREN_END):
|
|
2676
|
+
arg = self._parse_expression()
|
|
2677
|
+
extends_args.append(arg)
|
|
2678
|
+
self._match(TokenType.COMMA)
|
|
2679
|
+
self._expect(TokenType.PAREN_END)
|
|
2680
|
+
elif self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
2206
2681
|
# Parse extends and/or overwrites (can be chained with : or ::)
|
|
2207
2682
|
while True:
|
|
2208
2683
|
if self._match_keyword('extends'):
|
|
@@ -2248,8 +2723,18 @@ class CSSLParser:
|
|
|
2248
2723
|
supports_language = self._advance().value
|
|
2249
2724
|
else:
|
|
2250
2725
|
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
2726
|
+
# v4.9.0: Parse 'uses' keyword for memory binding (deferred execution)
|
|
2727
|
+
# Syntax: class MyClass : uses memory(address) { }
|
|
2728
|
+
elif self._match_keyword('uses'):
|
|
2729
|
+
if (self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD)) and self._current().value == 'memory':
|
|
2730
|
+
self._advance() # consume 'memory'
|
|
2731
|
+
self._expect(TokenType.PAREN_START)
|
|
2732
|
+
uses_memory = self._parse_expression() # Parse the address expression
|
|
2733
|
+
self._expect(TokenType.PAREN_END)
|
|
2734
|
+
else:
|
|
2735
|
+
raise CSSLSyntaxError("Expected 'memory(address)' after 'uses'")
|
|
2251
2736
|
else:
|
|
2252
|
-
raise CSSLSyntaxError("Expected 'extends', 'overwrites', or '
|
|
2737
|
+
raise CSSLSyntaxError("Expected 'extends', 'overwrites', 'supports', or 'uses' after ':' or '::' in class declaration")
|
|
2253
2738
|
# Check for another : or :: for chaining
|
|
2254
2739
|
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
2255
2740
|
break
|
|
@@ -2274,7 +2759,8 @@ class CSSLParser:
|
|
|
2274
2759
|
'supports_language': supports_language, # v4.1.0
|
|
2275
2760
|
'raw_body': raw_body, # v4.2.0: Raw body for language transformation
|
|
2276
2761
|
'append_ref_class': append_ref_class, # v4.2.5: &target class reference
|
|
2277
|
-
'append_ref_member': append_ref_member # v4.2.5: &target member reference
|
|
2762
|
+
'append_ref_member': append_ref_member, # v4.2.5: &target member reference
|
|
2763
|
+
'uses_memory': uses_memory # v4.9.0: Memory binding for deferred execution
|
|
2278
2764
|
}, children=[])
|
|
2279
2765
|
|
|
2280
2766
|
# v4.2.0: If we have raw_body for language transformation, skip regular parsing
|
|
@@ -2320,12 +2806,46 @@ class CSSLParser:
|
|
|
2320
2806
|
method = self._parse_define()
|
|
2321
2807
|
node.children.append(method)
|
|
2322
2808
|
|
|
2809
|
+
# v4.7: Check for function modifiers before define (e.g., bytearrayed define, private define)
|
|
2810
|
+
elif self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
|
|
2811
|
+
modifiers = []
|
|
2812
|
+
is_embedded = False
|
|
2813
|
+
is_global = False
|
|
2814
|
+
while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
|
|
2815
|
+
mod = self._advance().value
|
|
2816
|
+
modifiers.append(mod)
|
|
2817
|
+
if mod == 'embedded':
|
|
2818
|
+
is_embedded = True
|
|
2819
|
+
elif mod == 'global':
|
|
2820
|
+
is_global = True
|
|
2821
|
+
# After modifiers, expect 'define' or typed function
|
|
2822
|
+
if self._match_keyword('define'):
|
|
2823
|
+
method = self._parse_define(
|
|
2824
|
+
is_global=is_global, is_embedded=is_embedded,
|
|
2825
|
+
modifiers=modifiers
|
|
2826
|
+
)
|
|
2827
|
+
node.children.append(method)
|
|
2828
|
+
elif self._looks_like_function_declaration():
|
|
2829
|
+
method = self._parse_typed_function(modifiers=modifiers)
|
|
2830
|
+
node.children.append(method)
|
|
2831
|
+
else:
|
|
2832
|
+
self.error(f"Expected 'define' or function after modifiers in class: {modifiers}")
|
|
2833
|
+
|
|
2323
2834
|
# Check for constr keyword (constructor declaration)
|
|
2324
2835
|
# Syntax: constr ConstructorName() { ... }
|
|
2325
2836
|
# or: constr ConstructorName() : extends Parent::ConstructorName { ... }
|
|
2837
|
+
# v4.8.8: Also supports: secure constr Name(), callable constr Name()
|
|
2326
2838
|
elif self._match_keyword('constr'):
|
|
2327
|
-
constructor = self._parse_constructor(class_name)
|
|
2839
|
+
constructor = self._parse_constructor(class_name, modifiers=[])
|
|
2328
2840
|
node.children.append(constructor)
|
|
2841
|
+
# v4.8.8: Check for secure/callable modifiers before constr
|
|
2842
|
+
elif self._match_keyword('secure') or self._match_keyword('callable'):
|
|
2843
|
+
constr_modifier = self.tokens[self.pos - 1].value # 'secure' or 'callable'
|
|
2844
|
+
if self._match_keyword('constr'):
|
|
2845
|
+
constructor = self._parse_constructor(class_name, modifiers=[constr_modifier])
|
|
2846
|
+
node.children.append(constructor)
|
|
2847
|
+
else:
|
|
2848
|
+
self.error(f"Expected 'constr' after '{constr_modifier}'")
|
|
2329
2849
|
|
|
2330
2850
|
else:
|
|
2331
2851
|
self._advance()
|
|
@@ -2333,24 +2853,40 @@ class CSSLParser:
|
|
|
2333
2853
|
self._expect(TokenType.BLOCK_END)
|
|
2334
2854
|
return node
|
|
2335
2855
|
|
|
2336
|
-
def _parse_constructor(self, class_name: str) -> ASTNode:
|
|
2337
|
-
"""Parse constructor declaration inside a class.
|
|
2856
|
+
def _parse_constructor(self, class_name: str, modifiers: list = None) -> ASTNode:
|
|
2857
|
+
"""Parse constructor or destructor declaration inside a class.
|
|
2338
2858
|
|
|
2339
2859
|
Syntax:
|
|
2340
2860
|
constr ConstructorName() { ... }
|
|
2861
|
+
constr ~ClassName() { ... } // v4.8.8: Destructor (cleanup code)
|
|
2341
2862
|
constr ConstructorName() ++ { ... } // Append: keeps parent constructor + adds new code
|
|
2342
2863
|
constr ConstructorName() &ParentClass::constructors ++ { ... } // Append specific parent constructor
|
|
2343
2864
|
constr ConstructorName() : extends ParentClass::ConstructorName { ... }
|
|
2344
2865
|
constr ConstructorName() : extends ParentClass::ConstructorName : overwrites ParentClass::ConstructorName { ... }
|
|
2345
2866
|
|
|
2867
|
+
v4.8.8 Modifiers:
|
|
2868
|
+
secure constr Name() { ... } - Only runs on exception in class
|
|
2869
|
+
callable constr Name() { ... } - Must be manually called, not auto-run
|
|
2870
|
+
|
|
2346
2871
|
The ++ operator means: execute parent's version first, then execute this code (append mode).
|
|
2347
2872
|
The &ClassName::member syntax references a specific member from the overwritten class.
|
|
2873
|
+
Destructor (~Name) is called when instance is deleted or goes out of scope.
|
|
2348
2874
|
"""
|
|
2349
|
-
|
|
2875
|
+
modifiers = modifiers or []
|
|
2876
|
+
# v4.8.8: Check for destructor prefix ~
|
|
2877
|
+
is_destructor = False
|
|
2878
|
+
if self._match(TokenType.TILDE): # ~ operator for destructors
|
|
2879
|
+
is_destructor = True
|
|
2880
|
+
|
|
2881
|
+
# Get constructor/destructor name
|
|
2350
2882
|
if not self._check(TokenType.IDENTIFIER):
|
|
2351
|
-
raise CSSLSyntaxError("Expected constructor name after 'constr'")
|
|
2883
|
+
raise CSSLSyntaxError("Expected constructor name after 'constr'" + (" ~" if is_destructor else ""))
|
|
2352
2884
|
constr_name = self._advance().value
|
|
2353
2885
|
|
|
2886
|
+
# v4.8.8: Destructor name should match class name
|
|
2887
|
+
if is_destructor:
|
|
2888
|
+
constr_name = f"~{constr_name}" # Store with ~ prefix
|
|
2889
|
+
|
|
2354
2890
|
# Parse method-level extends/overwrites with :: syntax
|
|
2355
2891
|
extends_target = None
|
|
2356
2892
|
extends_class_ref = None
|
|
@@ -2432,7 +2968,10 @@ class CSSLParser:
|
|
|
2432
2968
|
'name': constr_name,
|
|
2433
2969
|
'class_name': class_name,
|
|
2434
2970
|
'params': params,
|
|
2435
|
-
'is_constructor':
|
|
2971
|
+
'is_constructor': not is_destructor, # v4.8.8: False for destructors
|
|
2972
|
+
'is_destructor': is_destructor, # v4.8.8: True for ~Name()
|
|
2973
|
+
'is_secure': 'secure' in modifiers, # v4.8.8: Only run on exception
|
|
2974
|
+
'is_callable': 'callable' in modifiers, # v4.8.8: Manual call only
|
|
2436
2975
|
'extends_target': extends_target,
|
|
2437
2976
|
'extends_class': extends_class_ref,
|
|
2438
2977
|
'extends_method': extends_method_ref,
|
|
@@ -2565,7 +3104,9 @@ class CSSLParser:
|
|
|
2565
3104
|
if self._match_keyword('open'):
|
|
2566
3105
|
param_info['open'] = True
|
|
2567
3106
|
# Handle type annotations (e.g., string, int, dynamic, etc.)
|
|
2568
|
-
|
|
3107
|
+
# v4.9.2: Also handle TYPE_LITERAL (dict, list) which are tokenized differently
|
|
3108
|
+
if (self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or \
|
|
3109
|
+
self._check(TokenType.TYPE_LITERAL):
|
|
2569
3110
|
param_info['type'] = self._advance().value
|
|
2570
3111
|
# Handle reference operator &
|
|
2571
3112
|
if self._match(TokenType.AMPERSAND):
|
|
@@ -2613,6 +3154,7 @@ class CSSLParser:
|
|
|
2613
3154
|
overwrites_class_ref = None
|
|
2614
3155
|
overwrites_method_ref = None
|
|
2615
3156
|
supports_language = None # v4.1.0: Multi-language syntax support
|
|
3157
|
+
uses_memory = None # v4.9.0: Memory binding for deferred execution
|
|
2616
3158
|
|
|
2617
3159
|
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
2618
3160
|
# Parse extends and/or overwrites (supports :: method-level syntax)
|
|
@@ -2685,6 +3227,16 @@ class CSSLParser:
|
|
|
2685
3227
|
supports_language = self._advance().value
|
|
2686
3228
|
else:
|
|
2687
3229
|
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
3230
|
+
# v4.9.0: Parse 'uses' keyword for memory binding (deferred execution)
|
|
3231
|
+
# Syntax: define func() : uses memory(address) { }
|
|
3232
|
+
elif self._match_keyword('uses'):
|
|
3233
|
+
if (self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD)) and self._current().value == 'memory':
|
|
3234
|
+
self._advance() # consume 'memory'
|
|
3235
|
+
self._expect(TokenType.PAREN_START)
|
|
3236
|
+
uses_memory = self._parse_expression() # Parse the address expression
|
|
3237
|
+
self._expect(TokenType.PAREN_END)
|
|
3238
|
+
else:
|
|
3239
|
+
raise CSSLSyntaxError("Expected 'memory(address)' after 'uses'")
|
|
2688
3240
|
else:
|
|
2689
3241
|
break
|
|
2690
3242
|
# Check for another :: or : for chaining extends/overwrites
|
|
@@ -2696,22 +3248,54 @@ class CSSLParser:
|
|
|
2696
3248
|
append_mode = False
|
|
2697
3249
|
append_ref_class = None
|
|
2698
3250
|
append_ref_member = None
|
|
3251
|
+
append_position = None # v4.9.2: Optional position for hook placement (e.g., &name[-1])
|
|
2699
3252
|
|
|
2700
|
-
# Check for &ClassName::member reference
|
|
3253
|
+
# Check for &ClassName::member or &builtinName reference
|
|
3254
|
+
# v4.9.2: Support &builtinName ++ for hooking into builtin functions
|
|
2701
3255
|
if self._match(TokenType.AMPERSAND):
|
|
2702
3256
|
if self._check(TokenType.IDENTIFIER):
|
|
2703
|
-
|
|
3257
|
+
ref_name = self._advance().value
|
|
3258
|
+
elif self._check(TokenType.KEYWORD):
|
|
3259
|
+
# v4.9.2: Allow keywords as function names (e.g., &reflect, &address)
|
|
3260
|
+
ref_name = self._advance().value
|
|
2704
3261
|
elif self._check(TokenType.SHARED_REF):
|
|
2705
|
-
|
|
3262
|
+
ref_name = f'${self._advance().value}'
|
|
3263
|
+
elif self._check(TokenType.AT):
|
|
3264
|
+
# &@globalFunc reference
|
|
3265
|
+
self._advance() # consume @
|
|
3266
|
+
if self._check(TokenType.IDENTIFIER):
|
|
3267
|
+
ref_name = '@' + self._advance().value
|
|
3268
|
+
else:
|
|
3269
|
+
raise CSSLSyntaxError("Expected identifier after '&@'")
|
|
3270
|
+
elif self._check(TokenType.TYPE_LITERAL):
|
|
3271
|
+
# v4.9.2: Allow type literals as function names (dict, list)
|
|
3272
|
+
ref_name = self._advance().value
|
|
2706
3273
|
else:
|
|
2707
|
-
|
|
3274
|
+
# Debug: show what token we got
|
|
3275
|
+
cur = self._current()
|
|
3276
|
+
raise CSSLSyntaxError(f"Expected function/class name after '&' in function reference, got {cur.type.name}='{cur.value}'")
|
|
2708
3277
|
|
|
2709
|
-
# Check for ::member
|
|
3278
|
+
# Check for ::member (class method reference)
|
|
2710
3279
|
if self._match(TokenType.DOUBLE_COLON):
|
|
3280
|
+
# It's a class::member reference
|
|
3281
|
+
append_ref_class = ref_name
|
|
2711
3282
|
if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
|
|
2712
3283
|
append_ref_member = self._advance().value
|
|
2713
3284
|
else:
|
|
2714
3285
|
raise CSSLSyntaxError("Expected member name after '::' in function reference")
|
|
3286
|
+
else:
|
|
3287
|
+
# v4.9.2: No ::, it's a builtin function reference like &reflect
|
|
3288
|
+
# Store as special builtin reference (class=None, member=builtinName)
|
|
3289
|
+
append_ref_class = '__builtins__'
|
|
3290
|
+
append_ref_member = ref_name
|
|
3291
|
+
|
|
3292
|
+
# v4.9.2: Check for position syntax [index] (e.g., &name[-1] to place hook before last statement)
|
|
3293
|
+
if self._match(TokenType.BRACKET_START):
|
|
3294
|
+
# Parse the position expression (usually a number, can be negative)
|
|
3295
|
+
pos_expr = self._parse_expression()
|
|
3296
|
+
self._expect(TokenType.BRACKET_END)
|
|
3297
|
+
# Store the position expression - will be evaluated at runtime
|
|
3298
|
+
append_position = pos_expr
|
|
2715
3299
|
|
|
2716
3300
|
# Check for ++ append operator
|
|
2717
3301
|
if self._match(TokenType.PLUS_PLUS):
|
|
@@ -2740,6 +3324,9 @@ class CSSLParser:
|
|
|
2740
3324
|
if supports_language:
|
|
2741
3325
|
raw_body = self._extract_raw_block_body()
|
|
2742
3326
|
# _extract_raw_block_body positions cursor at BLOCK_END
|
|
3327
|
+
# v4.7: Special parsing for bytearrayed functions
|
|
3328
|
+
elif modifiers and 'bytearrayed' in modifiers:
|
|
3329
|
+
children = self._parse_bytearrayed_body()
|
|
2743
3330
|
else:
|
|
2744
3331
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2745
3332
|
stmt = self._parse_statement()
|
|
@@ -2769,12 +3356,15 @@ class CSSLParser:
|
|
|
2769
3356
|
'append_mode': append_mode,
|
|
2770
3357
|
'append_ref_class': append_ref_class,
|
|
2771
3358
|
'append_ref_member': append_ref_member,
|
|
3359
|
+
'append_position': append_position, # v4.9.2: Position for hook placement
|
|
2772
3360
|
# v4.1.0: Multi-language support
|
|
2773
3361
|
'supports_language': supports_language,
|
|
2774
3362
|
# v4.2.0: Raw body for language transformation
|
|
2775
3363
|
'raw_body': raw_body,
|
|
2776
3364
|
# v4.5.1: Function modifiers (private, const, static, etc.)
|
|
2777
|
-
'modifiers': modifiers or []
|
|
3365
|
+
'modifiers': modifiers or [],
|
|
3366
|
+
# v4.9.0: Memory binding for deferred execution
|
|
3367
|
+
'uses_memory': uses_memory
|
|
2778
3368
|
}, children=children)
|
|
2779
3369
|
|
|
2780
3370
|
return node
|
|
@@ -2801,16 +3391,38 @@ class CSSLParser:
|
|
|
2801
3391
|
# v4.5.1: Add throw statement parsing
|
|
2802
3392
|
elif self._match_keyword('throw'):
|
|
2803
3393
|
return self._parse_throw()
|
|
3394
|
+
# v4.8: Add raise statement (Python-style: raise ExceptionType("message"))
|
|
3395
|
+
elif self._match_keyword('raise'):
|
|
3396
|
+
return self._parse_raise()
|
|
2804
3397
|
elif self._match_keyword('try'):
|
|
2805
3398
|
return self._parse_try()
|
|
2806
3399
|
elif self._match_keyword('await'):
|
|
2807
3400
|
return self._parse_await()
|
|
3401
|
+
elif self._match_keyword('yield'):
|
|
3402
|
+
return self._parse_yield()
|
|
2808
3403
|
elif self._match_keyword('supports'):
|
|
2809
3404
|
# v4.2.0: Standalone supports block for multi-language syntax
|
|
2810
3405
|
return self._parse_supports_block()
|
|
2811
3406
|
elif self._match_keyword('define'):
|
|
2812
3407
|
# Nested define function
|
|
2813
3408
|
return self._parse_define()
|
|
3409
|
+
elif self._match_keyword('global'):
|
|
3410
|
+
# v4.9.2: Global variable inside function: global int x = 1; or global x = 1;
|
|
3411
|
+
if self._looks_like_typed_variable():
|
|
3412
|
+
# global type varName = value;
|
|
3413
|
+
typed_node = self._parse_typed_variable()
|
|
3414
|
+
if typed_node and typed_node.type == 'typed_declaration':
|
|
3415
|
+
# Add 'global' to modifiers
|
|
3416
|
+
modifiers = typed_node.value.get('modifiers', [])
|
|
3417
|
+
modifiers.append('global')
|
|
3418
|
+
typed_node.value['modifiers'] = modifiers
|
|
3419
|
+
return typed_node
|
|
3420
|
+
else:
|
|
3421
|
+
# global varName = value; (dynamic type)
|
|
3422
|
+
stmt = self._parse_expression_statement()
|
|
3423
|
+
if stmt:
|
|
3424
|
+
return ASTNode('global_assignment', value=stmt)
|
|
3425
|
+
return None
|
|
2814
3426
|
elif self._looks_like_typed_variable():
|
|
2815
3427
|
# Typed variable declaration (e.g., stack<string> myStack;)
|
|
2816
3428
|
return self._parse_typed_variable()
|
|
@@ -2826,11 +3438,15 @@ class CSSLParser:
|
|
|
2826
3438
|
# super() or super::method() call - calls parent constructor/method
|
|
2827
3439
|
return self._parse_super_call()
|
|
2828
3440
|
# v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
|
|
3441
|
+
# v4.9.0: Allow KEYWORD tokens followed by ( to be treated as function calls (memory, uses, etc.)
|
|
3442
|
+
# v4.9.0: Added POINTER_REF for ?name = value pointer assignments
|
|
2829
3443
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
2830
3444
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
3445
|
+
self._check(TokenType.POINTER_REF) or
|
|
2831
3446
|
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
2832
3447
|
self._check(TokenType.LANG_INSTANCE_REF) or
|
|
2833
3448
|
(self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new')) or
|
|
3449
|
+
(self._check(TokenType.KEYWORD) and self._peek(1) and self._peek(1).type == TokenType.PAREN_START) or
|
|
2834
3450
|
self._looks_like_namespace_call()):
|
|
2835
3451
|
return self._parse_expression_statement()
|
|
2836
3452
|
else:
|
|
@@ -3539,12 +4155,83 @@ class CSSLParser:
|
|
|
3539
4155
|
self._match(TokenType.SEMICOLON)
|
|
3540
4156
|
return ASTNode('throw', value=expr)
|
|
3541
4157
|
|
|
4158
|
+
def _parse_raise(self) -> ASTNode:
|
|
4159
|
+
"""Parse raise statement (Python-style exceptions).
|
|
4160
|
+
|
|
4161
|
+
v4.8: Added raise statement for Python-style exception raising.
|
|
4162
|
+
|
|
4163
|
+
Syntax:
|
|
4164
|
+
raise; # Re-raise current exception
|
|
4165
|
+
raise "Error message"; # Simple message
|
|
4166
|
+
raise ValueError("message"); # Python exception type
|
|
4167
|
+
raise CustomError("msg", code); # Custom exception with args
|
|
4168
|
+
|
|
4169
|
+
Supported Python exception types:
|
|
4170
|
+
ValueError, TypeError, KeyError, IndexError, AttributeError,
|
|
4171
|
+
RuntimeError, IOError, OSError, FileNotFoundError, NameError,
|
|
4172
|
+
ZeroDivisionError, OverflowError, StopIteration, AssertionError
|
|
4173
|
+
"""
|
|
4174
|
+
# Check for re-raise (just raise;)
|
|
4175
|
+
if self._check(TokenType.SEMICOLON):
|
|
4176
|
+
self._advance()
|
|
4177
|
+
return ASTNode('raise', value={'type': None, 'message': None})
|
|
4178
|
+
|
|
4179
|
+
# Get the exception expression
|
|
4180
|
+
# This could be: "message", ExceptionType("message"), or variable
|
|
4181
|
+
expr = self._parse_expression()
|
|
4182
|
+
|
|
4183
|
+
self._match(TokenType.SEMICOLON)
|
|
4184
|
+
|
|
4185
|
+
# Check if it's a function call (ExceptionType("message"))
|
|
4186
|
+
if isinstance(expr, ASTNode) and expr.type == 'call':
|
|
4187
|
+
callee = expr.value.get('callee')
|
|
4188
|
+
args = expr.value.get('args', [])
|
|
4189
|
+
if isinstance(callee, ASTNode) and callee.type == 'identifier':
|
|
4190
|
+
exc_type = callee.value
|
|
4191
|
+
return ASTNode('raise', value={
|
|
4192
|
+
'type': exc_type,
|
|
4193
|
+
'args': args
|
|
4194
|
+
})
|
|
4195
|
+
|
|
4196
|
+
# Simple expression (string or variable)
|
|
4197
|
+
return ASTNode('raise', value={
|
|
4198
|
+
'type': 'Error', # Default exception type
|
|
4199
|
+
'message': expr
|
|
4200
|
+
})
|
|
4201
|
+
|
|
3542
4202
|
def _parse_await(self) -> ASTNode:
|
|
3543
4203
|
"""Parse await statement: await expression;"""
|
|
3544
4204
|
expr = self._parse_expression()
|
|
3545
4205
|
self._match(TokenType.SEMICOLON)
|
|
3546
4206
|
return ASTNode('await', value=expr)
|
|
3547
4207
|
|
|
4208
|
+
def _parse_yield(self) -> ASTNode:
|
|
4209
|
+
"""Parse yield statement: yield expression; or yield;
|
|
4210
|
+
|
|
4211
|
+
v4.9.3: Generator yield statement for lazy iteration.
|
|
4212
|
+
|
|
4213
|
+
Syntax:
|
|
4214
|
+
yield value; // Yield a value and pause
|
|
4215
|
+
yield; // Yield None and pause
|
|
4216
|
+
|
|
4217
|
+
Example:
|
|
4218
|
+
generator<int> define CountUp(int limit) {
|
|
4219
|
+
int i = 0;
|
|
4220
|
+
while (i < limit) {
|
|
4221
|
+
yield i;
|
|
4222
|
+
i = i + 1;
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
"""
|
|
4226
|
+
# Check for yield with no value (yield;)
|
|
4227
|
+
if self._check(TokenType.SEMICOLON):
|
|
4228
|
+
self._advance()
|
|
4229
|
+
return ASTNode('yield', value=None)
|
|
4230
|
+
|
|
4231
|
+
expr = self._parse_expression()
|
|
4232
|
+
self._match(TokenType.SEMICOLON)
|
|
4233
|
+
return ASTNode('yield', value=expr)
|
|
4234
|
+
|
|
3548
4235
|
def _parse_supports_block(self) -> ASTNode:
|
|
3549
4236
|
"""Parse standalone supports block for multi-language syntax.
|
|
3550
4237
|
|
|
@@ -3722,6 +4409,66 @@ class CSSLParser:
|
|
|
3722
4409
|
self._expect(TokenType.BLOCK_END)
|
|
3723
4410
|
return node
|
|
3724
4411
|
|
|
4412
|
+
def _parse_local_injection(self, local_node: ASTNode) -> ASTNode:
|
|
4413
|
+
"""Parse local::func injection operations.
|
|
4414
|
+
|
|
4415
|
+
Syntax:
|
|
4416
|
+
local::func -<<== { code } // Remove matching code
|
|
4417
|
+
local::func +<<== { code } // Add code
|
|
4418
|
+
local::func -<<==[injection::innerline(3)] null; // Remove specific line
|
|
4419
|
+
local::func +<<==[injection::innerline(3)] code; // Add at specific line
|
|
4420
|
+
|
|
4421
|
+
Args:
|
|
4422
|
+
local_node: The local_ref ASTNode (e.g., local::func)
|
|
4423
|
+
|
|
4424
|
+
Returns:
|
|
4425
|
+
ASTNode for the local injection operation
|
|
4426
|
+
"""
|
|
4427
|
+
local_name = local_node.value
|
|
4428
|
+
|
|
4429
|
+
# Determine injection mode
|
|
4430
|
+
mode = None
|
|
4431
|
+
if self._match(TokenType.INFUSE_MINUS_LEFT):
|
|
4432
|
+
mode = 'remove'
|
|
4433
|
+
elif self._match(TokenType.INFUSE_PLUS_LEFT):
|
|
4434
|
+
mode = 'add'
|
|
4435
|
+
elif self._match(TokenType.INFUSE_LEFT):
|
|
4436
|
+
mode = 'replace'
|
|
4437
|
+
else:
|
|
4438
|
+
raise CSSLSyntaxError("Expected -<<==, +<<==, or <<== after local::func")
|
|
4439
|
+
|
|
4440
|
+
# Parse optional filter: [injection::innerline(n)]
|
|
4441
|
+
filters = self._parse_injection_filter()
|
|
4442
|
+
|
|
4443
|
+
# Parse the value/code block
|
|
4444
|
+
code_block = None
|
|
4445
|
+
if self._check(TokenType.BLOCK_START):
|
|
4446
|
+
# Block form: local::func +<<== { code }
|
|
4447
|
+
self._advance()
|
|
4448
|
+
children = []
|
|
4449
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
4450
|
+
if self._check(TokenType.NEWLINE):
|
|
4451
|
+
self._advance()
|
|
4452
|
+
continue
|
|
4453
|
+
stmt = self._parse_statement()
|
|
4454
|
+
if stmt:
|
|
4455
|
+
children.append(stmt)
|
|
4456
|
+
self._expect(TokenType.BLOCK_END)
|
|
4457
|
+
code_block = ASTNode('block', children=children)
|
|
4458
|
+
elif self._match_keyword('null') or self._match_keyword('None'):
|
|
4459
|
+
# null; for removal without replacement
|
|
4460
|
+
code_block = None
|
|
4461
|
+
else:
|
|
4462
|
+
# Expression form: local::func +<<== expression;
|
|
4463
|
+
code_block = self._parse_expression()
|
|
4464
|
+
|
|
4465
|
+
return ASTNode('local_injection', value={
|
|
4466
|
+
'target': local_name,
|
|
4467
|
+
'mode': mode,
|
|
4468
|
+
'filters': filters,
|
|
4469
|
+
'code': code_block
|
|
4470
|
+
}, line=local_node.line, column=local_node.column)
|
|
4471
|
+
|
|
3725
4472
|
def _parse_injection_filter(self) -> Optional[list]:
|
|
3726
4473
|
"""Parse injection filter(s): [type::helper=value] or [f1][f2][f3]...
|
|
3727
4474
|
|
|
@@ -3969,6 +4716,16 @@ class CSSLParser:
|
|
|
3969
4716
|
elif self._match(TokenType.COMPARE_GE):
|
|
3970
4717
|
right = self._parse_term()
|
|
3971
4718
|
left = ASTNode('binary', value={'op': '>=', 'left': left, 'right': right})
|
|
4719
|
+
elif self._check(TokenType.KEYWORD) and self._peek().value == 'not':
|
|
4720
|
+
# Check for 'not in' compound operator: item not in list
|
|
4721
|
+
next_tok = self._peek(1)
|
|
4722
|
+
if next_tok and next_tok.type == TokenType.KEYWORD and next_tok.value == 'in':
|
|
4723
|
+
self._advance() # consume 'not'
|
|
4724
|
+
self._advance() # consume 'in'
|
|
4725
|
+
right = self._parse_term()
|
|
4726
|
+
left = ASTNode('binary', value={'op': 'not in', 'left': left, 'right': right})
|
|
4727
|
+
else:
|
|
4728
|
+
break
|
|
3972
4729
|
elif self._check(TokenType.KEYWORD) and self._peek().value == 'in':
|
|
3973
4730
|
# 'in' operator for containment: item in list
|
|
3974
4731
|
self._advance() # consume 'in'
|
|
@@ -4016,6 +4773,10 @@ class CSSLParser:
|
|
|
4016
4773
|
if self._match(TokenType.NOT) or self._match_keyword('not'):
|
|
4017
4774
|
operand = self._parse_unary()
|
|
4018
4775
|
return ASTNode('unary', value={'op': 'not', 'operand': operand})
|
|
4776
|
+
# v4.9.3: await as unary prefix for expressions
|
|
4777
|
+
if self._match_keyword('await'):
|
|
4778
|
+
operand = self._parse_unary()
|
|
4779
|
+
return ASTNode('await', value=operand)
|
|
4019
4780
|
if self._match(TokenType.MINUS):
|
|
4020
4781
|
operand = self._parse_unary()
|
|
4021
4782
|
return ASTNode('unary', value={'op': '-', 'operand': operand})
|
|
@@ -4034,9 +4795,18 @@ class CSSLParser:
|
|
|
4034
4795
|
|
|
4035
4796
|
# Non-null assertion: *$var, *@module, *identifier
|
|
4036
4797
|
# Also type exclusion filter: *[type]expr - exclude type from return
|
|
4798
|
+
# v4.9.0: Pointer syntax: *<expr> to get address
|
|
4037
4799
|
if self._check(TokenType.MULTIPLY):
|
|
4038
4800
|
next_token = self._peek(1)
|
|
4039
4801
|
|
|
4802
|
+
# v4.9.0: Pointer address: *<expr> - get address of expression
|
|
4803
|
+
if next_token and next_token.type == TokenType.COMPARE_LT:
|
|
4804
|
+
self._advance() # consume *
|
|
4805
|
+
self._advance() # consume <
|
|
4806
|
+
operand = self._parse_expression()
|
|
4807
|
+
self._expect(TokenType.COMPARE_GT) # expect >
|
|
4808
|
+
return ASTNode('pointer_address', value={'operand': operand})
|
|
4809
|
+
|
|
4040
4810
|
# Check for type exclusion filter: *[string], *[int], etc.
|
|
4041
4811
|
if next_token and next_token.type == TokenType.BRACKET_START:
|
|
4042
4812
|
self._advance() # consume *
|
|
@@ -4084,6 +4854,29 @@ class CSSLParser:
|
|
|
4084
4854
|
else:
|
|
4085
4855
|
break
|
|
4086
4856
|
return node
|
|
4857
|
+
# v4.7: Handle this.member syntax (using DOT instead of ->)
|
|
4858
|
+
elif self._match(TokenType.DOT):
|
|
4859
|
+
member = self._advance().value
|
|
4860
|
+
node = ASTNode('member_access', value={
|
|
4861
|
+
'object': ASTNode('identifier', value='this'),
|
|
4862
|
+
'member': member
|
|
4863
|
+
})
|
|
4864
|
+
# Continue to check for chained calls, member access, indexing
|
|
4865
|
+
while True:
|
|
4866
|
+
if self._match(TokenType.PAREN_START):
|
|
4867
|
+
args, kwargs = self._parse_call_arguments()
|
|
4868
|
+
self._expect(TokenType.PAREN_END)
|
|
4869
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
4870
|
+
elif self._match(TokenType.DOT):
|
|
4871
|
+
member = self._advance().value
|
|
4872
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
4873
|
+
elif self._match(TokenType.BRACKET_START):
|
|
4874
|
+
index = self._parse_expression()
|
|
4875
|
+
self._expect(TokenType.BRACKET_END)
|
|
4876
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
4877
|
+
else:
|
|
4878
|
+
break
|
|
4879
|
+
return node
|
|
4087
4880
|
else:
|
|
4088
4881
|
# Just 'this' keyword alone - return as identifier for now
|
|
4089
4882
|
return ASTNode('identifier', value='this')
|
|
@@ -4237,6 +5030,58 @@ class CSSLParser:
|
|
|
4237
5030
|
break
|
|
4238
5031
|
return node
|
|
4239
5032
|
|
|
5033
|
+
# v4.9.0: Pointer reference: ?name (dereferences pointer)
|
|
5034
|
+
if self._check(TokenType.POINTER_REF):
|
|
5035
|
+
token = self._advance()
|
|
5036
|
+
node = ASTNode('pointer_ref', value=token.value, line=token.line, column=token.column)
|
|
5037
|
+
# Check for member access, calls, indexing
|
|
5038
|
+
while True:
|
|
5039
|
+
if self._match(TokenType.PAREN_START):
|
|
5040
|
+
args, kwargs = self._parse_call_arguments()
|
|
5041
|
+
self._expect(TokenType.PAREN_END)
|
|
5042
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
5043
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
5044
|
+
member = self._advance().value
|
|
5045
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
5046
|
+
elif self._match(TokenType.BRACKET_START):
|
|
5047
|
+
index = self._parse_expression()
|
|
5048
|
+
self._expect(TokenType.BRACKET_END)
|
|
5049
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
5050
|
+
else:
|
|
5051
|
+
break
|
|
5052
|
+
return node
|
|
5053
|
+
|
|
5054
|
+
# v4.9.2: Local reference for hook variable access: local::varname, local::func
|
|
5055
|
+
if self._check(TokenType.LOCAL_REF):
|
|
5056
|
+
token = self._advance()
|
|
5057
|
+
local_name = token.value # The local variable/function name
|
|
5058
|
+
node = ASTNode('local_ref', value=local_name, line=token.line, column=token.column)
|
|
5059
|
+
# Check for injection operations on local::func
|
|
5060
|
+
if self._check(TokenType.INFUSE_MINUS_LEFT) or self._check(TokenType.INFUSE_PLUS_LEFT) or \
|
|
5061
|
+
self._check(TokenType.INFUSE_LEFT):
|
|
5062
|
+
# local::func -<<== {...} or local::func +<<== {...}
|
|
5063
|
+
return self._parse_local_injection(node)
|
|
5064
|
+
# Check for assignment: local::varname = value
|
|
5065
|
+
if self._match(TokenType.EQUALS):
|
|
5066
|
+
value = self._parse_expression()
|
|
5067
|
+
return ASTNode('local_assign', value={'name': local_name, 'value': value})
|
|
5068
|
+
# Check for member access, calls, indexing
|
|
5069
|
+
while True:
|
|
5070
|
+
if self._match(TokenType.PAREN_START):
|
|
5071
|
+
args, kwargs = self._parse_call_arguments()
|
|
5072
|
+
self._expect(TokenType.PAREN_END)
|
|
5073
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
5074
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
5075
|
+
member = self._advance().value
|
|
5076
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
5077
|
+
elif self._match(TokenType.BRACKET_START):
|
|
5078
|
+
index = self._parse_expression()
|
|
5079
|
+
self._expect(TokenType.BRACKET_END)
|
|
5080
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
5081
|
+
else:
|
|
5082
|
+
break
|
|
5083
|
+
return node
|
|
5084
|
+
|
|
4240
5085
|
# v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
|
|
4241
5086
|
if self._check(TokenType.LANG_INSTANCE_REF):
|
|
4242
5087
|
token = self._advance()
|
|
@@ -4261,7 +5106,20 @@ class CSSLParser:
|
|
|
4261
5106
|
return node
|
|
4262
5107
|
|
|
4263
5108
|
if self._check(TokenType.NUMBER):
|
|
4264
|
-
|
|
5109
|
+
num_token = self._advance()
|
|
5110
|
+
num_value = num_token.value
|
|
5111
|
+
# v4.9.0: Check for byte notation x^y (0^102, 1^250)
|
|
5112
|
+
if self._check(TokenType.CARET):
|
|
5113
|
+
self._advance() # consume ^
|
|
5114
|
+
if not self._check(TokenType.NUMBER):
|
|
5115
|
+
raise CSSLSyntaxError("Expected number after '^' in byte literal", num_token.line)
|
|
5116
|
+
weight = self._advance().value
|
|
5117
|
+
if num_value not in (0, 1):
|
|
5118
|
+
raise CSSLSyntaxError(f"Byte base must be 0 or 1, got {num_value}", num_token.line)
|
|
5119
|
+
if not (0 <= weight <= 255):
|
|
5120
|
+
raise CSSLSyntaxError(f"Byte weight must be 0-255, got {weight}", num_token.line)
|
|
5121
|
+
return ASTNode('byte_literal', value={'base': num_value, 'weight': weight})
|
|
5122
|
+
return ASTNode('literal', value=num_value)
|
|
4265
5123
|
|
|
4266
5124
|
if self._check(TokenType.STRING):
|
|
4267
5125
|
return ASTNode('literal', value=self._advance().value)
|
|
@@ -4279,16 +5137,66 @@ class CSSLParser:
|
|
|
4279
5137
|
return ASTNode('type_literal', value=type_name)
|
|
4280
5138
|
|
|
4281
5139
|
if self._match(TokenType.PAREN_START):
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
5140
|
+
# v4.8.9: Check for typed expression: (type name = value)
|
|
5141
|
+
# This creates a typed variable and returns its value
|
|
5142
|
+
# Used with snapshot assignment: %xyz = (int number = 200)
|
|
5143
|
+
saved_pos = self.pos
|
|
5144
|
+
is_typed_expr = False
|
|
5145
|
+
|
|
5146
|
+
# Check pattern: type identifier =
|
|
5147
|
+
if ((self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value)) or
|
|
5148
|
+
self._check(TokenType.IDENTIFIER)):
|
|
5149
|
+
potential_type = self._current().value
|
|
5150
|
+
self._advance() # consume type
|
|
5151
|
+
|
|
5152
|
+
# Check for generic type like vector<int>
|
|
5153
|
+
if self._check(TokenType.COMPARE_LT):
|
|
5154
|
+
self._advance() # consume <
|
|
5155
|
+
self._parse_generic_type_content() # consume generic content
|
|
5156
|
+
potential_type += '<...>' # mark as generic
|
|
5157
|
+
|
|
5158
|
+
if self._check(TokenType.IDENTIFIER):
|
|
5159
|
+
var_name = self._advance().value
|
|
5160
|
+
if self._check(TokenType.EQUALS):
|
|
5161
|
+
# This is a typed expression!
|
|
5162
|
+
is_typed_expr = True
|
|
5163
|
+
self._advance() # consume =
|
|
5164
|
+
value = self._parse_expression()
|
|
5165
|
+
self._expect(TokenType.PAREN_END)
|
|
5166
|
+
return ASTNode('typed_expression', value={
|
|
5167
|
+
'type': potential_type.replace('<...>', ''), # clean generic marker
|
|
5168
|
+
'name': var_name,
|
|
5169
|
+
'value': value
|
|
5170
|
+
})
|
|
5171
|
+
|
|
5172
|
+
# Not a typed expression, restore position and parse normally
|
|
5173
|
+
if not is_typed_expr:
|
|
5174
|
+
self.pos = saved_pos
|
|
5175
|
+
expr = self._parse_expression()
|
|
5176
|
+
# v4.9.2: Check for tuple literal (expr, expr, ...)
|
|
5177
|
+
if self._check(TokenType.COMMA):
|
|
5178
|
+
# This is a tuple - collect all elements
|
|
5179
|
+
elements = [expr]
|
|
5180
|
+
while self._match(TokenType.COMMA):
|
|
5181
|
+
if self._check(TokenType.PAREN_END):
|
|
5182
|
+
# Trailing comma allowed: (a, b,)
|
|
5183
|
+
break
|
|
5184
|
+
elements.append(self._parse_expression())
|
|
5185
|
+
self._expect(TokenType.PAREN_END)
|
|
5186
|
+
return ASTNode('tuple', value=elements)
|
|
5187
|
+
self._expect(TokenType.PAREN_END)
|
|
5188
|
+
return expr
|
|
4285
5189
|
|
|
4286
5190
|
if self._match(TokenType.BLOCK_START):
|
|
4287
|
-
# Distinguish between
|
|
4288
|
-
# Object literal
|
|
4289
|
-
#
|
|
5191
|
+
# Distinguish between:
|
|
5192
|
+
# 1. Object literal { key = value } - for dict-like initialization
|
|
5193
|
+
# 2. Array literal { 1, 2, 3 } - for vector/array initialization (v4.9.2)
|
|
5194
|
+
# 3. Action block { expr; } - for code blocks that return last value
|
|
4290
5195
|
if self._is_object_literal():
|
|
4291
5196
|
return self._parse_object()
|
|
5197
|
+
elif self._is_array_literal():
|
|
5198
|
+
# v4.9.2: Parse as array for vector<T> i = { 1, 2, 3 } initialization
|
|
5199
|
+
return self._parse_brace_array()
|
|
4292
5200
|
else:
|
|
4293
5201
|
return self._parse_action_block_expression()
|
|
4294
5202
|
|
|
@@ -4367,11 +5275,14 @@ class CSSLParser:
|
|
|
4367
5275
|
def _parse_identifier_or_call(self) -> ASTNode:
|
|
4368
5276
|
name = self._advance().value
|
|
4369
5277
|
|
|
4370
|
-
# Check for namespace syntax: json::read, string::cut, etc.
|
|
4371
|
-
|
|
5278
|
+
# Check for namespace syntax: json::read, string::cut, namespace::inner::func, etc.
|
|
5279
|
+
# v4.8: Support multiple levels of :: for nested namespace access
|
|
5280
|
+
while self._match(TokenType.DOUBLE_COLON):
|
|
4372
5281
|
if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
|
|
4373
5282
|
namespace_member = self._advance().value
|
|
4374
5283
|
name = f"{name}::{namespace_member}"
|
|
5284
|
+
else:
|
|
5285
|
+
break
|
|
4375
5286
|
|
|
4376
5287
|
# Check for instance<"name"> syntax - gets/creates shared instance
|
|
4377
5288
|
if name == 'instance' and self._check(TokenType.COMPARE_LT):
|
|
@@ -4392,6 +5303,7 @@ class CSSLParser:
|
|
|
4392
5303
|
self._advance() # consume <
|
|
4393
5304
|
element_type = 'dynamic'
|
|
4394
5305
|
value_type = None # For map<K, V>
|
|
5306
|
+
queue_size = 'dynamic' # v4.7: For queue<T, size>
|
|
4395
5307
|
|
|
4396
5308
|
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
4397
5309
|
element_type = self._advance().value
|
|
@@ -4404,6 +5316,20 @@ class CSSLParser:
|
|
|
4404
5316
|
else:
|
|
4405
5317
|
value_type = 'dynamic'
|
|
4406
5318
|
|
|
5319
|
+
# v4.7: Check for second parameter for queue<T, size>
|
|
5320
|
+
if name == 'queue' and self._check(TokenType.COMMA):
|
|
5321
|
+
self._advance() # consume ,
|
|
5322
|
+
if self._check(TokenType.NUMBER):
|
|
5323
|
+
queue_size = int(self._advance().value)
|
|
5324
|
+
elif self._check(TokenType.KEYWORD) and self._current().value == 'dynamic':
|
|
5325
|
+
self._advance() # consume 'dynamic'
|
|
5326
|
+
queue_size = 'dynamic'
|
|
5327
|
+
elif self._check(TokenType.IDENTIFIER) and self._current().value == 'dynamic':
|
|
5328
|
+
self._advance() # consume 'dynamic'
|
|
5329
|
+
queue_size = 'dynamic'
|
|
5330
|
+
else:
|
|
5331
|
+
queue_size = 'dynamic'
|
|
5332
|
+
|
|
4407
5333
|
self._expect(TokenType.COMPARE_GT) # consume >
|
|
4408
5334
|
|
|
4409
5335
|
# Check for inline initialization: map<K,V>{"key": "value", ...}
|
|
@@ -4455,6 +5381,7 @@ class CSSLParser:
|
|
|
4455
5381
|
'type': name,
|
|
4456
5382
|
'element_type': element_type,
|
|
4457
5383
|
'value_type': value_type,
|
|
5384
|
+
'queue_size': queue_size, # v4.7: For queue<T, size>
|
|
4458
5385
|
'init_values': init_values
|
|
4459
5386
|
})
|
|
4460
5387
|
|
|
@@ -4500,7 +5427,8 @@ class CSSLParser:
|
|
|
4500
5427
|
node = ASTNode('identifier', value=name)
|
|
4501
5428
|
|
|
4502
5429
|
while True:
|
|
4503
|
-
|
|
5430
|
+
# v4.8.5: Support both . and -> for member access (C++ style)
|
|
5431
|
+
if self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
4504
5432
|
member = self._advance().value
|
|
4505
5433
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
4506
5434
|
elif self._match(TokenType.PAREN_START):
|
|
@@ -4547,6 +5475,74 @@ class CSSLParser:
|
|
|
4547
5475
|
self.pos = saved_pos
|
|
4548
5476
|
return is_object
|
|
4549
5477
|
|
|
5478
|
+
def _is_array_literal(self) -> bool:
|
|
5479
|
+
"""Check if current position is an array literal { 1, 2, 3 } for vector/array initialization.
|
|
5480
|
+
|
|
5481
|
+
v4.9.2: Array literal: { value, value, ... } - comma-separated values
|
|
5482
|
+
NOT array literal: { expr; } - semicolon-separated (action block)
|
|
5483
|
+
"""
|
|
5484
|
+
# Empty block is not array literal
|
|
5485
|
+
if self._check(TokenType.BLOCK_END):
|
|
5486
|
+
return False
|
|
5487
|
+
|
|
5488
|
+
# Save position for lookahead
|
|
5489
|
+
saved_pos = self.pos
|
|
5490
|
+
|
|
5491
|
+
is_array = False
|
|
5492
|
+
# Try to parse first expression and check if followed by comma
|
|
5493
|
+
try:
|
|
5494
|
+
# Check for simple value followed by comma
|
|
5495
|
+
if (self._check(TokenType.NUMBER) or self._check(TokenType.STRING) or
|
|
5496
|
+
self._check(TokenType.BOOLEAN) or self._check(TokenType.IDENTIFIER) or
|
|
5497
|
+
self._check(TokenType.BRACKET_START) or self._check(TokenType.PAREN_START)):
|
|
5498
|
+
# Skip the first value (could be simple or complex)
|
|
5499
|
+
depth = 0
|
|
5500
|
+
while not self._is_at_end():
|
|
5501
|
+
if self._check(TokenType.PAREN_START) or self._check(TokenType.BRACKET_START) or self._check(TokenType.BLOCK_START):
|
|
5502
|
+
depth += 1
|
|
5503
|
+
self._advance()
|
|
5504
|
+
elif self._check(TokenType.PAREN_END) or self._check(TokenType.BRACKET_END) or self._check(TokenType.BLOCK_END):
|
|
5505
|
+
if depth > 0:
|
|
5506
|
+
depth -= 1
|
|
5507
|
+
self._advance()
|
|
5508
|
+
else:
|
|
5509
|
+
break
|
|
5510
|
+
elif self._check(TokenType.COMMA) and depth == 0:
|
|
5511
|
+
# Found comma at top level - this is an array literal
|
|
5512
|
+
is_array = True
|
|
5513
|
+
break
|
|
5514
|
+
elif self._check(TokenType.SEMICOLON) and depth == 0:
|
|
5515
|
+
# Found semicolon - this is an action block
|
|
5516
|
+
is_array = False
|
|
5517
|
+
break
|
|
5518
|
+
else:
|
|
5519
|
+
self._advance()
|
|
5520
|
+
except Exception:
|
|
5521
|
+
pass
|
|
5522
|
+
|
|
5523
|
+
# Restore position
|
|
5524
|
+
self.pos = saved_pos
|
|
5525
|
+
return is_array
|
|
5526
|
+
|
|
5527
|
+
def _parse_brace_array(self) -> ASTNode:
|
|
5528
|
+
"""Parse brace-enclosed array literal { 1, 2, 3 } for vector/array initialization.
|
|
5529
|
+
|
|
5530
|
+
v4.9.2: Returns an 'array' node, same as [ 1, 2, 3 ] bracket arrays.
|
|
5531
|
+
"""
|
|
5532
|
+
elements = []
|
|
5533
|
+
|
|
5534
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
5535
|
+
# Parse expression
|
|
5536
|
+
expr = self._parse_expression()
|
|
5537
|
+
elements.append(expr)
|
|
5538
|
+
|
|
5539
|
+
# Expect comma or end
|
|
5540
|
+
if not self._match(TokenType.COMMA):
|
|
5541
|
+
break
|
|
5542
|
+
|
|
5543
|
+
self._expect(TokenType.BLOCK_END)
|
|
5544
|
+
return ASTNode('array', value=elements)
|
|
5545
|
+
|
|
4550
5546
|
def _parse_action_block_expression(self) -> ASTNode:
|
|
4551
5547
|
"""Parse an action block expression: { expr; expr2; } returns last value
|
|
4552
5548
|
|