IncludeCPP 3.6.0__py3-none-any.whl → 3.7.9__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/cli/commands.py +590 -11
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +301 -20
- includecpp/core/cssl/cssl_builtins.py +114 -0
- includecpp/core/cssl/cssl_builtins.pyi +1393 -0
- includecpp/core/cssl/cssl_parser.py +348 -70
- includecpp/core/cssl/cssl_runtime.py +456 -16
- includecpp/core/cssl/cssl_types.py +224 -2
- includecpp/core/cssl_bridge.py +477 -48
- includecpp/vscode/cssl/language-configuration.json +1 -4
- includecpp/vscode/cssl/package.json +24 -4
- includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +298 -17
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/METADATA +1 -1
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/RECORD +19 -17
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/WHEEL +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/entry_points.txt +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/top_level.txt +0 -0
|
@@ -101,17 +101,20 @@ class TokenType(Enum):
|
|
|
101
101
|
SELF_REF = auto() # s@<name> self-reference to global struct
|
|
102
102
|
SHARED_REF = auto() # $<name> shared object reference
|
|
103
103
|
CAPTURED_REF = auto() # %<name> captured reference (for infusion)
|
|
104
|
+
THIS_REF = auto() # this-><name> class member reference
|
|
104
105
|
PACKAGE = auto()
|
|
105
106
|
PACKAGE_INCLUDES = auto()
|
|
106
107
|
AS = auto()
|
|
107
108
|
COMMENT = auto()
|
|
108
109
|
NEWLINE = auto()
|
|
109
110
|
EOF = auto()
|
|
111
|
+
# Super-functions for .cssl-pl payload files (v3.8.0)
|
|
112
|
+
SUPER_FUNC = auto() # #$run(), #$exec(), #$printl() - pre-execution hooks
|
|
110
113
|
|
|
111
114
|
|
|
112
115
|
KEYWORDS = {
|
|
113
116
|
# Service structure
|
|
114
|
-
'service-init', 'service-run', 'service-include', 'struct', 'define', 'main',
|
|
117
|
+
'service-init', 'service-run', 'service-include', 'struct', 'define', 'main', 'class', 'new', 'this',
|
|
115
118
|
# Control flow
|
|
116
119
|
'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
|
|
117
120
|
'switch', 'case', 'default', 'break', 'continue', 'return',
|
|
@@ -126,7 +129,7 @@ KEYWORDS = {
|
|
|
126
129
|
'package', 'package-includes', 'exec', 'as', 'global',
|
|
127
130
|
# CSSL Type Keywords
|
|
128
131
|
'int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
129
|
-
'list', 'dictionary', 'dict', # Python-like types
|
|
132
|
+
'list', 'dictionary', 'dict', 'instance', 'map', # Python-like types
|
|
130
133
|
'dynamic', # No type declaration (slow but flexible)
|
|
131
134
|
'undefined', # Function errors ignored
|
|
132
135
|
'open', # Accept any parameter type
|
|
@@ -154,7 +157,7 @@ TYPE_LITERALS = {'list', 'dict'}
|
|
|
154
157
|
# Generic type keywords that use <T> syntax
|
|
155
158
|
TYPE_GENERICS = {
|
|
156
159
|
'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo',
|
|
157
|
-
'vector', 'stack', 'array', 'openquote', 'list', 'dictionary'
|
|
160
|
+
'vector', 'stack', 'array', 'openquote', 'list', 'dictionary', 'map'
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
# Functions that accept type parameters: FuncName<type>(args)
|
|
@@ -211,9 +214,14 @@ class CSSLLexer:
|
|
|
211
214
|
|
|
212
215
|
char = self.source[self.pos]
|
|
213
216
|
|
|
214
|
-
# Comments
|
|
217
|
+
# Super-functions (#$) or Comments (# and // style)
|
|
215
218
|
if char == '#':
|
|
216
|
-
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()
|
|
217
225
|
elif char == '/' and self._peek(1) == '/':
|
|
218
226
|
# C-style // comment - NEW
|
|
219
227
|
self._skip_comment()
|
|
@@ -445,6 +453,31 @@ class CSSLLexer:
|
|
|
445
453
|
else:
|
|
446
454
|
self._add_token(TokenType.IDENTIFIER, value)
|
|
447
455
|
|
|
456
|
+
def _read_super_function(self):
|
|
457
|
+
"""Read #$<name>(...) super-function call for .cssl-pl payloads.
|
|
458
|
+
|
|
459
|
+
Super-functions are pre-execution hooks that run when a payload is loaded.
|
|
460
|
+
Valid super-functions: #$run(), #$exec(), #$printl()
|
|
461
|
+
|
|
462
|
+
Syntax:
|
|
463
|
+
#$run(initFunction); // Call a function at load time
|
|
464
|
+
#$exec(setup()); // Execute expression at load time
|
|
465
|
+
#$printl("Payload loaded"); // Print at load time
|
|
466
|
+
"""
|
|
467
|
+
start = self.pos
|
|
468
|
+
self._advance() # skip '#'
|
|
469
|
+
self._advance() # skip '$'
|
|
470
|
+
|
|
471
|
+
# Read the super-function name (run, exec, printl, etc.)
|
|
472
|
+
name_start = self.pos
|
|
473
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
474
|
+
self._advance()
|
|
475
|
+
func_name = self.source[name_start:self.pos]
|
|
476
|
+
|
|
477
|
+
# Store as #$<name> token value
|
|
478
|
+
value = f'#${func_name}'
|
|
479
|
+
self._add_token(TokenType.SUPER_FUNC, value)
|
|
480
|
+
|
|
448
481
|
def _read_self_ref(self):
|
|
449
482
|
"""Read s@<name> or s@<name>.<member>... self-reference"""
|
|
450
483
|
start = self.pos
|
|
@@ -679,7 +712,7 @@ class CSSLParser:
|
|
|
679
712
|
def _is_type_keyword(self, value: str) -> bool:
|
|
680
713
|
"""Check if a keyword is a type declaration"""
|
|
681
714
|
return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
682
|
-
'list', 'dictionary', 'dict',
|
|
715
|
+
'list', 'dictionary', 'dict', 'instance', 'map',
|
|
683
716
|
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
|
|
684
717
|
|
|
685
718
|
def _looks_like_function_declaration(self) -> bool:
|
|
@@ -898,7 +931,7 @@ class CSSLParser:
|
|
|
898
931
|
type_keywords = {'int', 'string', 'float', 'bool', 'dynamic', 'void',
|
|
899
932
|
'stack', 'vector', 'datastruct', 'dataspace', 'shuffled',
|
|
900
933
|
'iterator', 'combo', 'array', 'openquote', 'json',
|
|
901
|
-
'list', 'dictionary', 'dict'}
|
|
934
|
+
'list', 'dictionary', 'dict', 'instance', 'map'}
|
|
902
935
|
if type_name not in type_keywords:
|
|
903
936
|
return False
|
|
904
937
|
|
|
@@ -927,11 +960,13 @@ class CSSLParser:
|
|
|
927
960
|
# Get type name
|
|
928
961
|
type_name = self._advance().value # Consume type keyword
|
|
929
962
|
|
|
930
|
-
# Check for generic type <T>
|
|
963
|
+
# Check for generic type <T> or instance<"name">
|
|
931
964
|
element_type = None
|
|
932
965
|
if self._match(TokenType.COMPARE_LT):
|
|
933
|
-
#
|
|
934
|
-
if
|
|
966
|
+
# For instance<"name">, element_type can be a string literal
|
|
967
|
+
if type_name == 'instance' and self._check(TokenType.STRING):
|
|
968
|
+
element_type = self._advance().value
|
|
969
|
+
elif self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
935
970
|
element_type = self._advance().value
|
|
936
971
|
self._expect(TokenType.COMPARE_GT)
|
|
937
972
|
|
|
@@ -947,6 +982,14 @@ class CSSLParser:
|
|
|
947
982
|
|
|
948
983
|
self._match(TokenType.SEMICOLON)
|
|
949
984
|
|
|
985
|
+
# For instance<"name">, create a special node type
|
|
986
|
+
if type_name == 'instance':
|
|
987
|
+
return ASTNode('instance_declaration', value={
|
|
988
|
+
'instance_name': element_type,
|
|
989
|
+
'name': var_name,
|
|
990
|
+
'value': value
|
|
991
|
+
})
|
|
992
|
+
|
|
950
993
|
return ASTNode('typed_declaration', value={
|
|
951
994
|
'type': type_name,
|
|
952
995
|
'element_type': element_type,
|
|
@@ -961,6 +1004,8 @@ class CSSLParser:
|
|
|
961
1004
|
while not self._is_at_end():
|
|
962
1005
|
if self._match_keyword('struct'):
|
|
963
1006
|
root.children.append(self._parse_struct())
|
|
1007
|
+
elif self._match_keyword('class'):
|
|
1008
|
+
root.children.append(self._parse_class())
|
|
964
1009
|
elif self._match_keyword('define'):
|
|
965
1010
|
root.children.append(self._parse_define())
|
|
966
1011
|
# Check for C-style typed function declarations
|
|
@@ -1233,6 +1278,58 @@ class CSSLParser:
|
|
|
1233
1278
|
self._expect(TokenType.BLOCK_END)
|
|
1234
1279
|
return node
|
|
1235
1280
|
|
|
1281
|
+
def _parse_class(self) -> ASTNode:
|
|
1282
|
+
"""Parse class declaration with members and methods.
|
|
1283
|
+
|
|
1284
|
+
Syntax:
|
|
1285
|
+
class ClassName {
|
|
1286
|
+
string name;
|
|
1287
|
+
int age;
|
|
1288
|
+
|
|
1289
|
+
void ClassName(string n) { this->name = n; }
|
|
1290
|
+
|
|
1291
|
+
void sayHello() {
|
|
1292
|
+
printl("Hello " + this->name);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
"""
|
|
1296
|
+
class_name = self._advance().value
|
|
1297
|
+
|
|
1298
|
+
node = ASTNode('class', value={'name': class_name}, children=[])
|
|
1299
|
+
self._expect(TokenType.BLOCK_START)
|
|
1300
|
+
|
|
1301
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
1302
|
+
# Check for typed function (method) declaration
|
|
1303
|
+
if self._looks_like_function_declaration():
|
|
1304
|
+
method = self._parse_typed_function()
|
|
1305
|
+
method_info = method.value
|
|
1306
|
+
method_name = method_info.get('name')
|
|
1307
|
+
|
|
1308
|
+
# Mark constructor (same name as class or __init__)
|
|
1309
|
+
if method_name == class_name or method_name == '__init__':
|
|
1310
|
+
method.value['is_constructor'] = True
|
|
1311
|
+
|
|
1312
|
+
node.children.append(method)
|
|
1313
|
+
|
|
1314
|
+
# Check for typed member variable declaration
|
|
1315
|
+
elif self._looks_like_typed_variable():
|
|
1316
|
+
member = self._parse_typed_variable()
|
|
1317
|
+
if member:
|
|
1318
|
+
# Mark as class member
|
|
1319
|
+
member.value['is_member'] = True
|
|
1320
|
+
node.children.append(member)
|
|
1321
|
+
|
|
1322
|
+
# Check for define-style method
|
|
1323
|
+
elif self._match_keyword('define'):
|
|
1324
|
+
method = self._parse_define()
|
|
1325
|
+
node.children.append(method)
|
|
1326
|
+
|
|
1327
|
+
else:
|
|
1328
|
+
self._advance()
|
|
1329
|
+
|
|
1330
|
+
self._expect(TokenType.BLOCK_END)
|
|
1331
|
+
return node
|
|
1332
|
+
|
|
1236
1333
|
def _parse_define(self) -> ASTNode:
|
|
1237
1334
|
name = self._advance().value
|
|
1238
1335
|
params = []
|
|
@@ -1312,9 +1409,13 @@ class CSSLParser:
|
|
|
1312
1409
|
elif self._looks_like_function_declaration():
|
|
1313
1410
|
# Nested typed function (e.g., void Level2() { ... })
|
|
1314
1411
|
return self._parse_typed_function()
|
|
1412
|
+
elif self._check(TokenType.SUPER_FUNC):
|
|
1413
|
+
# Super-function for .cssl-pl payload files
|
|
1414
|
+
return self._parse_super_function()
|
|
1315
1415
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1316
1416
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
1317
|
-
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF)
|
|
1417
|
+
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
1418
|
+
(self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new'))):
|
|
1318
1419
|
return self._parse_expression_statement()
|
|
1319
1420
|
else:
|
|
1320
1421
|
self._advance()
|
|
@@ -1528,23 +1629,42 @@ class CSSLParser:
|
|
|
1528
1629
|
return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
|
|
1529
1630
|
|
|
1530
1631
|
def _parse_python_style_for(self) -> ASTNode:
|
|
1531
|
-
"""Parse Python-style for loop: for (i in range(...)) { }
|
|
1632
|
+
"""Parse Python-style for loop: for (i in range(...)) { } or for (item in collection) { }
|
|
1532
1633
|
|
|
1533
1634
|
Supports:
|
|
1534
1635
|
for (i in range(n)) { } - 0 to n-1
|
|
1535
1636
|
for (i in range(start, end)) { } - start to end-1
|
|
1536
1637
|
for (i in range(start, end, step)) { }
|
|
1638
|
+
for (item in collection) { } - iterate over list/vector
|
|
1639
|
+
for (item in @global_collection) { } - iterate over global
|
|
1537
1640
|
"""
|
|
1538
1641
|
var_name = self._advance().value
|
|
1539
1642
|
self._expect(TokenType.KEYWORD) # 'in'
|
|
1540
1643
|
|
|
1541
|
-
#
|
|
1644
|
+
# Check if this is range() or collection iteration
|
|
1645
|
+
is_range = False
|
|
1542
1646
|
if self._check(TokenType.KEYWORD) and self._peek().value == 'range':
|
|
1543
1647
|
self._advance() # consume 'range' keyword
|
|
1648
|
+
is_range = True
|
|
1544
1649
|
elif self._check(TokenType.IDENTIFIER) and self._peek().value == 'range':
|
|
1545
1650
|
self._advance() # consume 'range' identifier
|
|
1546
|
-
|
|
1547
|
-
|
|
1651
|
+
is_range = True
|
|
1652
|
+
|
|
1653
|
+
# If not range, parse as collection iteration
|
|
1654
|
+
if not is_range:
|
|
1655
|
+
iterable = self._parse_expression()
|
|
1656
|
+
self._expect(TokenType.PAREN_END)
|
|
1657
|
+
|
|
1658
|
+
node = ASTNode('foreach', value={'var': var_name, 'iterable': iterable}, children=[])
|
|
1659
|
+
self._expect(TokenType.BLOCK_START)
|
|
1660
|
+
|
|
1661
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
1662
|
+
stmt = self._parse_statement()
|
|
1663
|
+
if stmt:
|
|
1664
|
+
node.children.append(stmt)
|
|
1665
|
+
|
|
1666
|
+
self._expect(TokenType.BLOCK_END)
|
|
1667
|
+
return node
|
|
1548
1668
|
|
|
1549
1669
|
self._expect(TokenType.PAREN_START)
|
|
1550
1670
|
first_arg = self._parse_expression()
|
|
@@ -1656,11 +1776,56 @@ class CSSLParser:
|
|
|
1656
1776
|
return node
|
|
1657
1777
|
|
|
1658
1778
|
def _parse_return(self) -> ASTNode:
|
|
1659
|
-
|
|
1779
|
+
"""Parse return statement, supporting multiple values for shuffled functions.
|
|
1780
|
+
|
|
1781
|
+
Syntax:
|
|
1782
|
+
return; // Return None
|
|
1783
|
+
return value; // Return single value
|
|
1784
|
+
return a, b, c; // Return multiple values (for shuffled)
|
|
1785
|
+
"""
|
|
1786
|
+
values = []
|
|
1660
1787
|
if not self._check(TokenType.SEMICOLON) and not self._check(TokenType.BLOCK_END):
|
|
1661
|
-
|
|
1788
|
+
values.append(self._parse_expression())
|
|
1789
|
+
|
|
1790
|
+
# Check for comma-separated return values (shuffled return)
|
|
1791
|
+
while self._check(TokenType.COMMA):
|
|
1792
|
+
self._advance() # consume comma
|
|
1793
|
+
values.append(self._parse_expression())
|
|
1794
|
+
|
|
1662
1795
|
self._match(TokenType.SEMICOLON)
|
|
1663
|
-
|
|
1796
|
+
|
|
1797
|
+
if len(values) == 0:
|
|
1798
|
+
return ASTNode('return', value=None)
|
|
1799
|
+
elif len(values) == 1:
|
|
1800
|
+
return ASTNode('return', value=values[0])
|
|
1801
|
+
else:
|
|
1802
|
+
# Multiple return values - create tuple return
|
|
1803
|
+
return ASTNode('return', value={'multiple': True, 'values': values})
|
|
1804
|
+
|
|
1805
|
+
def _parse_super_function(self) -> ASTNode:
|
|
1806
|
+
"""Parse super-function for .cssl-pl payload files.
|
|
1807
|
+
|
|
1808
|
+
Syntax:
|
|
1809
|
+
#$run(initFunction); // Call function at load time
|
|
1810
|
+
#$exec(setup()); // Execute expression at load time
|
|
1811
|
+
#$printl("Payload loaded"); // Print at load time
|
|
1812
|
+
|
|
1813
|
+
These are pre-execution hooks that run when payload() loads the file.
|
|
1814
|
+
"""
|
|
1815
|
+
token = self._advance() # Get the SUPER_FUNC token
|
|
1816
|
+
super_name = token.value # e.g., "#$run", "#$exec", "#$printl"
|
|
1817
|
+
|
|
1818
|
+
# Parse the arguments
|
|
1819
|
+
self._expect(TokenType.PAREN_START)
|
|
1820
|
+
args = []
|
|
1821
|
+
if not self._check(TokenType.PAREN_END):
|
|
1822
|
+
args.append(self._parse_expression())
|
|
1823
|
+
while self._match(TokenType.COMMA):
|
|
1824
|
+
args.append(self._parse_expression())
|
|
1825
|
+
self._expect(TokenType.PAREN_END)
|
|
1826
|
+
self._match(TokenType.SEMICOLON)
|
|
1827
|
+
|
|
1828
|
+
return ASTNode('super_func', value={'name': super_name, 'args': args})
|
|
1664
1829
|
|
|
1665
1830
|
def _parse_try(self) -> ASTNode:
|
|
1666
1831
|
node = ASTNode('try', children=[])
|
|
@@ -1747,6 +1912,25 @@ class CSSLParser:
|
|
|
1747
1912
|
def _parse_expression_statement(self) -> Optional[ASTNode]:
|
|
1748
1913
|
expr = self._parse_expression()
|
|
1749
1914
|
|
|
1915
|
+
# === TUPLE UNPACKING: a, b, c = shuffled_func() ===
|
|
1916
|
+
# Check if we have comma-separated identifiers before =
|
|
1917
|
+
if expr.type == 'identifier' and self._check(TokenType.COMMA):
|
|
1918
|
+
targets = [expr]
|
|
1919
|
+
while self._match(TokenType.COMMA):
|
|
1920
|
+
next_expr = self._parse_expression()
|
|
1921
|
+
if next_expr.type == 'identifier':
|
|
1922
|
+
targets.append(next_expr)
|
|
1923
|
+
else:
|
|
1924
|
+
# Not a simple identifier list, this is something else
|
|
1925
|
+
# Restore and fall through to normal parsing
|
|
1926
|
+
break
|
|
1927
|
+
|
|
1928
|
+
# Check if followed by =
|
|
1929
|
+
if self._match(TokenType.EQUALS):
|
|
1930
|
+
value = self._parse_expression()
|
|
1931
|
+
self._match(TokenType.SEMICOLON)
|
|
1932
|
+
return ASTNode('tuple_assignment', value={'targets': targets, 'value': value})
|
|
1933
|
+
|
|
1750
1934
|
# === BASIC INJECTION: <== (replace target with source) ===
|
|
1751
1935
|
if self._match(TokenType.INJECT_LEFT):
|
|
1752
1936
|
# Check if this is a createcmd injection with a code block
|
|
@@ -1782,14 +1966,14 @@ class CSSLParser:
|
|
|
1782
1966
|
if self._check(TokenType.BRACKET_START):
|
|
1783
1967
|
# Peek ahead to see if this is an index [n] or a filter [type::helper=...]
|
|
1784
1968
|
# Only consume if it's a simple number index
|
|
1785
|
-
saved_pos = self.
|
|
1969
|
+
saved_pos = self.pos
|
|
1786
1970
|
self._advance() # consume [
|
|
1787
1971
|
if self._check(TokenType.NUMBER):
|
|
1788
1972
|
remove_index = int(self._advance().value)
|
|
1789
1973
|
self._expect(TokenType.BRACKET_END)
|
|
1790
1974
|
else:
|
|
1791
1975
|
# Not a number - restore position for filter parsing
|
|
1792
|
-
self.
|
|
1976
|
+
self.pos = saved_pos
|
|
1793
1977
|
|
|
1794
1978
|
filter_info = self._parse_injection_filter()
|
|
1795
1979
|
source = self._parse_expression()
|
|
@@ -1824,14 +2008,14 @@ class CSSLParser:
|
|
|
1824
2008
|
remove_index = None
|
|
1825
2009
|
if self._check(TokenType.BRACKET_START):
|
|
1826
2010
|
# Peek ahead to see if this is an index [n] or something else
|
|
1827
|
-
saved_pos = self.
|
|
2011
|
+
saved_pos = self.pos
|
|
1828
2012
|
self._advance() # consume [
|
|
1829
2013
|
if self._check(TokenType.NUMBER):
|
|
1830
2014
|
remove_index = int(self._advance().value)
|
|
1831
2015
|
self._expect(TokenType.BRACKET_END)
|
|
1832
2016
|
else:
|
|
1833
2017
|
# Not a number - restore position
|
|
1834
|
-
self.
|
|
2018
|
+
self.pos = saved_pos
|
|
1835
2019
|
|
|
1836
2020
|
if self._check(TokenType.BLOCK_START):
|
|
1837
2021
|
code_block = self._parse_action_block()
|
|
@@ -1987,19 +2171,75 @@ class CSSLParser:
|
|
|
1987
2171
|
return self._parse_primary()
|
|
1988
2172
|
|
|
1989
2173
|
def _parse_primary(self) -> ASTNode:
|
|
2174
|
+
# Handle 'this->' member access
|
|
2175
|
+
if self._check(TokenType.KEYWORD) and self._current().value == 'this':
|
|
2176
|
+
self._advance() # consume 'this'
|
|
2177
|
+
if self._match(TokenType.FLOW_RIGHT): # ->
|
|
2178
|
+
member = self._advance().value
|
|
2179
|
+
node = ASTNode('this_access', value={'member': member})
|
|
2180
|
+
# Continue to check for calls, member access, indexing
|
|
2181
|
+
while True:
|
|
2182
|
+
if self._match(TokenType.PAREN_START):
|
|
2183
|
+
# Method call: this->method()
|
|
2184
|
+
args, kwargs = self._parse_call_arguments()
|
|
2185
|
+
self._expect(TokenType.PAREN_END)
|
|
2186
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2187
|
+
elif self._match(TokenType.DOT):
|
|
2188
|
+
# Chained access: this->obj.method
|
|
2189
|
+
member = self._advance().value
|
|
2190
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
2191
|
+
elif self._match(TokenType.BRACKET_START):
|
|
2192
|
+
# Index access: this->arr[0]
|
|
2193
|
+
index = self._parse_expression()
|
|
2194
|
+
self._expect(TokenType.BRACKET_END)
|
|
2195
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
2196
|
+
elif self._match(TokenType.FLOW_RIGHT):
|
|
2197
|
+
# Chained this->a->b style access
|
|
2198
|
+
member = self._advance().value
|
|
2199
|
+
node = ASTNode('this_access', value={'member': member, 'object': node})
|
|
2200
|
+
else:
|
|
2201
|
+
break
|
|
2202
|
+
return node
|
|
2203
|
+
else:
|
|
2204
|
+
# Just 'this' keyword alone - return as identifier for now
|
|
2205
|
+
return ASTNode('identifier', value='this')
|
|
2206
|
+
|
|
2207
|
+
# Handle 'new ClassName(args)' instantiation
|
|
2208
|
+
if self._check(TokenType.KEYWORD) and self._current().value == 'new':
|
|
2209
|
+
self._advance() # consume 'new'
|
|
2210
|
+
class_name = self._advance().value # get class name
|
|
2211
|
+
args = []
|
|
2212
|
+
kwargs = {}
|
|
2213
|
+
if self._match(TokenType.PAREN_START):
|
|
2214
|
+
args, kwargs = self._parse_call_arguments()
|
|
2215
|
+
self._expect(TokenType.PAREN_END)
|
|
2216
|
+
node = ASTNode('new', value={'class': class_name, 'args': args, 'kwargs': kwargs})
|
|
2217
|
+
# Continue to check for member access, calls on the new object
|
|
2218
|
+
while True:
|
|
2219
|
+
if self._match(TokenType.DOT):
|
|
2220
|
+
member = self._advance().value
|
|
2221
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
2222
|
+
if self._match(TokenType.PAREN_START):
|
|
2223
|
+
args, kwargs = self._parse_call_arguments()
|
|
2224
|
+
self._expect(TokenType.PAREN_END)
|
|
2225
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2226
|
+
elif self._match(TokenType.BRACKET_START):
|
|
2227
|
+
index = self._parse_expression()
|
|
2228
|
+
self._expect(TokenType.BRACKET_END)
|
|
2229
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
2230
|
+
else:
|
|
2231
|
+
break
|
|
2232
|
+
return node
|
|
2233
|
+
|
|
1990
2234
|
if self._match(TokenType.AT):
|
|
1991
2235
|
node = self._parse_module_reference()
|
|
1992
2236
|
# Continue to check for calls, indexing, member access on module refs
|
|
1993
2237
|
while True:
|
|
1994
2238
|
if self._match(TokenType.PAREN_START):
|
|
1995
|
-
# Function call on module ref: @Module.method()
|
|
1996
|
-
args =
|
|
1997
|
-
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
1998
|
-
args.append(self._parse_expression())
|
|
1999
|
-
if not self._check(TokenType.PAREN_END):
|
|
2000
|
-
self._expect(TokenType.COMMA)
|
|
2239
|
+
# Function call on module ref: @Module.method() - with kwargs support
|
|
2240
|
+
args, kwargs = self._parse_call_arguments()
|
|
2001
2241
|
self._expect(TokenType.PAREN_END)
|
|
2002
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2242
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2003
2243
|
elif self._match(TokenType.DOT):
|
|
2004
2244
|
# Member access: @Module.property
|
|
2005
2245
|
member = self._advance().value
|
|
@@ -2017,31 +2257,23 @@ class CSSLParser:
|
|
|
2017
2257
|
# s@<name> self-reference to global struct
|
|
2018
2258
|
token = self._advance()
|
|
2019
2259
|
node = ASTNode('self_ref', value=token.value, line=token.line, column=token.column)
|
|
2020
|
-
# Check for function call: s@Backend.Loop.Start()
|
|
2260
|
+
# Check for function call: s@Backend.Loop.Start() - with kwargs support
|
|
2021
2261
|
if self._match(TokenType.PAREN_START):
|
|
2022
|
-
args =
|
|
2023
|
-
while not self._check(TokenType.PAREN_END):
|
|
2024
|
-
args.append(self._parse_expression())
|
|
2025
|
-
if not self._check(TokenType.PAREN_END):
|
|
2026
|
-
self._expect(TokenType.COMMA)
|
|
2262
|
+
args, kwargs = self._parse_call_arguments()
|
|
2027
2263
|
self._expect(TokenType.PAREN_END)
|
|
2028
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2264
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2029
2265
|
return node
|
|
2030
2266
|
|
|
2031
2267
|
if self._check(TokenType.GLOBAL_REF):
|
|
2032
2268
|
# r@<name> global variable reference/declaration
|
|
2033
2269
|
token = self._advance()
|
|
2034
2270
|
node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
|
|
2035
|
-
# Check for member access, calls, indexing
|
|
2271
|
+
# Check for member access, calls, indexing - with kwargs support
|
|
2036
2272
|
while True:
|
|
2037
2273
|
if self._match(TokenType.PAREN_START):
|
|
2038
|
-
args =
|
|
2039
|
-
while not self._check(TokenType.PAREN_END):
|
|
2040
|
-
args.append(self._parse_expression())
|
|
2041
|
-
if not self._check(TokenType.PAREN_END):
|
|
2042
|
-
self._expect(TokenType.COMMA)
|
|
2274
|
+
args, kwargs = self._parse_call_arguments()
|
|
2043
2275
|
self._expect(TokenType.PAREN_END)
|
|
2044
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2276
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2045
2277
|
elif self._match(TokenType.DOT):
|
|
2046
2278
|
member = self._advance().value
|
|
2047
2279
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
@@ -2057,16 +2289,12 @@ class CSSLParser:
|
|
|
2057
2289
|
# $<name> shared object reference
|
|
2058
2290
|
token = self._advance()
|
|
2059
2291
|
node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
|
|
2060
|
-
# Check for member access, calls, indexing
|
|
2292
|
+
# Check for member access, calls, indexing - with kwargs support
|
|
2061
2293
|
while True:
|
|
2062
2294
|
if self._match(TokenType.PAREN_START):
|
|
2063
|
-
args =
|
|
2064
|
-
while not self._check(TokenType.PAREN_END):
|
|
2065
|
-
args.append(self._parse_expression())
|
|
2066
|
-
if not self._check(TokenType.PAREN_END):
|
|
2067
|
-
self._expect(TokenType.COMMA)
|
|
2295
|
+
args, kwargs = self._parse_call_arguments()
|
|
2068
2296
|
self._expect(TokenType.PAREN_END)
|
|
2069
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2297
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2070
2298
|
elif self._match(TokenType.DOT):
|
|
2071
2299
|
member = self._advance().value
|
|
2072
2300
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
@@ -2082,16 +2310,12 @@ class CSSLParser:
|
|
|
2082
2310
|
# %<name> captured reference (captures value at infusion registration time)
|
|
2083
2311
|
token = self._advance()
|
|
2084
2312
|
node = ASTNode('captured_ref', value=token.value, line=token.line, column=token.column)
|
|
2085
|
-
# Check for member access, calls, indexing
|
|
2313
|
+
# Check for member access, calls, indexing - with kwargs support
|
|
2086
2314
|
while True:
|
|
2087
2315
|
if self._match(TokenType.PAREN_START):
|
|
2088
|
-
args =
|
|
2089
|
-
while not self._check(TokenType.PAREN_END):
|
|
2090
|
-
args.append(self._parse_expression())
|
|
2091
|
-
if not self._check(TokenType.PAREN_END):
|
|
2092
|
-
self._expect(TokenType.COMMA)
|
|
2316
|
+
args, kwargs = self._parse_call_arguments()
|
|
2093
2317
|
self._expect(TokenType.PAREN_END)
|
|
2094
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2318
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2095
2319
|
elif self._match(TokenType.DOT):
|
|
2096
2320
|
member = self._advance().value
|
|
2097
2321
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
@@ -2160,14 +2384,10 @@ class CSSLParser:
|
|
|
2160
2384
|
member = self._advance().value
|
|
2161
2385
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
2162
2386
|
elif self._match(TokenType.PAREN_START):
|
|
2163
|
-
# Function call
|
|
2164
|
-
args =
|
|
2165
|
-
while not self._check(TokenType.PAREN_END):
|
|
2166
|
-
args.append(self._parse_expression())
|
|
2167
|
-
if not self._check(TokenType.PAREN_END):
|
|
2168
|
-
self._expect(TokenType.COMMA)
|
|
2387
|
+
# Function call - use _parse_call_arguments for kwargs support
|
|
2388
|
+
args, kwargs = self._parse_call_arguments()
|
|
2169
2389
|
self._expect(TokenType.PAREN_END)
|
|
2170
|
-
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
2390
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
2171
2391
|
elif self._match(TokenType.BRACKET_START):
|
|
2172
2392
|
# Index access
|
|
2173
2393
|
index = self._parse_expression()
|
|
@@ -2191,7 +2411,7 @@ class CSSLParser:
|
|
|
2191
2411
|
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
2192
2412
|
# Check for named argument: identifier = expression
|
|
2193
2413
|
if self._check(TokenType.IDENTIFIER):
|
|
2194
|
-
saved_pos = self.
|
|
2414
|
+
saved_pos = self.pos # Save token position
|
|
2195
2415
|
name_token = self._advance()
|
|
2196
2416
|
|
|
2197
2417
|
if self._check(TokenType.EQUALS):
|
|
@@ -2201,7 +2421,7 @@ class CSSLParser:
|
|
|
2201
2421
|
kwargs[name_token.value] = value
|
|
2202
2422
|
else:
|
|
2203
2423
|
# Not named, restore and parse as expression
|
|
2204
|
-
self.
|
|
2424
|
+
self.pos = saved_pos # Restore token position
|
|
2205
2425
|
args.append(self._parse_expression())
|
|
2206
2426
|
else:
|
|
2207
2427
|
args.append(self._parse_expression())
|
|
@@ -2220,17 +2440,75 @@ class CSSLParser:
|
|
|
2220
2440
|
namespace_member = self._advance().value
|
|
2221
2441
|
name = f"{name}::{namespace_member}"
|
|
2222
2442
|
|
|
2223
|
-
# Check for
|
|
2443
|
+
# Check for instance<"name"> syntax - gets/creates shared instance
|
|
2444
|
+
if name == 'instance' and self._check(TokenType.COMPARE_LT):
|
|
2445
|
+
self._advance() # consume <
|
|
2446
|
+
# Expect string literal for instance name
|
|
2447
|
+
if self._check(TokenType.STRING):
|
|
2448
|
+
instance_name = self._advance().value
|
|
2449
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2450
|
+
instance_name = self._advance().value
|
|
2451
|
+
else:
|
|
2452
|
+
raise CSSLParserError("Expected instance name (string or identifier)", self._current_line())
|
|
2453
|
+
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2454
|
+
return ASTNode('instance_ref', value=instance_name)
|
|
2455
|
+
|
|
2456
|
+
# Check for type generic instantiation: stack<string>, vector<int>, map<string, int>, etc.
|
|
2224
2457
|
# This creates a new instance of the type with the specified element type
|
|
2225
2458
|
if name in TYPE_GENERICS and self._check(TokenType.COMPARE_LT):
|
|
2226
2459
|
self._advance() # consume <
|
|
2227
2460
|
element_type = 'dynamic'
|
|
2461
|
+
value_type = None # For map<K, V>
|
|
2462
|
+
|
|
2228
2463
|
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2229
2464
|
element_type = self._advance().value
|
|
2465
|
+
|
|
2466
|
+
# Check for second type parameter (map<K, V>)
|
|
2467
|
+
if name == 'map' and self._check(TokenType.COMMA):
|
|
2468
|
+
self._advance() # consume ,
|
|
2469
|
+
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2470
|
+
value_type = self._advance().value
|
|
2471
|
+
else:
|
|
2472
|
+
value_type = 'dynamic'
|
|
2473
|
+
|
|
2230
2474
|
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2475
|
+
|
|
2476
|
+
# Check for inline initialization: map<K,V>{"key": "value", ...}
|
|
2477
|
+
init_values = None
|
|
2478
|
+
if self._check(TokenType.BLOCK_START):
|
|
2479
|
+
self._advance() # consume {
|
|
2480
|
+
init_values = {}
|
|
2481
|
+
|
|
2482
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2483
|
+
# Parse key
|
|
2484
|
+
if self._check(TokenType.STRING):
|
|
2485
|
+
key = self._advance().value
|
|
2486
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2487
|
+
key = self._advance().value
|
|
2488
|
+
else:
|
|
2489
|
+
key = str(self._parse_expression().value) if hasattr(self._parse_expression(), 'value') else 'key'
|
|
2490
|
+
|
|
2491
|
+
# Expect : or =
|
|
2492
|
+
if self._check(TokenType.COLON):
|
|
2493
|
+
self._advance()
|
|
2494
|
+
elif self._check(TokenType.EQUALS):
|
|
2495
|
+
self._advance()
|
|
2496
|
+
|
|
2497
|
+
# Parse value
|
|
2498
|
+
value = self._parse_expression()
|
|
2499
|
+
init_values[key] = value
|
|
2500
|
+
|
|
2501
|
+
# Optional comma
|
|
2502
|
+
if self._check(TokenType.COMMA):
|
|
2503
|
+
self._advance()
|
|
2504
|
+
|
|
2505
|
+
self._expect(TokenType.BLOCK_END) # consume }
|
|
2506
|
+
|
|
2231
2507
|
return ASTNode('type_instantiation', value={
|
|
2232
2508
|
'type': name,
|
|
2233
|
-
'element_type': element_type
|
|
2509
|
+
'element_type': element_type,
|
|
2510
|
+
'value_type': value_type,
|
|
2511
|
+
'init_values': init_values
|
|
2234
2512
|
})
|
|
2235
2513
|
|
|
2236
2514
|
# Check for type-parameterized function call: OpenFind<string>(0)
|
|
@@ -2288,7 +2566,7 @@ class CSSLParser:
|
|
|
2288
2566
|
return False
|
|
2289
2567
|
|
|
2290
2568
|
# Save position for lookahead
|
|
2291
|
-
saved_pos = self.
|
|
2569
|
+
saved_pos = self.pos
|
|
2292
2570
|
|
|
2293
2571
|
# Check if it looks like key = value pattern
|
|
2294
2572
|
is_object = False
|
|
@@ -2299,7 +2577,7 @@ class CSSLParser:
|
|
|
2299
2577
|
is_object = True
|
|
2300
2578
|
|
|
2301
2579
|
# Restore position
|
|
2302
|
-
self.
|
|
2580
|
+
self.pos = saved_pos
|
|
2303
2581
|
return is_object
|
|
2304
2582
|
|
|
2305
2583
|
def _parse_action_block_expression(self) -> ASTNode:
|