IncludeCPP 3.7.25__py3-none-any.whl → 3.8.8__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 +26 -6
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +312 -1
- includecpp/core/cssl/cssl_builtins.py +343 -0
- includecpp/core/cssl/cssl_parser.py +422 -9
- includecpp/core/cssl/cssl_runtime.py +496 -17
- includecpp/core/cssl/cssl_types.py +58 -9
- includecpp/vscode/cssl/extension.js +5 -5
- includecpp/vscode/cssl/package.json +1 -1
- includecpp/vscode/cssl/snippets/cssl.snippets.json +126 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +232 -2
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/METADATA +55 -224
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/RECORD +17 -17
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/WHEEL +0 -0
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/entry_points.txt +0 -0
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.7.25.dist-info → includecpp-3.8.8.dist-info}/top_level.txt +0 -0
|
@@ -114,7 +114,7 @@ class TokenType(Enum):
|
|
|
114
114
|
|
|
115
115
|
KEYWORDS = {
|
|
116
116
|
# Service structure
|
|
117
|
-
'service-init', 'service-run', 'service-include', 'struct', 'define', 'main', 'class', 'new', 'this',
|
|
117
|
+
'service-init', 'service-run', 'service-include', 'struct', 'define', 'main', 'class', 'constr', 'extends', 'overwrites', 'new', 'this', 'super',
|
|
118
118
|
# Control flow
|
|
119
119
|
'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
|
|
120
120
|
'switch', 'case', 'default', 'break', 'continue', 'return',
|
|
@@ -716,7 +716,7 @@ class CSSLParser:
|
|
|
716
716
|
def _is_type_keyword(self, value: str) -> bool:
|
|
717
717
|
"""Check if a keyword is a type declaration"""
|
|
718
718
|
return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
719
|
-
'list', 'dictionary', 'dict', 'instance', 'map',
|
|
719
|
+
'list', 'dictionary', 'dict', 'instance', 'map', 'openquote', 'parameter',
|
|
720
720
|
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
|
|
721
721
|
|
|
722
722
|
def _looks_like_function_declaration(self) -> bool:
|
|
@@ -727,14 +727,17 @@ class CSSLParser:
|
|
|
727
727
|
- undefined int funcName(...)
|
|
728
728
|
- vector<string> funcName(...)
|
|
729
729
|
- undefined void funcName(...)
|
|
730
|
+
- private super virtual meta FuncName(...) <- modifiers without return type
|
|
730
731
|
"""
|
|
731
732
|
saved_pos = self.pos
|
|
733
|
+
has_modifiers = False
|
|
732
734
|
|
|
733
735
|
# Skip modifiers (undefined, open, meta, super, closed, private, virtual)
|
|
734
736
|
while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
|
|
735
737
|
self._advance()
|
|
738
|
+
has_modifiers = True
|
|
736
739
|
|
|
737
|
-
# Check for type keyword
|
|
740
|
+
# Check for type keyword (optional if modifiers present)
|
|
738
741
|
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
739
742
|
self._advance()
|
|
740
743
|
|
|
@@ -756,6 +759,14 @@ class CSSLParser:
|
|
|
756
759
|
self.pos = saved_pos
|
|
757
760
|
return is_func
|
|
758
761
|
|
|
762
|
+
# If we have modifiers and the next token is an identifier followed by (
|
|
763
|
+
# This handles: private super virtual meta FuncName()
|
|
764
|
+
elif has_modifiers and self._check(TokenType.IDENTIFIER):
|
|
765
|
+
self._advance()
|
|
766
|
+
is_func = self._check(TokenType.PAREN_START)
|
|
767
|
+
self.pos = saved_pos
|
|
768
|
+
return is_func
|
|
769
|
+
|
|
759
770
|
self.pos = saved_pos
|
|
760
771
|
return False
|
|
761
772
|
|
|
@@ -1014,7 +1025,11 @@ class CSSLParser:
|
|
|
1014
1025
|
return result
|
|
1015
1026
|
|
|
1016
1027
|
def _parse_typed_variable(self) -> Optional[ASTNode]:
|
|
1017
|
-
"""Parse a typed variable declaration: type varName; or type<T> varName = value;
|
|
1028
|
+
"""Parse a typed variable declaration: type varName; or type<T> *varName = value;
|
|
1029
|
+
|
|
1030
|
+
The * prefix indicates a non-nullable variable (can never be None/null).
|
|
1031
|
+
Example: vector<dynamic> *MyVector - can never contain None values.
|
|
1032
|
+
"""
|
|
1018
1033
|
# Get type name
|
|
1019
1034
|
type_name = self._advance().value # Consume type keyword
|
|
1020
1035
|
|
|
@@ -1028,6 +1043,11 @@ class CSSLParser:
|
|
|
1028
1043
|
element_type = self._advance().value
|
|
1029
1044
|
self._expect(TokenType.COMPARE_GT)
|
|
1030
1045
|
|
|
1046
|
+
# Check for * prefix (non-nullable indicator)
|
|
1047
|
+
non_null = False
|
|
1048
|
+
if self._match(TokenType.MULTIPLY):
|
|
1049
|
+
non_null = True
|
|
1050
|
+
|
|
1031
1051
|
# Get variable name
|
|
1032
1052
|
if not self._check(TokenType.IDENTIFIER):
|
|
1033
1053
|
return None
|
|
@@ -1045,14 +1065,16 @@ class CSSLParser:
|
|
|
1045
1065
|
return ASTNode('instance_declaration', value={
|
|
1046
1066
|
'instance_name': element_type,
|
|
1047
1067
|
'name': var_name,
|
|
1048
|
-
'value': value
|
|
1068
|
+
'value': value,
|
|
1069
|
+
'non_null': non_null
|
|
1049
1070
|
})
|
|
1050
1071
|
|
|
1051
1072
|
return ASTNode('typed_declaration', value={
|
|
1052
1073
|
'type': type_name,
|
|
1053
1074
|
'element_type': element_type,
|
|
1054
1075
|
'name': var_name,
|
|
1055
|
-
'value': value
|
|
1076
|
+
'value': value,
|
|
1077
|
+
'non_null': non_null
|
|
1056
1078
|
})
|
|
1057
1079
|
|
|
1058
1080
|
def parse_program(self) -> ASTNode:
|
|
@@ -1350,10 +1372,79 @@ class CSSLParser:
|
|
|
1350
1372
|
printl("Hello " + this->name);
|
|
1351
1373
|
}
|
|
1352
1374
|
}
|
|
1375
|
+
|
|
1376
|
+
Non-null class (all methods return non-null):
|
|
1377
|
+
class *MyClass { ... }
|
|
1353
1378
|
"""
|
|
1379
|
+
# Check for * prefix (non-null class - all methods return non-null)
|
|
1380
|
+
non_null = False
|
|
1381
|
+
if self._match(TokenType.MULTIPLY):
|
|
1382
|
+
non_null = True
|
|
1383
|
+
|
|
1354
1384
|
class_name = self._advance().value
|
|
1355
1385
|
|
|
1356
|
-
|
|
1386
|
+
# Check for class-level constructor parameters: class MyClass (int x, string y) { ... }
|
|
1387
|
+
class_params = []
|
|
1388
|
+
if self._match(TokenType.PAREN_START):
|
|
1389
|
+
class_params = self._parse_parameter_list()
|
|
1390
|
+
self._expect(TokenType.PAREN_END)
|
|
1391
|
+
|
|
1392
|
+
# Check for inheritance and overwrites:
|
|
1393
|
+
# class Child : extends Parent { ... }
|
|
1394
|
+
# class Child : extends $PythonObject { ... }
|
|
1395
|
+
# class Child : extends Parent : overwrites Parent { ... }
|
|
1396
|
+
# class Child : extends Parent (param1, param2) { ... } <- constructor args for parent
|
|
1397
|
+
extends_class = None
|
|
1398
|
+
extends_is_python = False
|
|
1399
|
+
extends_args = []
|
|
1400
|
+
overwrites_class = None
|
|
1401
|
+
overwrites_is_python = False
|
|
1402
|
+
|
|
1403
|
+
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
1404
|
+
# Parse extends and/or overwrites (can be chained with : or ::)
|
|
1405
|
+
while True:
|
|
1406
|
+
if self._match_keyword('extends'):
|
|
1407
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1408
|
+
extends_class = self._advance().value
|
|
1409
|
+
elif self._check(TokenType.SHARED_REF):
|
|
1410
|
+
extends_class = self._advance().value
|
|
1411
|
+
extends_is_python = True
|
|
1412
|
+
else:
|
|
1413
|
+
raise CSSLSyntaxError("Expected parent class name after 'extends'")
|
|
1414
|
+
# Check for constructor arguments: extends Parent (arg1, arg2)
|
|
1415
|
+
if self._match(TokenType.PAREN_START):
|
|
1416
|
+
while not self._check(TokenType.PAREN_END):
|
|
1417
|
+
arg = self._parse_expression()
|
|
1418
|
+
extends_args.append(arg)
|
|
1419
|
+
self._match(TokenType.COMMA)
|
|
1420
|
+
self._expect(TokenType.PAREN_END)
|
|
1421
|
+
elif self._match_keyword('overwrites'):
|
|
1422
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1423
|
+
overwrites_class = self._advance().value
|
|
1424
|
+
elif self._check(TokenType.SHARED_REF):
|
|
1425
|
+
overwrites_class = self._advance().value
|
|
1426
|
+
overwrites_is_python = True
|
|
1427
|
+
else:
|
|
1428
|
+
raise CSSLSyntaxError("Expected class name after 'overwrites'")
|
|
1429
|
+
# Skip optional () after class name
|
|
1430
|
+
if self._match(TokenType.PAREN_START):
|
|
1431
|
+
self._expect(TokenType.PAREN_END)
|
|
1432
|
+
else:
|
|
1433
|
+
raise CSSLSyntaxError("Expected 'extends' or 'overwrites' after ':' or '::' in class declaration")
|
|
1434
|
+
# Check for another : or :: for chaining
|
|
1435
|
+
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
1436
|
+
break
|
|
1437
|
+
|
|
1438
|
+
node = ASTNode('class', value={
|
|
1439
|
+
'name': class_name,
|
|
1440
|
+
'non_null': non_null,
|
|
1441
|
+
'class_params': class_params,
|
|
1442
|
+
'extends': extends_class,
|
|
1443
|
+
'extends_is_python': extends_is_python,
|
|
1444
|
+
'extends_args': extends_args,
|
|
1445
|
+
'overwrites': overwrites_class,
|
|
1446
|
+
'overwrites_is_python': overwrites_is_python
|
|
1447
|
+
}, children=[])
|
|
1357
1448
|
self._expect(TokenType.BLOCK_START)
|
|
1358
1449
|
|
|
1359
1450
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
@@ -1382,14 +1473,265 @@ class CSSLParser:
|
|
|
1382
1473
|
method = self._parse_define()
|
|
1383
1474
|
node.children.append(method)
|
|
1384
1475
|
|
|
1476
|
+
# Check for constr keyword (constructor declaration)
|
|
1477
|
+
# Syntax: constr ConstructorName() { ... }
|
|
1478
|
+
# or: constr ConstructorName() : extends Parent::ConstructorName { ... }
|
|
1479
|
+
elif self._match_keyword('constr'):
|
|
1480
|
+
constructor = self._parse_constructor(class_name)
|
|
1481
|
+
node.children.append(constructor)
|
|
1482
|
+
|
|
1385
1483
|
else:
|
|
1386
1484
|
self._advance()
|
|
1387
1485
|
|
|
1388
1486
|
self._expect(TokenType.BLOCK_END)
|
|
1389
1487
|
return node
|
|
1390
1488
|
|
|
1489
|
+
def _parse_constructor(self, class_name: str) -> ASTNode:
|
|
1490
|
+
"""Parse constructor declaration inside a class.
|
|
1491
|
+
|
|
1492
|
+
Syntax:
|
|
1493
|
+
constr ConstructorName() { ... }
|
|
1494
|
+
constr ConstructorName() : extends ParentClass::ConstructorName { ... }
|
|
1495
|
+
constr ConstructorName() : extends ParentClass::ConstructorName : overwrites ParentClass::ConstructorName { ... }
|
|
1496
|
+
|
|
1497
|
+
Multiple constructors are allowed and executed in order.
|
|
1498
|
+
Constructors can access parent constructor via super().
|
|
1499
|
+
"""
|
|
1500
|
+
# Get constructor name
|
|
1501
|
+
if not self._check(TokenType.IDENTIFIER):
|
|
1502
|
+
raise CSSLSyntaxError("Expected constructor name after 'constr'")
|
|
1503
|
+
constr_name = self._advance().value
|
|
1504
|
+
|
|
1505
|
+
# Parse method-level extends/overwrites with :: syntax
|
|
1506
|
+
# constr Name() :: extends Parent::Name :: overwrites Parent::Name { ... }
|
|
1507
|
+
extends_target = None
|
|
1508
|
+
extends_class_ref = None
|
|
1509
|
+
extends_method_ref = None
|
|
1510
|
+
overwrites_target = None
|
|
1511
|
+
overwrites_class_ref = None
|
|
1512
|
+
overwrites_method_ref = None
|
|
1513
|
+
|
|
1514
|
+
# Parse parameters
|
|
1515
|
+
params = []
|
|
1516
|
+
if self._match(TokenType.PAREN_START):
|
|
1517
|
+
params = self._parse_parameter_list()
|
|
1518
|
+
self._expect(TokenType.PAREN_END)
|
|
1519
|
+
|
|
1520
|
+
# Check for method-level extends/overwrites with :: or :
|
|
1521
|
+
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
1522
|
+
while True:
|
|
1523
|
+
if self._match_keyword('extends'):
|
|
1524
|
+
# Parse Parent::method or just method
|
|
1525
|
+
extends_target = self._parse_qualified_method_ref()
|
|
1526
|
+
if '::' in extends_target:
|
|
1527
|
+
parts = extends_target.split('::')
|
|
1528
|
+
extends_class_ref = parts[0]
|
|
1529
|
+
extends_method_ref = parts[1]
|
|
1530
|
+
else:
|
|
1531
|
+
extends_method_ref = extends_target
|
|
1532
|
+
elif self._match_keyword('overwrites'):
|
|
1533
|
+
# Parse Parent::method or just method
|
|
1534
|
+
overwrites_target = self._parse_qualified_method_ref()
|
|
1535
|
+
if '::' in overwrites_target:
|
|
1536
|
+
parts = overwrites_target.split('::')
|
|
1537
|
+
overwrites_class_ref = parts[0]
|
|
1538
|
+
overwrites_method_ref = parts[1]
|
|
1539
|
+
else:
|
|
1540
|
+
overwrites_method_ref = overwrites_target
|
|
1541
|
+
else:
|
|
1542
|
+
break
|
|
1543
|
+
# Check for another :: or : for chaining
|
|
1544
|
+
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
1545
|
+
break
|
|
1546
|
+
|
|
1547
|
+
# Parse constructor body
|
|
1548
|
+
self._expect(TokenType.BLOCK_START)
|
|
1549
|
+
body = []
|
|
1550
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
1551
|
+
stmt = self._parse_statement()
|
|
1552
|
+
if stmt:
|
|
1553
|
+
body.append(stmt)
|
|
1554
|
+
self._expect(TokenType.BLOCK_END)
|
|
1555
|
+
|
|
1556
|
+
return ASTNode('constructor', value={
|
|
1557
|
+
'name': constr_name,
|
|
1558
|
+
'class_name': class_name,
|
|
1559
|
+
'params': params,
|
|
1560
|
+
'is_constructor': True,
|
|
1561
|
+
'extends_target': extends_target,
|
|
1562
|
+
'extends_class': extends_class_ref,
|
|
1563
|
+
'extends_method': extends_method_ref,
|
|
1564
|
+
'overwrites_target': overwrites_target,
|
|
1565
|
+
'overwrites_class': overwrites_class_ref,
|
|
1566
|
+
'overwrites_method': overwrites_method_ref
|
|
1567
|
+
}, children=body)
|
|
1568
|
+
|
|
1569
|
+
def _parse_qualified_method_ref(self) -> str:
|
|
1570
|
+
"""Parse a qualified method reference like 'ParentClass::methodName' or just 'methodName'.
|
|
1571
|
+
|
|
1572
|
+
Returns the qualified name as a string (e.g., 'Parent::init' or just 'init').
|
|
1573
|
+
"""
|
|
1574
|
+
# Check for $PythonObject
|
|
1575
|
+
if self._check(TokenType.SHARED_REF):
|
|
1576
|
+
class_ref = self._advance().value # Gets the name without $
|
|
1577
|
+
class_ref = f'${class_ref}'
|
|
1578
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
1579
|
+
class_ref = self._advance().value
|
|
1580
|
+
else:
|
|
1581
|
+
raise CSSLSyntaxError("Expected class or method name in extends/overwrites")
|
|
1582
|
+
|
|
1583
|
+
# Check for :: to get method part
|
|
1584
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1585
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1586
|
+
method_ref = self._advance().value
|
|
1587
|
+
return f'{class_ref}::{method_ref}'
|
|
1588
|
+
else:
|
|
1589
|
+
raise CSSLSyntaxError("Expected method name after '::'")
|
|
1590
|
+
|
|
1591
|
+
# Just method name, no class qualifier
|
|
1592
|
+
return class_ref
|
|
1593
|
+
|
|
1594
|
+
def _parse_parameter_list(self) -> list:
|
|
1595
|
+
"""Parse a list of parameters (without the surrounding parentheses).
|
|
1596
|
+
|
|
1597
|
+
Returns a list of parameter definitions, each can be:
|
|
1598
|
+
- Simple string name: "paramName"
|
|
1599
|
+
- Dict with type info: {'name': 'paramName', 'type': 'string', 'ref': True, ...}
|
|
1600
|
+
"""
|
|
1601
|
+
params = []
|
|
1602
|
+
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
1603
|
+
param_info = {}
|
|
1604
|
+
|
|
1605
|
+
# Handle 'open' keyword for open parameters
|
|
1606
|
+
if self._match_keyword('open'):
|
|
1607
|
+
param_info['open'] = True
|
|
1608
|
+
|
|
1609
|
+
# Handle type annotations (e.g., string, int, dynamic, etc.)
|
|
1610
|
+
if self._check(TokenType.KEYWORD):
|
|
1611
|
+
param_info['type'] = self._advance().value
|
|
1612
|
+
|
|
1613
|
+
# Handle reference operator &
|
|
1614
|
+
if self._match(TokenType.AMPERSAND):
|
|
1615
|
+
param_info['ref'] = True
|
|
1616
|
+
|
|
1617
|
+
# Handle * prefix for non-null parameters
|
|
1618
|
+
if self._match(TokenType.MULTIPLY):
|
|
1619
|
+
param_info['non_null'] = True
|
|
1620
|
+
|
|
1621
|
+
# Get parameter name
|
|
1622
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1623
|
+
param_name = self._advance().value
|
|
1624
|
+
if param_info:
|
|
1625
|
+
params.append({'name': param_name, **param_info})
|
|
1626
|
+
else:
|
|
1627
|
+
params.append(param_name)
|
|
1628
|
+
self._match(TokenType.COMMA)
|
|
1629
|
+
elif self._check(TokenType.KEYWORD):
|
|
1630
|
+
# Parameter name could be a keyword like 'Params'
|
|
1631
|
+
param_name = self._advance().value
|
|
1632
|
+
if param_info:
|
|
1633
|
+
params.append({'name': param_name, **param_info})
|
|
1634
|
+
else:
|
|
1635
|
+
params.append(param_name)
|
|
1636
|
+
self._match(TokenType.COMMA)
|
|
1637
|
+
else:
|
|
1638
|
+
break
|
|
1639
|
+
|
|
1640
|
+
return params
|
|
1641
|
+
|
|
1391
1642
|
def _parse_define(self) -> ASTNode:
|
|
1643
|
+
"""Parse define function declaration.
|
|
1644
|
+
|
|
1645
|
+
Syntax:
|
|
1646
|
+
define MyFunc(args) { }
|
|
1647
|
+
define *MyFunc(args) { } // Non-null: must never return None
|
|
1648
|
+
define MyFunc : extends OtherFunc() { } // Inherit local vars
|
|
1649
|
+
define MyFunc : overwrites OtherFunc() { } // Replace OtherFunc
|
|
1650
|
+
define MyFunc :: extends Parent::Method :: overwrites Parent::Method() { } // Method-level inheritance
|
|
1651
|
+
"""
|
|
1652
|
+
# Check for * prefix (non-null function - must return non-null)
|
|
1653
|
+
non_null = False
|
|
1654
|
+
if self._match(TokenType.MULTIPLY):
|
|
1655
|
+
non_null = True
|
|
1656
|
+
|
|
1392
1657
|
name = self._advance().value
|
|
1658
|
+
|
|
1659
|
+
# Check for extends/overwrites: define func : extends/overwrites target() { }
|
|
1660
|
+
# Also supports method-level :: syntax: define func :: extends Parent::method
|
|
1661
|
+
extends_func = None
|
|
1662
|
+
overwrites_func = None
|
|
1663
|
+
extends_is_python = False
|
|
1664
|
+
overwrites_is_python = False
|
|
1665
|
+
extends_class_ref = None
|
|
1666
|
+
extends_method_ref = None
|
|
1667
|
+
overwrites_class_ref = None
|
|
1668
|
+
overwrites_method_ref = None
|
|
1669
|
+
|
|
1670
|
+
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
1671
|
+
# Parse extends and/or overwrites (supports :: method-level syntax)
|
|
1672
|
+
while True:
|
|
1673
|
+
if self._match_keyword('extends'):
|
|
1674
|
+
# Check for qualified reference: Parent::method
|
|
1675
|
+
if self._check(TokenType.SHARED_REF):
|
|
1676
|
+
extends_is_python = True
|
|
1677
|
+
extends_func = self._advance().value
|
|
1678
|
+
# Check for ::method
|
|
1679
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1680
|
+
extends_class_ref = f'${extends_func}'
|
|
1681
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1682
|
+
extends_method_ref = self._advance().value
|
|
1683
|
+
else:
|
|
1684
|
+
raise CSSLSyntaxError("Expected method name after '::'")
|
|
1685
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
1686
|
+
first_part = self._advance().value
|
|
1687
|
+
# Check for ::method (qualified reference)
|
|
1688
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1689
|
+
extends_class_ref = first_part
|
|
1690
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1691
|
+
extends_method_ref = self._advance().value
|
|
1692
|
+
else:
|
|
1693
|
+
raise CSSLSyntaxError("Expected method name after '::'")
|
|
1694
|
+
else:
|
|
1695
|
+
extends_func = first_part
|
|
1696
|
+
else:
|
|
1697
|
+
raise CSSLSyntaxError("Expected function name after 'extends'")
|
|
1698
|
+
# Skip optional () after function/method name
|
|
1699
|
+
if self._match(TokenType.PAREN_START):
|
|
1700
|
+
self._expect(TokenType.PAREN_END)
|
|
1701
|
+
elif self._match_keyword('overwrites'):
|
|
1702
|
+
# Check for qualified reference: Parent::method
|
|
1703
|
+
if self._check(TokenType.SHARED_REF):
|
|
1704
|
+
overwrites_is_python = True
|
|
1705
|
+
overwrites_func = self._advance().value
|
|
1706
|
+
# Check for ::method
|
|
1707
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1708
|
+
overwrites_class_ref = f'${overwrites_func}'
|
|
1709
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1710
|
+
overwrites_method_ref = self._advance().value
|
|
1711
|
+
else:
|
|
1712
|
+
raise CSSLSyntaxError("Expected method name after '::'")
|
|
1713
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
1714
|
+
first_part = self._advance().value
|
|
1715
|
+
# Check for ::method (qualified reference)
|
|
1716
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1717
|
+
overwrites_class_ref = first_part
|
|
1718
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1719
|
+
overwrites_method_ref = self._advance().value
|
|
1720
|
+
else:
|
|
1721
|
+
raise CSSLSyntaxError("Expected method name after '::'")
|
|
1722
|
+
else:
|
|
1723
|
+
overwrites_func = first_part
|
|
1724
|
+
else:
|
|
1725
|
+
raise CSSLSyntaxError("Expected function name after 'overwrites'")
|
|
1726
|
+
# Skip optional () after function/method name
|
|
1727
|
+
if self._match(TokenType.PAREN_START):
|
|
1728
|
+
self._expect(TokenType.PAREN_END)
|
|
1729
|
+
else:
|
|
1730
|
+
break
|
|
1731
|
+
# Check for another :: or : for chaining extends/overwrites
|
|
1732
|
+
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
1733
|
+
break
|
|
1734
|
+
|
|
1393
1735
|
params = []
|
|
1394
1736
|
|
|
1395
1737
|
if self._match(TokenType.PAREN_START):
|
|
@@ -1404,6 +1746,9 @@ class CSSLParser:
|
|
|
1404
1746
|
# Handle reference operator &
|
|
1405
1747
|
if self._match(TokenType.AMPERSAND):
|
|
1406
1748
|
param_info['ref'] = True
|
|
1749
|
+
# Handle * prefix for non-null parameters
|
|
1750
|
+
if self._match(TokenType.MULTIPLY):
|
|
1751
|
+
param_info['non_null'] = True
|
|
1407
1752
|
# Get parameter name
|
|
1408
1753
|
if self._check(TokenType.IDENTIFIER):
|
|
1409
1754
|
param_name = self._advance().value
|
|
@@ -1424,7 +1769,20 @@ class CSSLParser:
|
|
|
1424
1769
|
break
|
|
1425
1770
|
self._expect(TokenType.PAREN_END)
|
|
1426
1771
|
|
|
1427
|
-
node = ASTNode('function', value={
|
|
1772
|
+
node = ASTNode('function', value={
|
|
1773
|
+
'name': name,
|
|
1774
|
+
'params': params,
|
|
1775
|
+
'non_null': non_null,
|
|
1776
|
+
'extends': extends_func,
|
|
1777
|
+
'extends_is_python': extends_is_python,
|
|
1778
|
+
'overwrites': overwrites_func,
|
|
1779
|
+
'overwrites_is_python': overwrites_is_python,
|
|
1780
|
+
# Method-level inheritance (Parent::method syntax)
|
|
1781
|
+
'extends_class': extends_class_ref,
|
|
1782
|
+
'extends_method': extends_method_ref,
|
|
1783
|
+
'overwrites_class': overwrites_class_ref,
|
|
1784
|
+
'overwrites_method': overwrites_method_ref
|
|
1785
|
+
}, children=[])
|
|
1428
1786
|
self._expect(TokenType.BLOCK_START)
|
|
1429
1787
|
|
|
1430
1788
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
@@ -1470,6 +1828,11 @@ class CSSLParser:
|
|
|
1470
1828
|
elif self._check(TokenType.SUPER_FUNC):
|
|
1471
1829
|
# Super-function for .cssl-pl payload files
|
|
1472
1830
|
return self._parse_super_function()
|
|
1831
|
+
elif (self._check(TokenType.KEYWORD) and self._current().value == 'super' and
|
|
1832
|
+
(self._peek(1).type == TokenType.PAREN_START or
|
|
1833
|
+
self._peek(1).type == TokenType.DOUBLE_COLON)):
|
|
1834
|
+
# super() or super::method() call - calls parent constructor/method
|
|
1835
|
+
return self._parse_super_call()
|
|
1473
1836
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1474
1837
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
1475
1838
|
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
@@ -1480,6 +1843,43 @@ class CSSLParser:
|
|
|
1480
1843
|
self._advance()
|
|
1481
1844
|
return None
|
|
1482
1845
|
|
|
1846
|
+
def _parse_super_call(self) -> ASTNode:
|
|
1847
|
+
"""Parse super() call to invoke parent constructor or method.
|
|
1848
|
+
|
|
1849
|
+
Syntax:
|
|
1850
|
+
super() - Call parent constructor with no args
|
|
1851
|
+
super(arg1, arg2) - Call parent constructor with args
|
|
1852
|
+
super::method() - Call specific parent method
|
|
1853
|
+
super::method(args) - Call specific parent method with args
|
|
1854
|
+
|
|
1855
|
+
Used inside constructors (constr) and methods to call parent implementations.
|
|
1856
|
+
"""
|
|
1857
|
+
# Consume 'super' keyword
|
|
1858
|
+
self._advance()
|
|
1859
|
+
|
|
1860
|
+
# Check for ::method syntax
|
|
1861
|
+
target_method = None
|
|
1862
|
+
if self._match(TokenType.DOUBLE_COLON):
|
|
1863
|
+
if not self._check(TokenType.IDENTIFIER):
|
|
1864
|
+
raise CSSLSyntaxError("Expected method name after 'super::'")
|
|
1865
|
+
target_method = self._advance().value
|
|
1866
|
+
|
|
1867
|
+
# Parse arguments
|
|
1868
|
+
args = []
|
|
1869
|
+
self._expect(TokenType.PAREN_START)
|
|
1870
|
+
while not self._check(TokenType.PAREN_END):
|
|
1871
|
+
arg = self._parse_expression()
|
|
1872
|
+
args.append(arg)
|
|
1873
|
+
if not self._match(TokenType.COMMA):
|
|
1874
|
+
break
|
|
1875
|
+
self._expect(TokenType.PAREN_END)
|
|
1876
|
+
self._match(TokenType.SEMICOLON)
|
|
1877
|
+
|
|
1878
|
+
return ASTNode('super_call', value={
|
|
1879
|
+
'method': target_method, # None for constructor, method name for specific method
|
|
1880
|
+
'args': args
|
|
1881
|
+
})
|
|
1882
|
+
|
|
1483
1883
|
def _parse_if(self) -> ASTNode:
|
|
1484
1884
|
"""Parse if statement with support for else if AND elif syntax."""
|
|
1485
1885
|
self._expect(TokenType.PAREN_START)
|
|
@@ -2599,12 +2999,24 @@ class CSSLParser:
|
|
|
2599
2999
|
'init_values': init_values
|
|
2600
3000
|
})
|
|
2601
3001
|
|
|
2602
|
-
# Check for type-parameterized function call: OpenFind<string>(0)
|
|
3002
|
+
# Check for type-parameterized function call: OpenFind<string>(0) or OpenFind<dynamic, "name">
|
|
2603
3003
|
if name in TYPE_PARAM_FUNCTIONS and self._check(TokenType.COMPARE_LT):
|
|
2604
3004
|
self._advance() # consume <
|
|
2605
3005
|
type_param = 'dynamic'
|
|
3006
|
+
param_name = None # Optional: named parameter search
|
|
3007
|
+
|
|
2606
3008
|
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
2607
3009
|
type_param = self._advance().value
|
|
3010
|
+
|
|
3011
|
+
# Check for second parameter: OpenFind<type, "name">
|
|
3012
|
+
if self._check(TokenType.COMMA):
|
|
3013
|
+
self._advance() # consume comma
|
|
3014
|
+
# Expect a string literal for the parameter name
|
|
3015
|
+
if self._check(TokenType.STRING):
|
|
3016
|
+
param_name = self._advance().value
|
|
3017
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
3018
|
+
param_name = self._advance().value
|
|
3019
|
+
|
|
2608
3020
|
self._expect(TokenType.COMPARE_GT) # consume >
|
|
2609
3021
|
|
|
2610
3022
|
# Must be followed by ()
|
|
@@ -2621,6 +3033,7 @@ class CSSLParser:
|
|
|
2621
3033
|
return ASTNode('typed_call', value={
|
|
2622
3034
|
'name': name,
|
|
2623
3035
|
'type_param': type_param,
|
|
3036
|
+
'param_name': param_name, # Named parameter for OpenFind
|
|
2624
3037
|
'args': args
|
|
2625
3038
|
})
|
|
2626
3039
|
|