IncludeCPP 3.7.1__py3-none-any.whl → 3.7.25__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/__init__.py +1 -1
- includecpp/__init__.pyi +2 -2
- includecpp/cli/commands.py +278 -75
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +27 -8
- includecpp/core/cssl/__init__.py +7 -2
- includecpp/core/cssl/cssl_builtins.py +201 -9
- includecpp/core/cssl/cssl_builtins.pyi +3682 -401
- includecpp/core/cssl/cssl_parser.py +291 -40
- includecpp/core/cssl/cssl_runtime.py +629 -40
- includecpp/core/cssl/cssl_syntax.py +7 -7
- includecpp/core/cssl/cssl_types.py +75 -2
- includecpp/core/cssl_bridge.py +540 -53
- includecpp/vscode/cssl/extension.js +133 -0
- includecpp/vscode/cssl/images/cssl.png +0 -0
- includecpp/vscode/cssl/images/cssl_pl.png +0 -0
- includecpp/vscode/cssl/language-configuration.json +1 -4
- includecpp/vscode/cssl/package.json +117 -11
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +213 -29
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/METADATA +2 -2
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/RECORD +24 -21
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/WHEEL +0 -0
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/entry_points.txt +0 -0
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.7.1.dist-info → includecpp-3.7.25.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
CSSL Parser - Lexer and Parser for
|
|
2
|
+
CSSL Parser - Lexer and Parser for CSSL Language
|
|
3
3
|
|
|
4
4
|
Features:
|
|
5
5
|
- Complete tokenization of CSSL syntax
|
|
@@ -108,6 +108,8 @@ class TokenType(Enum):
|
|
|
108
108
|
COMMENT = auto()
|
|
109
109
|
NEWLINE = auto()
|
|
110
110
|
EOF = auto()
|
|
111
|
+
# Super-functions for .cssl-pl payload files (v3.8.0)
|
|
112
|
+
SUPER_FUNC = auto() # #$run(), #$exec(), #$printl() - pre-execution hooks
|
|
111
113
|
|
|
112
114
|
|
|
113
115
|
KEYWORDS = {
|
|
@@ -212,9 +214,14 @@ class CSSLLexer:
|
|
|
212
214
|
|
|
213
215
|
char = self.source[self.pos]
|
|
214
216
|
|
|
215
|
-
# Comments
|
|
217
|
+
# Super-functions (#$) or Comments (# and // style)
|
|
216
218
|
if char == '#':
|
|
217
|
-
self.
|
|
219
|
+
if self._peek(1) == '$':
|
|
220
|
+
# Super-function: #$run(), #$exec(), #$printl()
|
|
221
|
+
self._read_super_function()
|
|
222
|
+
else:
|
|
223
|
+
# Regular comment
|
|
224
|
+
self._skip_comment()
|
|
218
225
|
elif char == '/' and self._peek(1) == '/':
|
|
219
226
|
# C-style // comment - NEW
|
|
220
227
|
self._skip_comment()
|
|
@@ -245,8 +252,15 @@ class CSSLLexer:
|
|
|
245
252
|
# $<name> shared object reference
|
|
246
253
|
self._read_shared_ref()
|
|
247
254
|
elif char == '%':
|
|
248
|
-
# %<name> captured reference
|
|
249
|
-
self.
|
|
255
|
+
# Check if this is %<name> captured reference or % modulo operator
|
|
256
|
+
next_char = self._peek(1)
|
|
257
|
+
if next_char and (next_char.isalpha() or next_char == '_'):
|
|
258
|
+
# %<name> captured reference (for infusion)
|
|
259
|
+
self._read_captured_ref()
|
|
260
|
+
else:
|
|
261
|
+
# % modulo operator
|
|
262
|
+
self._add_token(TokenType.MODULO, '%')
|
|
263
|
+
self._advance()
|
|
250
264
|
elif char == '&':
|
|
251
265
|
# & for references
|
|
252
266
|
if self._peek(1) == '&':
|
|
@@ -314,9 +328,6 @@ class CSSLLexer:
|
|
|
314
328
|
else:
|
|
315
329
|
# Already handled by // comment check above, but just in case
|
|
316
330
|
self._skip_comment()
|
|
317
|
-
elif char == '%':
|
|
318
|
-
self._add_token(TokenType.MODULO, '%')
|
|
319
|
-
self._advance()
|
|
320
331
|
elif char == '<':
|
|
321
332
|
self._read_less_than()
|
|
322
333
|
elif char == '>':
|
|
@@ -446,6 +457,31 @@ class CSSLLexer:
|
|
|
446
457
|
else:
|
|
447
458
|
self._add_token(TokenType.IDENTIFIER, value)
|
|
448
459
|
|
|
460
|
+
def _read_super_function(self):
|
|
461
|
+
"""Read #$<name>(...) super-function call for .cssl-pl payloads.
|
|
462
|
+
|
|
463
|
+
Super-functions are pre-execution hooks that run when a payload is loaded.
|
|
464
|
+
Valid super-functions: #$run(), #$exec(), #$printl()
|
|
465
|
+
|
|
466
|
+
Syntax:
|
|
467
|
+
#$run(initFunction); // Call a function at load time
|
|
468
|
+
#$exec(setup()); // Execute expression at load time
|
|
469
|
+
#$printl("Payload loaded"); // Print at load time
|
|
470
|
+
"""
|
|
471
|
+
start = self.pos
|
|
472
|
+
self._advance() # skip '#'
|
|
473
|
+
self._advance() # skip '$'
|
|
474
|
+
|
|
475
|
+
# Read the super-function name (run, exec, printl, etc.)
|
|
476
|
+
name_start = self.pos
|
|
477
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
478
|
+
self._advance()
|
|
479
|
+
func_name = self.source[name_start:self.pos]
|
|
480
|
+
|
|
481
|
+
# Store as #$<name> token value
|
|
482
|
+
value = f'#${func_name}'
|
|
483
|
+
self._add_token(TokenType.SUPER_FUNC, value)
|
|
484
|
+
|
|
449
485
|
def _read_self_ref(self):
|
|
450
486
|
"""Read s@<name> or s@<name>.<member>... self-reference"""
|
|
451
487
|
start = self.pos
|
|
@@ -816,7 +852,7 @@ class CSSLParser:
|
|
|
816
852
|
if self._match_keyword('open'):
|
|
817
853
|
param_info['open'] = True
|
|
818
854
|
|
|
819
|
-
# Handle type annotations
|
|
855
|
+
# Handle type annotations (builtin types like int, string, etc.)
|
|
820
856
|
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
821
857
|
param_info['type'] = self._advance().value
|
|
822
858
|
|
|
@@ -840,6 +876,39 @@ class CSSLParser:
|
|
|
840
876
|
self._advance()
|
|
841
877
|
param_info['generic'] = ''.join(generic_parts)
|
|
842
878
|
|
|
879
|
+
# Handle custom class types (identifier followed by another identifier = type + name)
|
|
880
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
881
|
+
# Look ahead: if next token is also an identifier, current is the type
|
|
882
|
+
saved_pos = self.pos
|
|
883
|
+
potential_type = self._advance().value
|
|
884
|
+
|
|
885
|
+
# Check for generic type parameter <T> on custom type
|
|
886
|
+
if self._check(TokenType.COMPARE_LT):
|
|
887
|
+
self._advance()
|
|
888
|
+
generic_parts = []
|
|
889
|
+
depth = 1
|
|
890
|
+
while depth > 0 and not self._is_at_end():
|
|
891
|
+
if self._check(TokenType.COMPARE_LT):
|
|
892
|
+
depth += 1
|
|
893
|
+
generic_parts.append('<')
|
|
894
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
895
|
+
depth -= 1
|
|
896
|
+
if depth > 0:
|
|
897
|
+
generic_parts.append('>')
|
|
898
|
+
elif self._check(TokenType.COMMA):
|
|
899
|
+
generic_parts.append(',')
|
|
900
|
+
else:
|
|
901
|
+
generic_parts.append(self._current().value)
|
|
902
|
+
self._advance()
|
|
903
|
+
param_info['generic'] = ''.join(generic_parts)
|
|
904
|
+
|
|
905
|
+
# If followed by identifier, this is "Type name" pattern
|
|
906
|
+
if self._check(TokenType.IDENTIFIER):
|
|
907
|
+
param_info['type'] = potential_type
|
|
908
|
+
else:
|
|
909
|
+
# Not a type, restore position - this is just a param name
|
|
910
|
+
self.pos = saved_pos
|
|
911
|
+
|
|
843
912
|
# Handle reference operator &
|
|
844
913
|
if self._match(TokenType.AMPERSAND):
|
|
845
914
|
param_info['ref'] = True
|
|
@@ -882,6 +951,27 @@ class CSSLParser:
|
|
|
882
951
|
self._expect(TokenType.BLOCK_END)
|
|
883
952
|
return node
|
|
884
953
|
|
|
954
|
+
def _looks_like_namespace_call(self) -> bool:
|
|
955
|
+
"""Check if current position looks like a namespace function call.
|
|
956
|
+
|
|
957
|
+
Pattern: keyword::identifier(...) like json::write(), string::cut()
|
|
958
|
+
This allows type keywords to be used as namespace prefixes for function calls.
|
|
959
|
+
"""
|
|
960
|
+
if not self._check(TokenType.KEYWORD):
|
|
961
|
+
return False
|
|
962
|
+
|
|
963
|
+
# Save position
|
|
964
|
+
saved_pos = self.pos
|
|
965
|
+
|
|
966
|
+
self._advance() # Skip keyword
|
|
967
|
+
|
|
968
|
+
# Must be followed by ::
|
|
969
|
+
result = self._check(TokenType.DOUBLE_COLON)
|
|
970
|
+
|
|
971
|
+
# Restore position
|
|
972
|
+
self.pos = saved_pos
|
|
973
|
+
return result
|
|
974
|
+
|
|
885
975
|
def _looks_like_typed_variable(self) -> bool:
|
|
886
976
|
"""Check if current position looks like a typed variable declaration:
|
|
887
977
|
type_name varName; or type_name<T> varName; or type_name varName = value;
|
|
@@ -1377,10 +1467,14 @@ class CSSLParser:
|
|
|
1377
1467
|
elif self._looks_like_function_declaration():
|
|
1378
1468
|
# Nested typed function (e.g., void Level2() { ... })
|
|
1379
1469
|
return self._parse_typed_function()
|
|
1470
|
+
elif self._check(TokenType.SUPER_FUNC):
|
|
1471
|
+
# Super-function for .cssl-pl payload files
|
|
1472
|
+
return self._parse_super_function()
|
|
1380
1473
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1381
1474
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
1382
1475
|
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
1383
|
-
(self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new'))
|
|
1476
|
+
(self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new')) or
|
|
1477
|
+
self._looks_like_namespace_call()):
|
|
1384
1478
|
return self._parse_expression_statement()
|
|
1385
1479
|
else:
|
|
1386
1480
|
self._advance()
|
|
@@ -1594,23 +1688,42 @@ class CSSLParser:
|
|
|
1594
1688
|
return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
|
|
1595
1689
|
|
|
1596
1690
|
def _parse_python_style_for(self) -> ASTNode:
|
|
1597
|
-
"""Parse Python-style for loop: for (i in range(...)) { }
|
|
1691
|
+
"""Parse Python-style for loop: for (i in range(...)) { } or for (item in collection) { }
|
|
1598
1692
|
|
|
1599
1693
|
Supports:
|
|
1600
1694
|
for (i in range(n)) { } - 0 to n-1
|
|
1601
1695
|
for (i in range(start, end)) { } - start to end-1
|
|
1602
1696
|
for (i in range(start, end, step)) { }
|
|
1697
|
+
for (item in collection) { } - iterate over list/vector
|
|
1698
|
+
for (item in @global_collection) { } - iterate over global
|
|
1603
1699
|
"""
|
|
1604
1700
|
var_name = self._advance().value
|
|
1605
1701
|
self._expect(TokenType.KEYWORD) # 'in'
|
|
1606
1702
|
|
|
1607
|
-
#
|
|
1703
|
+
# Check if this is range() or collection iteration
|
|
1704
|
+
is_range = False
|
|
1608
1705
|
if self._check(TokenType.KEYWORD) and self._peek().value == 'range':
|
|
1609
1706
|
self._advance() # consume 'range' keyword
|
|
1707
|
+
is_range = True
|
|
1610
1708
|
elif self._check(TokenType.IDENTIFIER) and self._peek().value == 'range':
|
|
1611
1709
|
self._advance() # consume 'range' identifier
|
|
1612
|
-
|
|
1613
|
-
|
|
1710
|
+
is_range = True
|
|
1711
|
+
|
|
1712
|
+
# If not range, parse as collection iteration
|
|
1713
|
+
if not is_range:
|
|
1714
|
+
iterable = self._parse_expression()
|
|
1715
|
+
self._expect(TokenType.PAREN_END)
|
|
1716
|
+
|
|
1717
|
+
node = ASTNode('foreach', value={'var': var_name, 'iterable': iterable}, children=[])
|
|
1718
|
+
self._expect(TokenType.BLOCK_START)
|
|
1719
|
+
|
|
1720
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
1721
|
+
stmt = self._parse_statement()
|
|
1722
|
+
if stmt:
|
|
1723
|
+
node.children.append(stmt)
|
|
1724
|
+
|
|
1725
|
+
self._expect(TokenType.BLOCK_END)
|
|
1726
|
+
return node
|
|
1614
1727
|
|
|
1615
1728
|
self._expect(TokenType.PAREN_START)
|
|
1616
1729
|
first_arg = self._parse_expression()
|
|
@@ -1722,11 +1835,56 @@ class CSSLParser:
|
|
|
1722
1835
|
return node
|
|
1723
1836
|
|
|
1724
1837
|
def _parse_return(self) -> ASTNode:
|
|
1725
|
-
|
|
1838
|
+
"""Parse return statement, supporting multiple values for shuffled functions.
|
|
1839
|
+
|
|
1840
|
+
Syntax:
|
|
1841
|
+
return; // Return None
|
|
1842
|
+
return value; // Return single value
|
|
1843
|
+
return a, b, c; // Return multiple values (for shuffled)
|
|
1844
|
+
"""
|
|
1845
|
+
values = []
|
|
1726
1846
|
if not self._check(TokenType.SEMICOLON) and not self._check(TokenType.BLOCK_END):
|
|
1727
|
-
|
|
1847
|
+
values.append(self._parse_expression())
|
|
1848
|
+
|
|
1849
|
+
# Check for comma-separated return values (shuffled return)
|
|
1850
|
+
while self._check(TokenType.COMMA):
|
|
1851
|
+
self._advance() # consume comma
|
|
1852
|
+
values.append(self._parse_expression())
|
|
1853
|
+
|
|
1728
1854
|
self._match(TokenType.SEMICOLON)
|
|
1729
|
-
|
|
1855
|
+
|
|
1856
|
+
if len(values) == 0:
|
|
1857
|
+
return ASTNode('return', value=None)
|
|
1858
|
+
elif len(values) == 1:
|
|
1859
|
+
return ASTNode('return', value=values[0])
|
|
1860
|
+
else:
|
|
1861
|
+
# Multiple return values - create tuple return
|
|
1862
|
+
return ASTNode('return', value={'multiple': True, 'values': values})
|
|
1863
|
+
|
|
1864
|
+
def _parse_super_function(self) -> ASTNode:
|
|
1865
|
+
"""Parse super-function for .cssl-pl payload files.
|
|
1866
|
+
|
|
1867
|
+
Syntax:
|
|
1868
|
+
#$run(initFunction); // Call function at load time
|
|
1869
|
+
#$exec(setup()); // Execute expression at load time
|
|
1870
|
+
#$printl("Payload loaded"); // Print at load time
|
|
1871
|
+
|
|
1872
|
+
These are pre-execution hooks that run when payload() loads the file.
|
|
1873
|
+
"""
|
|
1874
|
+
token = self._advance() # Get the SUPER_FUNC token
|
|
1875
|
+
super_name = token.value # e.g., "#$run", "#$exec", "#$printl"
|
|
1876
|
+
|
|
1877
|
+
# Parse the arguments
|
|
1878
|
+
self._expect(TokenType.PAREN_START)
|
|
1879
|
+
args = []
|
|
1880
|
+
if not self._check(TokenType.PAREN_END):
|
|
1881
|
+
args.append(self._parse_expression())
|
|
1882
|
+
while self._match(TokenType.COMMA):
|
|
1883
|
+
args.append(self._parse_expression())
|
|
1884
|
+
self._expect(TokenType.PAREN_END)
|
|
1885
|
+
self._match(TokenType.SEMICOLON)
|
|
1886
|
+
|
|
1887
|
+
return ASTNode('super_func', value={'name': super_name, 'args': args})
|
|
1730
1888
|
|
|
1731
1889
|
def _parse_try(self) -> ASTNode:
|
|
1732
1890
|
node = ASTNode('try', children=[])
|
|
@@ -1783,36 +1941,65 @@ class CSSLParser:
|
|
|
1783
1941
|
self._expect(TokenType.BLOCK_END)
|
|
1784
1942
|
return node
|
|
1785
1943
|
|
|
1786
|
-
def _parse_injection_filter(self) -> Optional[
|
|
1787
|
-
"""Parse injection filter: [type::helper=value]
|
|
1788
|
-
|
|
1944
|
+
def _parse_injection_filter(self) -> Optional[list]:
|
|
1945
|
+
"""Parse injection filter(s): [type::helper=value] or [f1][f2][f3]...
|
|
1946
|
+
|
|
1947
|
+
Returns a list of filter dictionaries to support chained filters.
|
|
1948
|
+
"""
|
|
1949
|
+
if not self._check(TokenType.BRACKET_START):
|
|
1789
1950
|
return None
|
|
1790
1951
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1952
|
+
filters = []
|
|
1953
|
+
|
|
1954
|
+
# Parse multiple consecutive filter brackets
|
|
1955
|
+
while self._match(TokenType.BRACKET_START):
|
|
1956
|
+
filter_info = {}
|
|
1957
|
+
# Parse type::helper=value patterns within this bracket
|
|
1958
|
+
while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
|
|
1959
|
+
if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
|
|
1960
|
+
filter_type = self._advance().value
|
|
1961
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1962
|
+
helper = self._advance().value
|
|
1963
|
+
if self._match(TokenType.EQUALS):
|
|
1964
|
+
value = self._parse_expression()
|
|
1965
|
+
filter_info[f'{filter_type}::{helper}'] = value
|
|
1966
|
+
else:
|
|
1967
|
+
filter_info[f'{filter_type}::{helper}'] = True
|
|
1801
1968
|
else:
|
|
1802
|
-
filter_info[
|
|
1969
|
+
filter_info['type'] = filter_type
|
|
1970
|
+
elif self._check(TokenType.COMMA):
|
|
1971
|
+
self._advance()
|
|
1803
1972
|
else:
|
|
1804
|
-
|
|
1805
|
-
elif self._check(TokenType.COMMA):
|
|
1806
|
-
self._advance()
|
|
1807
|
-
else:
|
|
1808
|
-
break
|
|
1973
|
+
break
|
|
1809
1974
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1975
|
+
self._expect(TokenType.BRACKET_END)
|
|
1976
|
+
if filter_info:
|
|
1977
|
+
filters.append(filter_info)
|
|
1978
|
+
|
|
1979
|
+
return filters if filters else None
|
|
1812
1980
|
|
|
1813
1981
|
def _parse_expression_statement(self) -> Optional[ASTNode]:
|
|
1814
1982
|
expr = self._parse_expression()
|
|
1815
1983
|
|
|
1984
|
+
# === TUPLE UNPACKING: a, b, c = shuffled_func() ===
|
|
1985
|
+
# Check if we have comma-separated identifiers before =
|
|
1986
|
+
if expr.type == 'identifier' and self._check(TokenType.COMMA):
|
|
1987
|
+
targets = [expr]
|
|
1988
|
+
while self._match(TokenType.COMMA):
|
|
1989
|
+
next_expr = self._parse_expression()
|
|
1990
|
+
if next_expr.type == 'identifier':
|
|
1991
|
+
targets.append(next_expr)
|
|
1992
|
+
else:
|
|
1993
|
+
# Not a simple identifier list, this is something else
|
|
1994
|
+
# Restore and fall through to normal parsing
|
|
1995
|
+
break
|
|
1996
|
+
|
|
1997
|
+
# Check if followed by =
|
|
1998
|
+
if self._match(TokenType.EQUALS):
|
|
1999
|
+
value = self._parse_expression()
|
|
2000
|
+
self._match(TokenType.SEMICOLON)
|
|
2001
|
+
return ASTNode('tuple_assignment', value={'targets': targets, 'value': value})
|
|
2002
|
+
|
|
1816
2003
|
# === BASIC INJECTION: <== (replace target with source) ===
|
|
1817
2004
|
if self._match(TokenType.INJECT_LEFT):
|
|
1818
2005
|
# Check if this is a createcmd injection with a code block
|
|
@@ -2000,6 +2187,11 @@ class CSSLParser:
|
|
|
2000
2187
|
elif self._match(TokenType.COMPARE_GE):
|
|
2001
2188
|
right = self._parse_term()
|
|
2002
2189
|
left = ASTNode('binary', value={'op': '>=', 'left': left, 'right': right})
|
|
2190
|
+
elif self._check(TokenType.KEYWORD) and self._peek().value == 'in':
|
|
2191
|
+
# 'in' operator for containment: item in list
|
|
2192
|
+
self._advance() # consume 'in'
|
|
2193
|
+
right = self._parse_term()
|
|
2194
|
+
left = ASTNode('binary', value={'op': 'in', 'left': left, 'right': right})
|
|
2003
2195
|
else:
|
|
2004
2196
|
break
|
|
2005
2197
|
|
|
@@ -2335,17 +2527,76 @@ class CSSLParser:
|
|
|
2335
2527
|
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2336
2528
|
return ASTNode('instance_ref', value=instance_name)
|
|
2337
2529
|
|
|
2338
|
-
# Check for type generic instantiation: stack<string>, vector<int>, etc.
|
|
2530
|
+
# Check for type generic instantiation: stack<string>, vector<int>, map<string, int>, etc.
|
|
2339
2531
|
# This creates a new instance of the type with the specified element type
|
|
2340
2532
|
if name in TYPE_GENERICS and self._check(TokenType.COMPARE_LT):
|
|
2341
2533
|
self._advance() # consume <
|
|
2342
2534
|
element_type = 'dynamic'
|
|
2535
|
+
value_type = None # For map<K, V>
|
|
2536
|
+
|
|
2343
2537
|
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2344
2538
|
element_type = self._advance().value
|
|
2539
|
+
|
|
2540
|
+
# Check for second type parameter (map<K, V>)
|
|
2541
|
+
if name == 'map' and self._check(TokenType.COMMA):
|
|
2542
|
+
self._advance() # consume ,
|
|
2543
|
+
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2544
|
+
value_type = self._advance().value
|
|
2545
|
+
else:
|
|
2546
|
+
value_type = 'dynamic'
|
|
2547
|
+
|
|
2345
2548
|
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2549
|
+
|
|
2550
|
+
# Check for inline initialization: map<K,V>{"key": "value", ...}
|
|
2551
|
+
init_values = None
|
|
2552
|
+
if self._check(TokenType.BLOCK_START):
|
|
2553
|
+
self._advance() # consume {
|
|
2554
|
+
init_values = {}
|
|
2555
|
+
|
|
2556
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2557
|
+
# Parse key
|
|
2558
|
+
if self._check(TokenType.STRING):
|
|
2559
|
+
key = self._advance().value
|
|
2560
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2561
|
+
key = self._advance().value
|
|
2562
|
+
else:
|
|
2563
|
+
key = str(self._parse_expression().value) if hasattr(self._parse_expression(), 'value') else 'key'
|
|
2564
|
+
|
|
2565
|
+
# Expect : or =
|
|
2566
|
+
if self._check(TokenType.COLON):
|
|
2567
|
+
self._advance()
|
|
2568
|
+
elif self._check(TokenType.EQUALS):
|
|
2569
|
+
self._advance()
|
|
2570
|
+
|
|
2571
|
+
# Parse value
|
|
2572
|
+
value = self._parse_expression()
|
|
2573
|
+
init_values[key] = value
|
|
2574
|
+
|
|
2575
|
+
# Optional comma
|
|
2576
|
+
if self._check(TokenType.COMMA):
|
|
2577
|
+
self._advance()
|
|
2578
|
+
|
|
2579
|
+
self._expect(TokenType.BLOCK_END) # consume }
|
|
2580
|
+
|
|
2581
|
+
# Check for array-style initialization: vector<int>[1, 2, 3], array<string>["a", "b"]
|
|
2582
|
+
elif self._check(TokenType.BRACKET_START):
|
|
2583
|
+
self._advance() # consume [
|
|
2584
|
+
init_values = []
|
|
2585
|
+
|
|
2586
|
+
while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
|
|
2587
|
+
init_values.append(self._parse_expression())
|
|
2588
|
+
|
|
2589
|
+
# Optional comma
|
|
2590
|
+
if self._check(TokenType.COMMA):
|
|
2591
|
+
self._advance()
|
|
2592
|
+
|
|
2593
|
+
self._expect(TokenType.BRACKET_END) # consume ]
|
|
2594
|
+
|
|
2346
2595
|
return ASTNode('type_instantiation', value={
|
|
2347
2596
|
'type': name,
|
|
2348
|
-
'element_type': element_type
|
|
2597
|
+
'element_type': element_type,
|
|
2598
|
+
'value_type': value_type,
|
|
2599
|
+
'init_values': init_values
|
|
2349
2600
|
})
|
|
2350
2601
|
|
|
2351
2602
|
# Check for type-parameterized function call: OpenFind<string>(0)
|