IncludeCPP 4.0.2__py3-none-any.whl → 4.2.2__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 +175 -0
- includecpp/DOCUMENTATION.md +593 -0
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +4 -1
- includecpp/cli/commands.py +698 -84
- includecpp/core/ai_integration.py +46 -13
- includecpp/core/cpp_api_extensions.pyi +350 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +186 -5
- includecpp/core/cssl/cssl_builtins.py +101 -4
- includecpp/core/cssl/cssl_languages.py +1757 -0
- includecpp/core/cssl/cssl_parser.py +429 -98
- includecpp/core/cssl/cssl_runtime.py +666 -51
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +172 -1
- includecpp/core/cssl_bridge.py +194 -8
- includecpp/core/cssl_bridge.pyi +148 -10
- includecpp/generator/parser.cpp +121 -4
- includecpp/generator/parser.h +6 -0
- includecpp/vscode/cssl/package.json +43 -1
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +140 -17
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/METADATA +101 -1
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/RECORD +26 -22
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/WHEEL +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/top_level.txt +0 -0
|
@@ -113,6 +113,8 @@ class TokenType(Enum):
|
|
|
113
113
|
# Append operator for constructor/function extension
|
|
114
114
|
PLUS_PLUS = auto() # ++ for constructor/function append (keeps old + adds new)
|
|
115
115
|
MINUS_MINUS = auto() # -- for potential future use
|
|
116
|
+
# Multi-language support (v4.1.0)
|
|
117
|
+
LANG_INSTANCE_REF = auto() # cpp$InstanceName, py$Object - cross-language instance access
|
|
116
118
|
|
|
117
119
|
|
|
118
120
|
KEYWORDS = {
|
|
@@ -155,6 +157,8 @@ KEYWORDS = {
|
|
|
155
157
|
'static', # Static method/function
|
|
156
158
|
# CSSL Include Keywords
|
|
157
159
|
'include', 'get',
|
|
160
|
+
# Multi-language support (v4.1.0)
|
|
161
|
+
'supports', 'libinclude',
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
# Function modifiers that can appear in any order before function name
|
|
@@ -182,6 +186,12 @@ INJECTION_HELPERS = {
|
|
|
182
186
|
'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
|
|
183
187
|
}
|
|
184
188
|
|
|
189
|
+
# Language identifiers for multi-language support (v4.1.0)
|
|
190
|
+
# Used in lang$instance patterns like cpp$MyClass, py$Object
|
|
191
|
+
LANGUAGE_IDS = {
|
|
192
|
+
'cpp', 'py', 'python', 'java', 'csharp', 'js', 'javascript'
|
|
193
|
+
}
|
|
194
|
+
|
|
185
195
|
|
|
186
196
|
@dataclass
|
|
187
197
|
class Token:
|
|
@@ -457,6 +467,25 @@ class CSSLLexer:
|
|
|
457
467
|
self._advance()
|
|
458
468
|
value = self.source[start:self.pos]
|
|
459
469
|
|
|
470
|
+
# Check for language$instance pattern (v4.1.0)
|
|
471
|
+
# e.g., cpp$MyClass, py$Object, java$Service
|
|
472
|
+
if value.lower() in LANGUAGE_IDS and self.pos < len(self.source) and self.source[self.pos] == '$':
|
|
473
|
+
lang_id = value
|
|
474
|
+
self._advance() # skip '$'
|
|
475
|
+
# Read instance name
|
|
476
|
+
instance_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
|
+
instance_name = self.source[instance_start:self.pos]
|
|
480
|
+
if instance_name:
|
|
481
|
+
self._add_token(TokenType.LANG_INSTANCE_REF, {'lang': lang_id, 'instance': instance_name})
|
|
482
|
+
return
|
|
483
|
+
# If no instance name, revert and treat as normal identifier
|
|
484
|
+
self.pos = start
|
|
485
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
486
|
+
self._advance()
|
|
487
|
+
value = self.source[start:self.pos]
|
|
488
|
+
|
|
460
489
|
if value in ('True', 'true'):
|
|
461
490
|
self._add_token(TokenType.BOOLEAN, True)
|
|
462
491
|
elif value in ('False', 'false'):
|
|
@@ -672,10 +701,11 @@ class ASTNode:
|
|
|
672
701
|
class CSSLParser:
|
|
673
702
|
"""Parses CSSL tokens into an Abstract Syntax Tree."""
|
|
674
703
|
|
|
675
|
-
def __init__(self, tokens: List[Token], source_lines: List[str] = None):
|
|
704
|
+
def __init__(self, tokens: List[Token], source_lines: List[str] = None, source: str = None):
|
|
676
705
|
self.tokens = [t for t in tokens if t.type != TokenType.NEWLINE]
|
|
677
706
|
self.pos = 0
|
|
678
707
|
self.source_lines = source_lines or []
|
|
708
|
+
self.source = source or '\n'.join(self.source_lines) # v4.2.0: Full source for raw extraction
|
|
679
709
|
|
|
680
710
|
def get_source_line(self, line_num: int) -> str:
|
|
681
711
|
"""Get a specific source line for error reporting"""
|
|
@@ -1350,13 +1380,7 @@ class CSSLParser:
|
|
|
1350
1380
|
# Wrap in global_assignment to mark as global variable (same as 'global' keyword)
|
|
1351
1381
|
global_stmt = ASTNode('global_assignment', value=stmt)
|
|
1352
1382
|
root.children.append(global_stmt)
|
|
1353
|
-
#
|
|
1354
|
-
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1355
|
-
self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
|
|
1356
|
-
self._check(TokenType.KEYWORD)):
|
|
1357
|
-
stmt = self._parse_expression_statement()
|
|
1358
|
-
if stmt:
|
|
1359
|
-
root.children.append(stmt)
|
|
1383
|
+
# Control flow keywords must be checked BEFORE generic KEYWORD handling
|
|
1360
1384
|
elif self._match_keyword('if'):
|
|
1361
1385
|
root.children.append(self._parse_if())
|
|
1362
1386
|
elif self._match_keyword('while'):
|
|
@@ -1365,6 +1389,14 @@ class CSSLParser:
|
|
|
1365
1389
|
root.children.append(self._parse_for())
|
|
1366
1390
|
elif self._match_keyword('foreach'):
|
|
1367
1391
|
root.children.append(self._parse_foreach())
|
|
1392
|
+
# Handle statements - keywords like 'instance', 'list', 'map' can be variable names
|
|
1393
|
+
# v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements (js$GameData.score = 1337)
|
|
1394
|
+
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
1395
|
+
self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
|
|
1396
|
+
self._check(TokenType.KEYWORD) or self._check(TokenType.LANG_INSTANCE_REF)):
|
|
1397
|
+
stmt = self._parse_expression_statement()
|
|
1398
|
+
if stmt:
|
|
1399
|
+
root.children.append(stmt)
|
|
1368
1400
|
# Skip comments and newlines
|
|
1369
1401
|
elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
|
|
1370
1402
|
self._advance()
|
|
@@ -1627,15 +1659,22 @@ class CSSLParser:
|
|
|
1627
1659
|
# class Child : extends Parent (param1, param2) { ... } <- constructor args for parent
|
|
1628
1660
|
extends_class = None
|
|
1629
1661
|
extends_is_python = False
|
|
1662
|
+
extends_lang_ref = None # v4.1.0: Cross-language inheritance (cpp$ClassName)
|
|
1630
1663
|
extends_args = []
|
|
1631
1664
|
overwrites_class = None
|
|
1632
1665
|
overwrites_is_python = False
|
|
1666
|
+
supports_language = None # v4.1.0: Multi-language syntax support
|
|
1633
1667
|
|
|
1634
1668
|
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
1635
1669
|
# Parse extends and/or overwrites (can be chained with : or ::)
|
|
1636
1670
|
while True:
|
|
1637
1671
|
if self._match_keyword('extends'):
|
|
1638
|
-
|
|
1672
|
+
# v4.1.0: Check for cross-language inheritance: extends cpp$ClassName
|
|
1673
|
+
if self._check(TokenType.LANG_INSTANCE_REF):
|
|
1674
|
+
ref = self._advance().value
|
|
1675
|
+
extends_lang_ref = ref # {'lang': 'cpp', 'instance': 'ClassName'}
|
|
1676
|
+
extends_class = ref['instance']
|
|
1677
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
1639
1678
|
extends_class = self._advance().value
|
|
1640
1679
|
elif self._check(TokenType.SHARED_REF):
|
|
1641
1680
|
extends_class = self._advance().value
|
|
@@ -1660,12 +1699,29 @@ class CSSLParser:
|
|
|
1660
1699
|
# Skip optional () after class name
|
|
1661
1700
|
if self._match(TokenType.PAREN_START):
|
|
1662
1701
|
self._expect(TokenType.PAREN_END)
|
|
1702
|
+
# v4.1.0: Parse 'supports' keyword for multi-language syntax
|
|
1703
|
+
elif self._match_keyword('supports'):
|
|
1704
|
+
if self._check(TokenType.AT):
|
|
1705
|
+
self._advance() # consume @
|
|
1706
|
+
if self._check(TokenType.IDENTIFIER):
|
|
1707
|
+
supports_language = '@' + self._advance().value
|
|
1708
|
+
else:
|
|
1709
|
+
raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
|
|
1710
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
1711
|
+
supports_language = self._advance().value
|
|
1712
|
+
else:
|
|
1713
|
+
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
1663
1714
|
else:
|
|
1664
|
-
raise CSSLSyntaxError("Expected 'extends' or '
|
|
1715
|
+
raise CSSLSyntaxError("Expected 'extends', 'overwrites', or 'supports' after ':' or '::' in class declaration")
|
|
1665
1716
|
# Check for another : or :: for chaining
|
|
1666
1717
|
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
1667
1718
|
break
|
|
1668
1719
|
|
|
1720
|
+
# v4.2.0: If supports_language is set, capture raw body for runtime transformation
|
|
1721
|
+
raw_body = None
|
|
1722
|
+
if supports_language:
|
|
1723
|
+
raw_body = self._extract_raw_block_body()
|
|
1724
|
+
|
|
1669
1725
|
node = ASTNode('class', value={
|
|
1670
1726
|
'name': class_name,
|
|
1671
1727
|
'is_global': is_global,
|
|
@@ -1673,10 +1729,29 @@ class CSSLParser:
|
|
|
1673
1729
|
'class_params': class_params,
|
|
1674
1730
|
'extends': extends_class,
|
|
1675
1731
|
'extends_is_python': extends_is_python,
|
|
1732
|
+
'extends_lang_ref': extends_lang_ref, # v4.1.0
|
|
1676
1733
|
'extends_args': extends_args,
|
|
1677
1734
|
'overwrites': overwrites_class,
|
|
1678
|
-
'overwrites_is_python': overwrites_is_python
|
|
1735
|
+
'overwrites_is_python': overwrites_is_python,
|
|
1736
|
+
'supports_language': supports_language, # v4.1.0
|
|
1737
|
+
'raw_body': raw_body # v4.2.0: Raw body for language transformation
|
|
1679
1738
|
}, children=[])
|
|
1739
|
+
|
|
1740
|
+
# v4.2.0: If we have raw_body for language transformation, skip regular parsing
|
|
1741
|
+
if raw_body is not None:
|
|
1742
|
+
# Skip the block entirely - runtime will transform and parse
|
|
1743
|
+
self._expect(TokenType.BLOCK_START)
|
|
1744
|
+
brace_count = 1
|
|
1745
|
+
while brace_count > 0 and not self._is_at_end():
|
|
1746
|
+
if self._check(TokenType.BLOCK_START):
|
|
1747
|
+
brace_count += 1
|
|
1748
|
+
elif self._check(TokenType.BLOCK_END):
|
|
1749
|
+
brace_count -= 1
|
|
1750
|
+
if brace_count > 0:
|
|
1751
|
+
self._advance()
|
|
1752
|
+
self._expect(TokenType.BLOCK_END)
|
|
1753
|
+
return node
|
|
1754
|
+
|
|
1680
1755
|
self._expect(TokenType.BLOCK_START)
|
|
1681
1756
|
|
|
1682
1757
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
@@ -1907,13 +1982,14 @@ class CSSLParser:
|
|
|
1907
1982
|
"""Parse define function declaration.
|
|
1908
1983
|
|
|
1909
1984
|
Syntax:
|
|
1910
|
-
define MyFunc(args) { }
|
|
1911
|
-
global define MyFunc(args) { }
|
|
1912
|
-
define @MyFunc(args) { }
|
|
1913
|
-
define *MyFunc(args) { }
|
|
1914
|
-
define MyFunc : extends OtherFunc
|
|
1915
|
-
define MyFunc : overwrites OtherFunc
|
|
1916
|
-
define MyFunc
|
|
1985
|
+
define MyFunc(args) { } // Local function
|
|
1986
|
+
global define MyFunc(args) { } // Global function
|
|
1987
|
+
define @MyFunc(args) { } // Global function (alternative)
|
|
1988
|
+
define *MyFunc(args) { } // Non-null: must never return None
|
|
1989
|
+
define MyFunc(args) : extends OtherFunc { } // Inherit local vars
|
|
1990
|
+
define MyFunc(args) : overwrites OtherFunc { } // Replace OtherFunc
|
|
1991
|
+
define MyFunc(args) : supports python { } // Multi-language syntax
|
|
1992
|
+
define MyFunc(args) :: extends Parent::Method { } // Method-level inheritance
|
|
1917
1993
|
"""
|
|
1918
1994
|
# Check for * prefix (non-null function - must return non-null)
|
|
1919
1995
|
# Also *[type] for type exclusion (must NOT return that type)
|
|
@@ -1935,8 +2011,56 @@ class CSSLParser:
|
|
|
1935
2011
|
|
|
1936
2012
|
name = self._advance().value
|
|
1937
2013
|
|
|
1938
|
-
#
|
|
1939
|
-
#
|
|
2014
|
+
# Parse parameters FIRST (before :extends/:overwrites/:supports)
|
|
2015
|
+
# Syntax: define funcName(params) : extends/overwrites/supports { }
|
|
2016
|
+
params = []
|
|
2017
|
+
|
|
2018
|
+
if self._match(TokenType.PAREN_START):
|
|
2019
|
+
while not self._check(TokenType.PAREN_END):
|
|
2020
|
+
param_info = {}
|
|
2021
|
+
# Handle 'open' keyword for open parameters
|
|
2022
|
+
if self._match_keyword('open'):
|
|
2023
|
+
param_info['open'] = True
|
|
2024
|
+
# Handle type annotations (e.g., string, int, dynamic, etc.)
|
|
2025
|
+
if self._check(TokenType.KEYWORD):
|
|
2026
|
+
param_info['type'] = self._advance().value
|
|
2027
|
+
# Handle reference operator &
|
|
2028
|
+
if self._match(TokenType.AMPERSAND):
|
|
2029
|
+
param_info['ref'] = True
|
|
2030
|
+
# Handle * prefix for non-null parameters
|
|
2031
|
+
if self._match(TokenType.MULTIPLY):
|
|
2032
|
+
param_info['non_null'] = True
|
|
2033
|
+
# Get parameter name
|
|
2034
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2035
|
+
param_name = self._advance().value
|
|
2036
|
+
# v4.2.0: Handle default parameter values (param = value)
|
|
2037
|
+
if self._match(TokenType.EQUALS):
|
|
2038
|
+
default_value = self._parse_expression()
|
|
2039
|
+
param_info['default'] = default_value
|
|
2040
|
+
if param_info:
|
|
2041
|
+
params.append({'name': param_name, **param_info})
|
|
2042
|
+
else:
|
|
2043
|
+
params.append(param_name)
|
|
2044
|
+
self._match(TokenType.COMMA)
|
|
2045
|
+
elif self._check(TokenType.KEYWORD):
|
|
2046
|
+
# Parameter name could be a keyword like 'Params'
|
|
2047
|
+
param_name = self._advance().value
|
|
2048
|
+
# v4.2.0: Handle default parameter values (param = value)
|
|
2049
|
+
if self._match(TokenType.EQUALS):
|
|
2050
|
+
default_value = self._parse_expression()
|
|
2051
|
+
param_info['default'] = default_value
|
|
2052
|
+
if param_info:
|
|
2053
|
+
params.append({'name': param_name, **param_info})
|
|
2054
|
+
else:
|
|
2055
|
+
params.append(param_name)
|
|
2056
|
+
self._match(TokenType.COMMA)
|
|
2057
|
+
else:
|
|
2058
|
+
break
|
|
2059
|
+
self._expect(TokenType.PAREN_END)
|
|
2060
|
+
|
|
2061
|
+
# Check for extends/overwrites/supports AFTER parameters
|
|
2062
|
+
# Syntax: define func(params) : extends/overwrites target { }
|
|
2063
|
+
# Also supports method-level :: syntax: define func() :: extends Parent::method
|
|
1940
2064
|
extends_func = None
|
|
1941
2065
|
overwrites_func = None
|
|
1942
2066
|
extends_is_python = False
|
|
@@ -1945,6 +2069,7 @@ class CSSLParser:
|
|
|
1945
2069
|
extends_method_ref = None
|
|
1946
2070
|
overwrites_class_ref = None
|
|
1947
2071
|
overwrites_method_ref = None
|
|
2072
|
+
supports_language = None # v4.1.0: Multi-language syntax support
|
|
1948
2073
|
|
|
1949
2074
|
if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
|
|
1950
2075
|
# Parse extends and/or overwrites (supports :: method-level syntax)
|
|
@@ -2005,49 +2130,24 @@ class CSSLParser:
|
|
|
2005
2130
|
# Skip optional () after function/method name
|
|
2006
2131
|
if self._match(TokenType.PAREN_START):
|
|
2007
2132
|
self._expect(TokenType.PAREN_END)
|
|
2133
|
+
# v4.1.0: Parse 'supports' keyword for multi-language syntax
|
|
2134
|
+
elif self._match_keyword('supports'):
|
|
2135
|
+
if self._check(TokenType.AT):
|
|
2136
|
+
self._advance() # consume @
|
|
2137
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2138
|
+
supports_language = '@' + self._advance().value
|
|
2139
|
+
else:
|
|
2140
|
+
raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
|
|
2141
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2142
|
+
supports_language = self._advance().value
|
|
2143
|
+
else:
|
|
2144
|
+
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
2008
2145
|
else:
|
|
2009
2146
|
break
|
|
2010
2147
|
# Check for another :: or : for chaining extends/overwrites
|
|
2011
2148
|
if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
|
|
2012
2149
|
break
|
|
2013
2150
|
|
|
2014
|
-
params = []
|
|
2015
|
-
|
|
2016
|
-
if self._match(TokenType.PAREN_START):
|
|
2017
|
-
while not self._check(TokenType.PAREN_END):
|
|
2018
|
-
param_info = {}
|
|
2019
|
-
# Handle 'open' keyword for open parameters
|
|
2020
|
-
if self._match_keyword('open'):
|
|
2021
|
-
param_info['open'] = True
|
|
2022
|
-
# Handle type annotations (e.g., string, int, dynamic, etc.)
|
|
2023
|
-
if self._check(TokenType.KEYWORD):
|
|
2024
|
-
param_info['type'] = self._advance().value
|
|
2025
|
-
# Handle reference operator &
|
|
2026
|
-
if self._match(TokenType.AMPERSAND):
|
|
2027
|
-
param_info['ref'] = True
|
|
2028
|
-
# Handle * prefix for non-null parameters
|
|
2029
|
-
if self._match(TokenType.MULTIPLY):
|
|
2030
|
-
param_info['non_null'] = True
|
|
2031
|
-
# Get parameter name
|
|
2032
|
-
if self._check(TokenType.IDENTIFIER):
|
|
2033
|
-
param_name = self._advance().value
|
|
2034
|
-
if param_info:
|
|
2035
|
-
params.append({'name': param_name, **param_info})
|
|
2036
|
-
else:
|
|
2037
|
-
params.append(param_name)
|
|
2038
|
-
self._match(TokenType.COMMA)
|
|
2039
|
-
elif self._check(TokenType.KEYWORD):
|
|
2040
|
-
# Parameter name could be a keyword like 'Params'
|
|
2041
|
-
param_name = self._advance().value
|
|
2042
|
-
if param_info:
|
|
2043
|
-
params.append({'name': param_name, **param_info})
|
|
2044
|
-
else:
|
|
2045
|
-
params.append(param_name)
|
|
2046
|
-
self._match(TokenType.COMMA)
|
|
2047
|
-
else:
|
|
2048
|
-
break
|
|
2049
|
-
self._expect(TokenType.PAREN_END)
|
|
2050
|
-
|
|
2051
2151
|
# New: Append mode and reference tracking for functions
|
|
2052
2152
|
# Syntax: define XYZ(int zahl) &overwrittenclass::functionyouwanttokeep ++ { ... }
|
|
2053
2153
|
append_mode = False
|
|
@@ -2074,6 +2174,37 @@ class CSSLParser:
|
|
|
2074
2174
|
if self._match(TokenType.PLUS_PLUS):
|
|
2075
2175
|
append_mode = True
|
|
2076
2176
|
|
|
2177
|
+
# v4.2.0: Allow 'supports' AFTER &Class::member reference
|
|
2178
|
+
# Syntax: define func() &$pyclass::method : supports python { }
|
|
2179
|
+
if self._match(TokenType.COLON) or self._match(TokenType.DOUBLE_COLON):
|
|
2180
|
+
if self._match_keyword('supports'):
|
|
2181
|
+
if self._check(TokenType.AT):
|
|
2182
|
+
self._advance()
|
|
2183
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2184
|
+
supports_language = '@' + self._advance().value
|
|
2185
|
+
else:
|
|
2186
|
+
raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
|
|
2187
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2188
|
+
supports_language = self._advance().value
|
|
2189
|
+
else:
|
|
2190
|
+
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
2191
|
+
|
|
2192
|
+
self._expect(TokenType.BLOCK_START)
|
|
2193
|
+
|
|
2194
|
+
# v4.2.0: Extract raw body when supports_language is set for transformation
|
|
2195
|
+
raw_body = None
|
|
2196
|
+
children = []
|
|
2197
|
+
if supports_language:
|
|
2198
|
+
raw_body = self._extract_raw_block_body()
|
|
2199
|
+
# _extract_raw_block_body positions cursor at BLOCK_END
|
|
2200
|
+
else:
|
|
2201
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2202
|
+
stmt = self._parse_statement()
|
|
2203
|
+
if stmt:
|
|
2204
|
+
children.append(stmt)
|
|
2205
|
+
|
|
2206
|
+
self._expect(TokenType.BLOCK_END)
|
|
2207
|
+
|
|
2077
2208
|
node = ASTNode('function', value={
|
|
2078
2209
|
'name': name,
|
|
2079
2210
|
'is_global': is_global,
|
|
@@ -2092,16 +2223,13 @@ class CSSLParser:
|
|
|
2092
2223
|
# New append mode fields
|
|
2093
2224
|
'append_mode': append_mode,
|
|
2094
2225
|
'append_ref_class': append_ref_class,
|
|
2095
|
-
'append_ref_member': append_ref_member
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
if stmt:
|
|
2102
|
-
node.children.append(stmt)
|
|
2226
|
+
'append_ref_member': append_ref_member,
|
|
2227
|
+
# v4.1.0: Multi-language support
|
|
2228
|
+
'supports_language': supports_language,
|
|
2229
|
+
# v4.2.0: Raw body for language transformation
|
|
2230
|
+
'raw_body': raw_body
|
|
2231
|
+
}, children=children)
|
|
2103
2232
|
|
|
2104
|
-
self._expect(TokenType.BLOCK_END)
|
|
2105
2233
|
return node
|
|
2106
2234
|
|
|
2107
2235
|
def _parse_statement(self) -> Optional[ASTNode]:
|
|
@@ -2127,6 +2255,9 @@ class CSSLParser:
|
|
|
2127
2255
|
return self._parse_try()
|
|
2128
2256
|
elif self._match_keyword('await'):
|
|
2129
2257
|
return self._parse_await()
|
|
2258
|
+
elif self._match_keyword('supports'):
|
|
2259
|
+
# v4.2.0: Standalone supports block for multi-language syntax
|
|
2260
|
+
return self._parse_supports_block()
|
|
2130
2261
|
elif self._match_keyword('define'):
|
|
2131
2262
|
# Nested define function
|
|
2132
2263
|
return self._parse_define()
|
|
@@ -2144,9 +2275,11 @@ class CSSLParser:
|
|
|
2144
2275
|
self._peek(1).type == TokenType.DOUBLE_COLON)):
|
|
2145
2276
|
# super() or super::method() call - calls parent constructor/method
|
|
2146
2277
|
return self._parse_super_call()
|
|
2278
|
+
# v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
|
|
2147
2279
|
elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
2148
2280
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
2149
2281
|
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
2282
|
+
self._check(TokenType.LANG_INSTANCE_REF) or
|
|
2150
2283
|
(self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new')) or
|
|
2151
2284
|
self._looks_like_namespace_call()):
|
|
2152
2285
|
return self._parse_expression_statement()
|
|
@@ -2352,42 +2485,40 @@ class CSSLParser:
|
|
|
2352
2485
|
|
|
2353
2486
|
Supports: i = i + 1, i++, ++i, i += 1, i -= 1
|
|
2354
2487
|
"""
|
|
2355
|
-
# Check for prefix increment/decrement: ++i or --i
|
|
2356
|
-
if self._check(TokenType.
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2488
|
+
# Check for prefix increment/decrement: ++i or --i (as single PLUS_PLUS/MINUS_MINUS token)
|
|
2489
|
+
if self._check(TokenType.PLUS_PLUS):
|
|
2490
|
+
self._advance()
|
|
2491
|
+
var_name = self._advance().value
|
|
2492
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
|
|
2493
|
+
elif self._check(TokenType.MINUS_MINUS):
|
|
2494
|
+
self._advance()
|
|
2495
|
+
var_name = self._advance().value
|
|
2496
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
|
|
2364
2497
|
|
|
2365
2498
|
# Regular variable assignment or postfix
|
|
2366
2499
|
var_name = self._advance().value
|
|
2367
2500
|
|
|
2368
|
-
# Check for postfix increment/decrement: i++ or i--
|
|
2369
|
-
if self._check(TokenType.
|
|
2501
|
+
# Check for postfix increment/decrement: i++ or i-- (as single PLUS_PLUS/MINUS_MINUS token)
|
|
2502
|
+
if self._check(TokenType.PLUS_PLUS):
|
|
2503
|
+
self._advance()
|
|
2504
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
|
|
2505
|
+
elif self._check(TokenType.MINUS_MINUS):
|
|
2506
|
+
self._advance()
|
|
2507
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
|
|
2508
|
+
# i += value
|
|
2509
|
+
elif self._check(TokenType.PLUS):
|
|
2370
2510
|
self._advance()
|
|
2371
|
-
if self._check(TokenType.
|
|
2511
|
+
if self._check(TokenType.EQUALS):
|
|
2372
2512
|
self._advance()
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
if self._check(TokenType.EQUALS):
|
|
2377
|
-
self._advance()
|
|
2378
|
-
value = self._parse_expression()
|
|
2379
|
-
return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
|
|
2513
|
+
value = self._parse_expression()
|
|
2514
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
|
|
2515
|
+
# i -= value
|
|
2380
2516
|
elif self._check(TokenType.MINUS):
|
|
2381
2517
|
self._advance()
|
|
2382
|
-
if self._check(TokenType.
|
|
2518
|
+
if self._check(TokenType.EQUALS):
|
|
2383
2519
|
self._advance()
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
# i -= value
|
|
2387
|
-
if self._check(TokenType.EQUALS):
|
|
2388
|
-
self._advance()
|
|
2389
|
-
value = self._parse_expression()
|
|
2390
|
-
return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
|
|
2520
|
+
value = self._parse_expression()
|
|
2521
|
+
return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
|
|
2391
2522
|
|
|
2392
2523
|
# Regular assignment: i = expression
|
|
2393
2524
|
if self._check(TokenType.EQUALS):
|
|
@@ -2632,6 +2763,163 @@ class CSSLParser:
|
|
|
2632
2763
|
self._match(TokenType.SEMICOLON)
|
|
2633
2764
|
return ASTNode('await', value=expr)
|
|
2634
2765
|
|
|
2766
|
+
def _parse_supports_block(self) -> ASTNode:
|
|
2767
|
+
"""Parse standalone supports block for multi-language syntax.
|
|
2768
|
+
|
|
2769
|
+
v4.2.0: Allows 'supports' to be used anywhere, not just in class/function.
|
|
2770
|
+
|
|
2771
|
+
Syntax:
|
|
2772
|
+
supports py { } // Python syntax block
|
|
2773
|
+
supports @py { } // With @ prefix
|
|
2774
|
+
supports python { } // Full language name
|
|
2775
|
+
supports cpp { } // C++ syntax block
|
|
2776
|
+
supports javascript { } // JavaScript syntax block
|
|
2777
|
+
|
|
2778
|
+
Example:
|
|
2779
|
+
supports py {
|
|
2780
|
+
for i in range(10):
|
|
2781
|
+
print(i)
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
supports cpp {
|
|
2785
|
+
std::cout << "Hello" << std::endl;
|
|
2786
|
+
int x = 42;
|
|
2787
|
+
}
|
|
2788
|
+
"""
|
|
2789
|
+
# Parse language identifier
|
|
2790
|
+
language = None
|
|
2791
|
+
if self._check(TokenType.AT):
|
|
2792
|
+
self._advance()
|
|
2793
|
+
if self._check(TokenType.IDENTIFIER):
|
|
2794
|
+
language = '@' + self._advance().value
|
|
2795
|
+
else:
|
|
2796
|
+
raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
|
|
2797
|
+
elif self._check(TokenType.IDENTIFIER):
|
|
2798
|
+
language = self._advance().value
|
|
2799
|
+
else:
|
|
2800
|
+
raise CSSLSyntaxError("Expected language identifier after 'supports'")
|
|
2801
|
+
|
|
2802
|
+
# Extract raw block source for language transformation (preserves indentation)
|
|
2803
|
+
raw_source = None
|
|
2804
|
+
if self._check(TokenType.BLOCK_START):
|
|
2805
|
+
raw_source = self._extract_raw_block_body()
|
|
2806
|
+
|
|
2807
|
+
# Skip parsing body if we have raw_source - runtime will transform and parse
|
|
2808
|
+
body = []
|
|
2809
|
+
self._expect(TokenType.BLOCK_START)
|
|
2810
|
+
if raw_source is None:
|
|
2811
|
+
# No raw source (e.g., already CSSL syntax), parse normally
|
|
2812
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
2813
|
+
stmt = self._parse_statement()
|
|
2814
|
+
if stmt:
|
|
2815
|
+
body.append(stmt)
|
|
2816
|
+
else:
|
|
2817
|
+
# Skip the block - runtime will transform and parse
|
|
2818
|
+
brace_count = 1
|
|
2819
|
+
while brace_count > 0 and not self._is_at_end():
|
|
2820
|
+
if self._check(TokenType.BLOCK_START):
|
|
2821
|
+
brace_count += 1
|
|
2822
|
+
elif self._check(TokenType.BLOCK_END):
|
|
2823
|
+
brace_count -= 1
|
|
2824
|
+
if brace_count > 0:
|
|
2825
|
+
self._advance()
|
|
2826
|
+
self._expect(TokenType.BLOCK_END)
|
|
2827
|
+
|
|
2828
|
+
return ASTNode('supports_block', value={
|
|
2829
|
+
'language': language,
|
|
2830
|
+
'raw_source': raw_source
|
|
2831
|
+
}, children=body)
|
|
2832
|
+
|
|
2833
|
+
def _extract_raw_block_source(self) -> Optional[str]:
|
|
2834
|
+
"""Extract raw source code from a {} block before parsing.
|
|
2835
|
+
|
|
2836
|
+
Used for 'supports' blocks to allow language transformation.
|
|
2837
|
+
"""
|
|
2838
|
+
if not self._check(TokenType.BLOCK_START):
|
|
2839
|
+
return None
|
|
2840
|
+
|
|
2841
|
+
# Find the matching block end by counting braces
|
|
2842
|
+
start_pos = self.pos
|
|
2843
|
+
brace_count = 0
|
|
2844
|
+
found_start = False
|
|
2845
|
+
|
|
2846
|
+
# Walk through tokens to find matching close brace
|
|
2847
|
+
temp_pos = self.pos
|
|
2848
|
+
while temp_pos < len(self.tokens):
|
|
2849
|
+
token = self.tokens[temp_pos]
|
|
2850
|
+
if token.type == TokenType.BLOCK_START:
|
|
2851
|
+
if not found_start:
|
|
2852
|
+
found_start = True
|
|
2853
|
+
brace_count += 1
|
|
2854
|
+
elif token.type == TokenType.BLOCK_END:
|
|
2855
|
+
brace_count -= 1
|
|
2856
|
+
if brace_count == 0:
|
|
2857
|
+
break
|
|
2858
|
+
temp_pos += 1
|
|
2859
|
+
|
|
2860
|
+
# Build source from tokens between braces (excluding braces)
|
|
2861
|
+
source_parts = []
|
|
2862
|
+
for i in range(start_pos + 1, temp_pos):
|
|
2863
|
+
token = self.tokens[i]
|
|
2864
|
+
if token.type == TokenType.STRING:
|
|
2865
|
+
source_parts.append(f'"{token.value}"')
|
|
2866
|
+
elif token.type == TokenType.NEWLINE:
|
|
2867
|
+
source_parts.append('\n')
|
|
2868
|
+
else:
|
|
2869
|
+
source_parts.append(str(token.value))
|
|
2870
|
+
|
|
2871
|
+
return ' '.join(source_parts)
|
|
2872
|
+
|
|
2873
|
+
def _extract_raw_block_body(self) -> Optional[str]:
|
|
2874
|
+
"""Extract raw source code body from a {} block for language transformation.
|
|
2875
|
+
|
|
2876
|
+
v4.2.0: Used for 'supports' blocks to preserve original source (including indentation).
|
|
2877
|
+
This extracts the raw text between { and } from the original source string.
|
|
2878
|
+
|
|
2879
|
+
Returns the raw body content without the surrounding braces.
|
|
2880
|
+
"""
|
|
2881
|
+
if not self._check(TokenType.BLOCK_START):
|
|
2882
|
+
return None
|
|
2883
|
+
|
|
2884
|
+
# Get the { token's position
|
|
2885
|
+
start_token = self._current()
|
|
2886
|
+
start_line = start_token.line
|
|
2887
|
+
start_col = start_token.column
|
|
2888
|
+
|
|
2889
|
+
# Find the { character position in source
|
|
2890
|
+
# Line numbers are 1-indexed, columns are 1-indexed
|
|
2891
|
+
brace_start_pos = 0
|
|
2892
|
+
current_line = 1
|
|
2893
|
+
for i, char in enumerate(self.source):
|
|
2894
|
+
if current_line == start_line:
|
|
2895
|
+
# Found the right line, now find the column
|
|
2896
|
+
col_in_line = i - brace_start_pos + 1
|
|
2897
|
+
if col_in_line >= start_col:
|
|
2898
|
+
# Search for { from here
|
|
2899
|
+
for j in range(i, len(self.source)):
|
|
2900
|
+
if self.source[j] == '{':
|
|
2901
|
+
brace_start_pos = j
|
|
2902
|
+
break
|
|
2903
|
+
break
|
|
2904
|
+
if char == '\n':
|
|
2905
|
+
current_line += 1
|
|
2906
|
+
brace_start_pos = i + 1
|
|
2907
|
+
|
|
2908
|
+
# Now find the matching closing brace
|
|
2909
|
+
brace_count = 1
|
|
2910
|
+
pos = brace_start_pos + 1
|
|
2911
|
+
while pos < len(self.source) and brace_count > 0:
|
|
2912
|
+
char = self.source[pos]
|
|
2913
|
+
if char == '{':
|
|
2914
|
+
brace_count += 1
|
|
2915
|
+
elif char == '}':
|
|
2916
|
+
brace_count -= 1
|
|
2917
|
+
pos += 1
|
|
2918
|
+
|
|
2919
|
+
# Extract the body (everything between { and })
|
|
2920
|
+
body = self.source[brace_start_pos + 1:pos - 1]
|
|
2921
|
+
return body.strip()
|
|
2922
|
+
|
|
2635
2923
|
def _parse_action_block(self) -> ASTNode:
|
|
2636
2924
|
"""Parse an action block { ... } containing statements for createcmd"""
|
|
2637
2925
|
node = ASTNode('action_block', children=[])
|
|
@@ -2948,6 +3236,14 @@ class CSSLParser:
|
|
|
2948
3236
|
if self._match(TokenType.MINUS):
|
|
2949
3237
|
operand = self._parse_unary()
|
|
2950
3238
|
return ASTNode('unary', value={'op': '-', 'operand': operand})
|
|
3239
|
+
# Prefix increment: ++i
|
|
3240
|
+
if self._match(TokenType.PLUS_PLUS):
|
|
3241
|
+
operand = self._parse_unary()
|
|
3242
|
+
return ASTNode('increment', value={'op': 'prefix', 'operand': operand})
|
|
3243
|
+
# Prefix decrement: --i
|
|
3244
|
+
if self._match(TokenType.MINUS_MINUS):
|
|
3245
|
+
operand = self._parse_unary()
|
|
3246
|
+
return ASTNode('decrement', value={'op': 'prefix', 'operand': operand})
|
|
2951
3247
|
if self._match(TokenType.AMPERSAND):
|
|
2952
3248
|
# Reference operator: &variable or &@module
|
|
2953
3249
|
operand = self._parse_unary()
|
|
@@ -3090,12 +3386,13 @@ class CSSLParser:
|
|
|
3090
3386
|
token = self._advance()
|
|
3091
3387
|
node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
|
|
3092
3388
|
# Check for member access, calls, indexing - with kwargs support
|
|
3389
|
+
# Support both . and -> for member access
|
|
3093
3390
|
while True:
|
|
3094
3391
|
if self._match(TokenType.PAREN_START):
|
|
3095
3392
|
args, kwargs = self._parse_call_arguments()
|
|
3096
3393
|
self._expect(TokenType.PAREN_END)
|
|
3097
3394
|
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
3098
|
-
elif self._match(TokenType.DOT):
|
|
3395
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
3099
3396
|
member = self._advance().value
|
|
3100
3397
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
3101
3398
|
elif self._match(TokenType.BRACKET_START):
|
|
@@ -3111,12 +3408,14 @@ class CSSLParser:
|
|
|
3111
3408
|
token = self._advance()
|
|
3112
3409
|
node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
|
|
3113
3410
|
# Check for member access, calls, indexing - with kwargs support
|
|
3411
|
+
# Support both . and -> for member access (like this->member)
|
|
3114
3412
|
while True:
|
|
3115
3413
|
if self._match(TokenType.PAREN_START):
|
|
3116
3414
|
args, kwargs = self._parse_call_arguments()
|
|
3117
3415
|
self._expect(TokenType.PAREN_END)
|
|
3118
3416
|
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
3119
|
-
elif self._match(TokenType.DOT):
|
|
3417
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
3418
|
+
# Support both $obj.member and $obj->member syntax
|
|
3120
3419
|
member = self._advance().value
|
|
3121
3420
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
3122
3421
|
elif self._match(TokenType.BRACKET_START):
|
|
@@ -3132,12 +3431,36 @@ class CSSLParser:
|
|
|
3132
3431
|
token = self._advance()
|
|
3133
3432
|
node = ASTNode('captured_ref', value=token.value, line=token.line, column=token.column)
|
|
3134
3433
|
# Check for member access, calls, indexing - with kwargs support
|
|
3434
|
+
# Support both . and -> for member access
|
|
3135
3435
|
while True:
|
|
3136
3436
|
if self._match(TokenType.PAREN_START):
|
|
3137
3437
|
args, kwargs = self._parse_call_arguments()
|
|
3138
3438
|
self._expect(TokenType.PAREN_END)
|
|
3139
3439
|
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
3140
|
-
elif self._match(TokenType.DOT):
|
|
3440
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
3441
|
+
member = self._advance().value
|
|
3442
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
3443
|
+
elif self._match(TokenType.BRACKET_START):
|
|
3444
|
+
index = self._parse_expression()
|
|
3445
|
+
self._expect(TokenType.BRACKET_END)
|
|
3446
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
3447
|
+
else:
|
|
3448
|
+
break
|
|
3449
|
+
return node
|
|
3450
|
+
|
|
3451
|
+
# v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
|
|
3452
|
+
if self._check(TokenType.LANG_INSTANCE_REF):
|
|
3453
|
+
token = self._advance()
|
|
3454
|
+
ref = token.value # {'lang': 'cpp', 'instance': 'ClassName'}
|
|
3455
|
+
node = ASTNode('lang_instance_ref', value=ref, line=token.line, column=token.column)
|
|
3456
|
+
# Check for member access, calls, indexing
|
|
3457
|
+
# Support both . and -> for member access
|
|
3458
|
+
while True:
|
|
3459
|
+
if self._match(TokenType.PAREN_START):
|
|
3460
|
+
args, kwargs = self._parse_call_arguments()
|
|
3461
|
+
self._expect(TokenType.PAREN_END)
|
|
3462
|
+
node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
|
|
3463
|
+
elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
|
|
3141
3464
|
member = self._advance().value
|
|
3142
3465
|
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
3143
3466
|
elif self._match(TokenType.BRACKET_START):
|
|
@@ -3398,6 +3721,12 @@ class CSSLParser:
|
|
|
3398
3721
|
index = self._parse_expression()
|
|
3399
3722
|
self._expect(TokenType.BRACKET_END)
|
|
3400
3723
|
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
3724
|
+
# Postfix increment: i++
|
|
3725
|
+
elif self._match(TokenType.PLUS_PLUS):
|
|
3726
|
+
node = ASTNode('increment', value={'op': 'postfix', 'operand': node})
|
|
3727
|
+
# Postfix decrement: i--
|
|
3728
|
+
elif self._match(TokenType.MINUS_MINUS):
|
|
3729
|
+
node = ASTNode('decrement', value={'op': 'postfix', 'operand': node})
|
|
3401
3730
|
else:
|
|
3402
3731
|
break
|
|
3403
3732
|
|
|
@@ -3437,9 +3766,11 @@ class CSSLParser:
|
|
|
3437
3766
|
|
|
3438
3767
|
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
3439
3768
|
# Parse statement or expression
|
|
3769
|
+
# v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
|
|
3440
3770
|
if (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
|
|
3441
3771
|
self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
|
|
3442
3772
|
self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
|
|
3773
|
+
self._check(TokenType.LANG_INSTANCE_REF) or
|
|
3443
3774
|
self._check(TokenType.STRING) or self._check(TokenType.NUMBER) or
|
|
3444
3775
|
self._check(TokenType.BOOLEAN) or self._check(TokenType.NULL) or
|
|
3445
3776
|
self._check(TokenType.PAREN_START)):
|
|
@@ -3508,7 +3839,7 @@ def parse_cssl(source: str) -> ASTNode:
|
|
|
3508
3839
|
"""Parse CSSL source code into an AST - auto-detects service vs program format"""
|
|
3509
3840
|
lexer = CSSLLexer(source)
|
|
3510
3841
|
tokens = lexer.tokenize()
|
|
3511
|
-
parser = CSSLParser(tokens, lexer.source_lines)
|
|
3842
|
+
parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
|
|
3512
3843
|
|
|
3513
3844
|
# Auto-detect: if first token is '{', it's a service file
|
|
3514
3845
|
# Otherwise treat as standalone program (whitespace is already filtered by lexer)
|
|
@@ -3522,7 +3853,7 @@ def parse_cssl_program(source: str) -> ASTNode:
|
|
|
3522
3853
|
"""Parse standalone CSSL program (no service wrapper) into an AST"""
|
|
3523
3854
|
lexer = CSSLLexer(source)
|
|
3524
3855
|
tokens = lexer.tokenize()
|
|
3525
|
-
parser = CSSLParser(tokens, lexer.source_lines)
|
|
3856
|
+
parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
|
|
3526
3857
|
return parser.parse_program()
|
|
3527
3858
|
|
|
3528
3859
|
|