IncludeCPP 4.0.0__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.
@@ -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"""
@@ -1084,7 +1114,7 @@ class CSSLParser:
1084
1114
  append_ref_class = None
1085
1115
  append_ref_member = None
1086
1116
 
1087
- # Check for &ClassName::member reference
1117
+ # Check for &ClassName::member or &ClassName.member or &function reference
1088
1118
  if self._match(TokenType.AMPERSAND):
1089
1119
  if self._check(TokenType.IDENTIFIER):
1090
1120
  append_ref_class = self._advance().value
@@ -1095,8 +1125,8 @@ class CSSLParser:
1095
1125
  elif self._check(TokenType.SHARED_REF):
1096
1126
  append_ref_class = f'${self._advance().value}'
1097
1127
 
1098
- # Check for ::member
1099
- if self._match(TokenType.DOUBLE_COLON):
1128
+ # Check for ::member or .member (support both syntaxes)
1129
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1100
1130
  if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1101
1131
  append_ref_member = self._advance().value
1102
1132
 
@@ -1108,7 +1138,7 @@ class CSSLParser:
1108
1138
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1109
1139
  while True:
1110
1140
  if self._match_keyword('extends'):
1111
- # Parse target: @ModuleName, $PythonObject, Parent::method
1141
+ # Parse target: @ModuleName, $PythonObject, Parent::method, Parent.method
1112
1142
  if self._check(TokenType.AT):
1113
1143
  self._advance()
1114
1144
  extends_func = '@' + self._advance().value
@@ -1117,7 +1147,8 @@ class CSSLParser:
1117
1147
  extends_func = self._advance().value
1118
1148
  elif self._check(TokenType.IDENTIFIER):
1119
1149
  first_part = self._advance().value
1120
- if self._match(TokenType.DOUBLE_COLON):
1150
+ # Support both :: and . for class method access
1151
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1121
1152
  extends_class_ref = first_part
1122
1153
  extends_method_ref = self._advance().value
1123
1154
  else:
@@ -1134,7 +1165,8 @@ class CSSLParser:
1134
1165
  overwrites_func = self._advance().value
1135
1166
  elif self._check(TokenType.IDENTIFIER):
1136
1167
  first_part = self._advance().value
1137
- if self._match(TokenType.DOUBLE_COLON):
1168
+ # Support both :: and . for class method access
1169
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1138
1170
  overwrites_class_ref = first_part
1139
1171
  overwrites_method_ref = self._advance().value
1140
1172
  else:
@@ -1348,13 +1380,7 @@ class CSSLParser:
1348
1380
  # Wrap in global_assignment to mark as global variable (same as 'global' keyword)
1349
1381
  global_stmt = ASTNode('global_assignment', value=stmt)
1350
1382
  root.children.append(global_stmt)
1351
- # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1352
- elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1353
- self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
1354
- self._check(TokenType.KEYWORD)):
1355
- stmt = self._parse_expression_statement()
1356
- if stmt:
1357
- root.children.append(stmt)
1383
+ # Control flow keywords must be checked BEFORE generic KEYWORD handling
1358
1384
  elif self._match_keyword('if'):
1359
1385
  root.children.append(self._parse_if())
1360
1386
  elif self._match_keyword('while'):
@@ -1363,6 +1389,14 @@ class CSSLParser:
1363
1389
  root.children.append(self._parse_for())
1364
1390
  elif self._match_keyword('foreach'):
1365
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)
1366
1400
  # Skip comments and newlines
1367
1401
  elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
1368
1402
  self._advance()
@@ -1625,15 +1659,22 @@ class CSSLParser:
1625
1659
  # class Child : extends Parent (param1, param2) { ... } <- constructor args for parent
1626
1660
  extends_class = None
1627
1661
  extends_is_python = False
1662
+ extends_lang_ref = None # v4.1.0: Cross-language inheritance (cpp$ClassName)
1628
1663
  extends_args = []
1629
1664
  overwrites_class = None
1630
1665
  overwrites_is_python = False
1666
+ supports_language = None # v4.1.0: Multi-language syntax support
1631
1667
 
1632
1668
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1633
1669
  # Parse extends and/or overwrites (can be chained with : or ::)
1634
1670
  while True:
1635
1671
  if self._match_keyword('extends'):
1636
- if self._check(TokenType.IDENTIFIER):
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):
1637
1678
  extends_class = self._advance().value
1638
1679
  elif self._check(TokenType.SHARED_REF):
1639
1680
  extends_class = self._advance().value
@@ -1658,12 +1699,29 @@ class CSSLParser:
1658
1699
  # Skip optional () after class name
1659
1700
  if self._match(TokenType.PAREN_START):
1660
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'")
1661
1714
  else:
1662
- raise CSSLSyntaxError("Expected 'extends' or 'overwrites' after ':' or '::' in class declaration")
1715
+ raise CSSLSyntaxError("Expected 'extends', 'overwrites', or 'supports' after ':' or '::' in class declaration")
1663
1716
  # Check for another : or :: for chaining
1664
1717
  if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
1665
1718
  break
1666
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
+
1667
1725
  node = ASTNode('class', value={
1668
1726
  'name': class_name,
1669
1727
  'is_global': is_global,
@@ -1671,10 +1729,29 @@ class CSSLParser:
1671
1729
  'class_params': class_params,
1672
1730
  'extends': extends_class,
1673
1731
  'extends_is_python': extends_is_python,
1732
+ 'extends_lang_ref': extends_lang_ref, # v4.1.0
1674
1733
  'extends_args': extends_args,
1675
1734
  'overwrites': overwrites_class,
1676
- '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
1677
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
+
1678
1755
  self._expect(TokenType.BLOCK_START)
1679
1756
 
1680
1757
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
@@ -1905,13 +1982,14 @@ class CSSLParser:
1905
1982
  """Parse define function declaration.
1906
1983
 
1907
1984
  Syntax:
1908
- define MyFunc(args) { } // Local function
1909
- global define MyFunc(args) { } // Global function
1910
- define @MyFunc(args) { } // Global function (alternative)
1911
- define *MyFunc(args) { } // Non-null: must never return None
1912
- define MyFunc : extends OtherFunc() { } // Inherit local vars
1913
- define MyFunc : overwrites OtherFunc() { } // Replace OtherFunc
1914
- define MyFunc :: extends Parent::Method :: overwrites Parent::Method() { } // Method-level inheritance
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
1915
1993
  """
1916
1994
  # Check for * prefix (non-null function - must return non-null)
1917
1995
  # Also *[type] for type exclusion (must NOT return that type)
@@ -1933,8 +2011,56 @@ class CSSLParser:
1933
2011
 
1934
2012
  name = self._advance().value
1935
2013
 
1936
- # Check for extends/overwrites: define func : extends/overwrites target() { }
1937
- # Also supports method-level :: syntax: define func :: extends Parent::method
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
1938
2064
  extends_func = None
1939
2065
  overwrites_func = None
1940
2066
  extends_is_python = False
@@ -1943,6 +2069,7 @@ class CSSLParser:
1943
2069
  extends_method_ref = None
1944
2070
  overwrites_class_ref = None
1945
2071
  overwrites_method_ref = None
2072
+ supports_language = None # v4.1.0: Multi-language syntax support
1946
2073
 
1947
2074
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1948
2075
  # Parse extends and/or overwrites (supports :: method-level syntax)
@@ -2003,49 +2130,24 @@ class CSSLParser:
2003
2130
  # Skip optional () after function/method name
2004
2131
  if self._match(TokenType.PAREN_START):
2005
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'")
2006
2145
  else:
2007
2146
  break
2008
2147
  # Check for another :: or : for chaining extends/overwrites
2009
2148
  if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
2010
2149
  break
2011
2150
 
2012
- params = []
2013
-
2014
- if self._match(TokenType.PAREN_START):
2015
- while not self._check(TokenType.PAREN_END):
2016
- param_info = {}
2017
- # Handle 'open' keyword for open parameters
2018
- if self._match_keyword('open'):
2019
- param_info['open'] = True
2020
- # Handle type annotations (e.g., string, int, dynamic, etc.)
2021
- if self._check(TokenType.KEYWORD):
2022
- param_info['type'] = self._advance().value
2023
- # Handle reference operator &
2024
- if self._match(TokenType.AMPERSAND):
2025
- param_info['ref'] = True
2026
- # Handle * prefix for non-null parameters
2027
- if self._match(TokenType.MULTIPLY):
2028
- param_info['non_null'] = True
2029
- # Get parameter name
2030
- if self._check(TokenType.IDENTIFIER):
2031
- param_name = self._advance().value
2032
- if param_info:
2033
- params.append({'name': param_name, **param_info})
2034
- else:
2035
- params.append(param_name)
2036
- self._match(TokenType.COMMA)
2037
- elif self._check(TokenType.KEYWORD):
2038
- # Parameter name could be a keyword like 'Params'
2039
- param_name = self._advance().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
- else:
2046
- break
2047
- self._expect(TokenType.PAREN_END)
2048
-
2049
2151
  # New: Append mode and reference tracking for functions
2050
2152
  # Syntax: define XYZ(int zahl) &overwrittenclass::functionyouwanttokeep ++ { ... }
2051
2153
  append_mode = False
@@ -2072,6 +2174,37 @@ class CSSLParser:
2072
2174
  if self._match(TokenType.PLUS_PLUS):
2073
2175
  append_mode = True
2074
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
+
2075
2208
  node = ASTNode('function', value={
2076
2209
  'name': name,
2077
2210
  'is_global': is_global,
@@ -2090,16 +2223,13 @@ class CSSLParser:
2090
2223
  # New append mode fields
2091
2224
  'append_mode': append_mode,
2092
2225
  'append_ref_class': append_ref_class,
2093
- 'append_ref_member': append_ref_member
2094
- }, children=[])
2095
- self._expect(TokenType.BLOCK_START)
2096
-
2097
- while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
2098
- stmt = self._parse_statement()
2099
- if stmt:
2100
- 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)
2101
2232
 
2102
- self._expect(TokenType.BLOCK_END)
2103
2233
  return node
2104
2234
 
2105
2235
  def _parse_statement(self) -> Optional[ASTNode]:
@@ -2125,6 +2255,9 @@ class CSSLParser:
2125
2255
  return self._parse_try()
2126
2256
  elif self._match_keyword('await'):
2127
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()
2128
2261
  elif self._match_keyword('define'):
2129
2262
  # Nested define function
2130
2263
  return self._parse_define()
@@ -2142,9 +2275,11 @@ class CSSLParser:
2142
2275
  self._peek(1).type == TokenType.DOUBLE_COLON)):
2143
2276
  # super() or super::method() call - calls parent constructor/method
2144
2277
  return self._parse_super_call()
2278
+ # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
2145
2279
  elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
2146
2280
  self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
2147
2281
  self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
2282
+ self._check(TokenType.LANG_INSTANCE_REF) or
2148
2283
  (self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new')) or
2149
2284
  self._looks_like_namespace_call()):
2150
2285
  return self._parse_expression_statement()
@@ -2350,42 +2485,40 @@ class CSSLParser:
2350
2485
 
2351
2486
  Supports: i = i + 1, i++, ++i, i += 1, i -= 1
2352
2487
  """
2353
- # Check for prefix increment/decrement: ++i or --i
2354
- if self._check(TokenType.PLUS) or self._check(TokenType.MINUS):
2355
- op_token = self._advance()
2356
- # Check for double operator (++ or --)
2357
- if self._check(op_token.type):
2358
- self._advance()
2359
- var_name = self._advance().value
2360
- op = 'increment' if op_token.type == TokenType.PLUS else 'decrement'
2361
- return ASTNode('c_for_update', value={'var': var_name, 'op': op})
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'})
2362
2497
 
2363
2498
  # Regular variable assignment or postfix
2364
2499
  var_name = self._advance().value
2365
2500
 
2366
- # Check for postfix increment/decrement: i++ or i--
2367
- if self._check(TokenType.PLUS):
2501
+ # Check for postfix increment/decrement: i++ or i-- (as single PLUS_PLUS/MINUS_MINUS token)
2502
+ if self._check(TokenType.PLUS_PLUS):
2368
2503
  self._advance()
2369
- if self._check(TokenType.PLUS):
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):
2510
+ self._advance()
2511
+ if self._check(TokenType.EQUALS):
2370
2512
  self._advance()
2371
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2372
- else:
2373
- # i += value
2374
- if self._check(TokenType.EQUALS):
2375
- self._advance()
2376
- value = self._parse_expression()
2377
- 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
2378
2516
  elif self._check(TokenType.MINUS):
2379
2517
  self._advance()
2380
- if self._check(TokenType.MINUS):
2518
+ if self._check(TokenType.EQUALS):
2381
2519
  self._advance()
2382
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2383
- else:
2384
- # i -= value
2385
- if self._check(TokenType.EQUALS):
2386
- self._advance()
2387
- value = self._parse_expression()
2388
- 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})
2389
2522
 
2390
2523
  # Regular assignment: i = expression
2391
2524
  if self._check(TokenType.EQUALS):
@@ -2630,6 +2763,163 @@ class CSSLParser:
2630
2763
  self._match(TokenType.SEMICOLON)
2631
2764
  return ASTNode('await', value=expr)
2632
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
+
2633
2923
  def _parse_action_block(self) -> ASTNode:
2634
2924
  """Parse an action block { ... } containing statements for createcmd"""
2635
2925
  node = ASTNode('action_block', children=[])
@@ -2946,6 +3236,14 @@ class CSSLParser:
2946
3236
  if self._match(TokenType.MINUS):
2947
3237
  operand = self._parse_unary()
2948
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})
2949
3247
  if self._match(TokenType.AMPERSAND):
2950
3248
  # Reference operator: &variable or &@module
2951
3249
  operand = self._parse_unary()
@@ -3088,12 +3386,13 @@ class CSSLParser:
3088
3386
  token = self._advance()
3089
3387
  node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
3090
3388
  # Check for member access, calls, indexing - with kwargs support
3389
+ # Support both . and -> for member access
3091
3390
  while True:
3092
3391
  if self._match(TokenType.PAREN_START):
3093
3392
  args, kwargs = self._parse_call_arguments()
3094
3393
  self._expect(TokenType.PAREN_END)
3095
3394
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3096
- elif self._match(TokenType.DOT):
3395
+ elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
3097
3396
  member = self._advance().value
3098
3397
  node = ASTNode('member_access', value={'object': node, 'member': member})
3099
3398
  elif self._match(TokenType.BRACKET_START):
@@ -3109,12 +3408,14 @@ class CSSLParser:
3109
3408
  token = self._advance()
3110
3409
  node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
3111
3410
  # Check for member access, calls, indexing - with kwargs support
3411
+ # Support both . and -> for member access (like this->member)
3112
3412
  while True:
3113
3413
  if self._match(TokenType.PAREN_START):
3114
3414
  args, kwargs = self._parse_call_arguments()
3115
3415
  self._expect(TokenType.PAREN_END)
3116
3416
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3117
- 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
3118
3419
  member = self._advance().value
3119
3420
  node = ASTNode('member_access', value={'object': node, 'member': member})
3120
3421
  elif self._match(TokenType.BRACKET_START):
@@ -3130,12 +3431,36 @@ class CSSLParser:
3130
3431
  token = self._advance()
3131
3432
  node = ASTNode('captured_ref', value=token.value, line=token.line, column=token.column)
3132
3433
  # Check for member access, calls, indexing - with kwargs support
3434
+ # Support both . and -> for member access
3133
3435
  while True:
3134
3436
  if self._match(TokenType.PAREN_START):
3135
3437
  args, kwargs = self._parse_call_arguments()
3136
3438
  self._expect(TokenType.PAREN_END)
3137
3439
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3138
- 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):
3139
3464
  member = self._advance().value
3140
3465
  node = ASTNode('member_access', value={'object': node, 'member': member})
3141
3466
  elif self._match(TokenType.BRACKET_START):
@@ -3396,6 +3721,12 @@ class CSSLParser:
3396
3721
  index = self._parse_expression()
3397
3722
  self._expect(TokenType.BRACKET_END)
3398
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})
3399
3730
  else:
3400
3731
  break
3401
3732
 
@@ -3435,9 +3766,11 @@ class CSSLParser:
3435
3766
 
3436
3767
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3437
3768
  # Parse statement or expression
3769
+ # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
3438
3770
  if (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
3439
3771
  self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
3440
3772
  self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
3773
+ self._check(TokenType.LANG_INSTANCE_REF) or
3441
3774
  self._check(TokenType.STRING) or self._check(TokenType.NUMBER) or
3442
3775
  self._check(TokenType.BOOLEAN) or self._check(TokenType.NULL) or
3443
3776
  self._check(TokenType.PAREN_START)):
@@ -3506,7 +3839,7 @@ def parse_cssl(source: str) -> ASTNode:
3506
3839
  """Parse CSSL source code into an AST - auto-detects service vs program format"""
3507
3840
  lexer = CSSLLexer(source)
3508
3841
  tokens = lexer.tokenize()
3509
- parser = CSSLParser(tokens, lexer.source_lines)
3842
+ parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
3510
3843
 
3511
3844
  # Auto-detect: if first token is '{', it's a service file
3512
3845
  # Otherwise treat as standalone program (whitespace is already filtered by lexer)
@@ -3520,7 +3853,7 @@ def parse_cssl_program(source: str) -> ASTNode:
3520
3853
  """Parse standalone CSSL program (no service wrapper) into an AST"""
3521
3854
  lexer = CSSLLexer(source)
3522
3855
  tokens = lexer.tokenize()
3523
- parser = CSSLParser(tokens, lexer.source_lines)
3856
+ parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
3524
3857
  return parser.parse_program()
3525
3858
 
3526
3859