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.
@@ -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
- # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
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
- 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):
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 'overwrites' after ':' or '::' in class declaration")
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) { } // Local function
1911
- global define MyFunc(args) { } // Global function
1912
- define @MyFunc(args) { } // Global function (alternative)
1913
- define *MyFunc(args) { } // Non-null: must never return None
1914
- define MyFunc : extends OtherFunc() { } // Inherit local vars
1915
- define MyFunc : overwrites OtherFunc() { } // Replace OtherFunc
1916
- 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
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
- # Check for extends/overwrites: define func : extends/overwrites target() { }
1939
- # 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
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
- }, children=[])
2097
- self._expect(TokenType.BLOCK_START)
2098
-
2099
- while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
2100
- stmt = self._parse_statement()
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.PLUS) or self._check(TokenType.MINUS):
2357
- op_token = self._advance()
2358
- # Check for double operator (++ or --)
2359
- if self._check(op_token.type):
2360
- self._advance()
2361
- var_name = self._advance().value
2362
- op = 'increment' if op_token.type == TokenType.PLUS else 'decrement'
2363
- 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'})
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.PLUS):
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.PLUS):
2511
+ if self._check(TokenType.EQUALS):
2372
2512
  self._advance()
2373
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2374
- else:
2375
- # i += value
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.MINUS):
2518
+ if self._check(TokenType.EQUALS):
2383
2519
  self._advance()
2384
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2385
- else:
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