IncludeCPP 4.0.2__py3-none-any.whl → 4.3.0__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,15 +113,18 @@ 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 = {
119
121
  # Service structure
120
- 'service-init', 'service-run', 'service-include', 'struct', 'define', 'main', 'class', 'constr', 'extends', 'overwrites', 'new', 'this', 'super',
122
+ 'service-init', 'service-run', 'service-include', 'struct', 'define', 'main', 'class', 'constr', 'extends', 'overwrites', 'new', 'this', 'super', 'enum',
121
123
  # Control flow
122
124
  'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
123
125
  'switch', 'case', 'default', 'break', 'continue', 'return',
124
126
  'try', 'catch', 'finally', 'throw',
127
+ 'except', 'always', # v4.2.5: param switch keywords
125
128
  # Literals
126
129
  'True', 'False', 'null', 'None', 'true', 'false',
127
130
  # Logical operators
@@ -139,6 +142,7 @@ KEYWORDS = {
139
142
  'datastruct', # Universal container (lazy declarator)
140
143
  'dataspace', # SQL/data storage container
141
144
  'shuffled', # Unorganized fast storage (multiple returns)
145
+ 'bytearrayed', # Function-to-byte mapping with pattern matching (v4.2.5)
142
146
  'iterator', # Advanced iterator with tasks
143
147
  'combo', # Filter/search spaces
144
148
  'structure', # Advanced C++/Py Class
@@ -153,14 +157,17 @@ KEYWORDS = {
153
157
  'sqlbased', # SQL-based function
154
158
  'public', # Explicitly public (default)
155
159
  'static', # Static method/function
160
+ 'embedded', # Immediate &target replacement at registration (v4.2.5)
156
161
  # CSSL Include Keywords
157
162
  'include', 'get',
163
+ # Multi-language support (v4.1.0)
164
+ 'supports', 'libinclude',
158
165
  }
159
166
 
160
167
  # Function modifiers that can appear in any order before function name
161
168
  FUNCTION_MODIFIERS = {
162
169
  'undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual',
163
- 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled'
170
+ 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled', 'embedded'
164
171
  }
165
172
 
166
173
  # Type literals that create empty instances
@@ -182,6 +189,12 @@ INJECTION_HELPERS = {
182
189
  'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
183
190
  }
184
191
 
192
+ # Language identifiers for multi-language support (v4.1.0)
193
+ # Used in lang$instance patterns like cpp$MyClass, py$Object
194
+ LANGUAGE_IDS = {
195
+ 'cpp', 'py', 'python', 'java', 'csharp', 'js', 'javascript'
196
+ }
197
+
185
198
 
186
199
  @dataclass
187
200
  class Token:
@@ -338,13 +351,16 @@ class CSSLLexer:
338
351
  self._add_token(TokenType.MULTIPLY, '*')
339
352
  self._advance()
340
353
  elif char == '/':
341
- # Check if this is a // comment (handled above) or division
342
- if self._peek(1) != '/':
354
+ # Check for // comment, /* block comment */, or division
355
+ if self._peek(1) == '/':
356
+ # Single-line comment
357
+ self._skip_comment()
358
+ elif self._peek(1) == '*':
359
+ # Block comment /* ... */
360
+ self._skip_block_comment()
361
+ else:
343
362
  self._add_token(TokenType.DIVIDE, '/')
344
363
  self._advance()
345
- else:
346
- # Already handled by // comment check above, but just in case
347
- self._skip_comment()
348
364
  elif char == '<':
349
365
  self._read_less_than()
350
366
  elif char == '>':
@@ -389,6 +405,26 @@ class CSSLLexer:
389
405
  while self.pos < len(self.source) and self.source[self.pos] != '\n':
390
406
  self._advance()
391
407
 
408
+ def _skip_block_comment(self):
409
+ """Skip block comment /* ... */ including nested comments"""
410
+ self._advance() # skip /
411
+ self._advance() # skip *
412
+ depth = 1
413
+ while self.pos < len(self.source) and depth > 0:
414
+ if self.source[self.pos] == '/' and self._peek(1) == '*':
415
+ depth += 1
416
+ self._advance()
417
+ self._advance()
418
+ elif self.source[self.pos] == '*' and self._peek(1) == '/':
419
+ depth -= 1
420
+ self._advance()
421
+ self._advance()
422
+ else:
423
+ if self.source[self.pos] == '\n':
424
+ self.line += 1
425
+ self.column = 0
426
+ self._advance()
427
+
392
428
  def _read_string(self, quote_char: str):
393
429
  self._advance()
394
430
  start = self.pos
@@ -443,6 +479,19 @@ class CSSLLexer:
443
479
  start = self.pos
444
480
  if self.source[self.pos] == '-':
445
481
  self._advance()
482
+
483
+ # Check for hex number: 0x... or 0X...
484
+ if (self.pos < len(self.source) and self.source[self.pos] == '0' and
485
+ self.pos + 1 < len(self.source) and self.source[self.pos + 1] in 'xX'):
486
+ self._advance() # skip '0'
487
+ self._advance() # skip 'x' or 'X'
488
+ # Read hex digits
489
+ while self.pos < len(self.source) and self.source[self.pos] in '0123456789abcdefABCDEF':
490
+ self._advance()
491
+ value = self.source[start:self.pos]
492
+ self._add_token(TokenType.NUMBER, int(value, 16))
493
+ return
494
+
446
495
  while self.pos < len(self.source) and (self.source[self.pos].isdigit() or self.source[self.pos] == '.'):
447
496
  self._advance()
448
497
  value = self.source[start:self.pos]
@@ -457,6 +506,25 @@ class CSSLLexer:
457
506
  self._advance()
458
507
  value = self.source[start:self.pos]
459
508
 
509
+ # Check for language$instance pattern (v4.1.0)
510
+ # e.g., cpp$MyClass, py$Object, java$Service
511
+ if value.lower() in LANGUAGE_IDS and self.pos < len(self.source) and self.source[self.pos] == '$':
512
+ lang_id = value
513
+ self._advance() # skip '$'
514
+ # Read instance name
515
+ instance_start = self.pos
516
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
517
+ self._advance()
518
+ instance_name = self.source[instance_start:self.pos]
519
+ if instance_name:
520
+ self._add_token(TokenType.LANG_INSTANCE_REF, {'lang': lang_id, 'instance': instance_name})
521
+ return
522
+ # If no instance name, revert and treat as normal identifier
523
+ self.pos = start
524
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
525
+ self._advance()
526
+ value = self.source[start:self.pos]
527
+
460
528
  if value in ('True', 'true'):
461
529
  self._add_token(TokenType.BOOLEAN, True)
462
530
  elif value in ('False', 'false'):
@@ -672,10 +740,11 @@ class ASTNode:
672
740
  class CSSLParser:
673
741
  """Parses CSSL tokens into an Abstract Syntax Tree."""
674
742
 
675
- def __init__(self, tokens: List[Token], source_lines: List[str] = None):
743
+ def __init__(self, tokens: List[Token], source_lines: List[str] = None, source: str = None):
676
744
  self.tokens = [t for t in tokens if t.type != TokenType.NEWLINE]
677
745
  self.pos = 0
678
746
  self.source_lines = source_lines or []
747
+ self.source = source or '\n'.join(self.source_lines) # v4.2.0: Full source for raw extraction
679
748
 
680
749
  def get_source_line(self, line_num: int) -> str:
681
750
  """Get a specific source line for error reporting"""
@@ -736,6 +805,41 @@ class CSSLParser:
736
805
  'list', 'dictionary', 'dict', 'instance', 'map', 'openquote', 'parameter',
737
806
  'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
738
807
 
808
+ def _parse_generic_type_content(self) -> str:
809
+ """Parse generic type content including nested generics.
810
+
811
+ Handles: <int>, <string, dynamic>, <map<string, dynamic>>, etc.
812
+ Returns the full type string including nested generics.
813
+
814
+ Called AFTER consuming the opening '<'.
815
+ """
816
+ parts = []
817
+ depth = 1 # Already inside first <
818
+
819
+ while depth > 0 and not self._is_at_end():
820
+ if self._check(TokenType.COMPARE_LT):
821
+ parts.append('<')
822
+ depth += 1
823
+ self._advance()
824
+ elif self._check(TokenType.COMPARE_GT):
825
+ depth -= 1
826
+ if depth > 0: # Only add > if not the final closing >
827
+ parts.append('>')
828
+ self._advance()
829
+ elif self._check(TokenType.COMMA):
830
+ parts.append(', ')
831
+ self._advance()
832
+ elif self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
833
+ parts.append(self._advance().value)
834
+ elif self._check(TokenType.STRING):
835
+ # For instance<"name">
836
+ parts.append(f'"{self._advance().value}"')
837
+ else:
838
+ # Skip whitespace/other tokens
839
+ self._advance()
840
+
841
+ return ''.join(parts)
842
+
739
843
  def _looks_like_function_declaration(self) -> bool:
740
844
  """Check if current position looks like a C-style function declaration.
741
845
 
@@ -870,7 +974,7 @@ class CSSLParser:
870
974
  self.pos = saved_pos
871
975
  return False
872
976
 
873
- def _parse_typed_function(self, is_global: bool = False) -> ASTNode:
977
+ def _parse_typed_function(self, is_global: bool = False, is_embedded: bool = False) -> ASTNode:
874
978
  """Parse C-style typed function declaration with flexible modifier ordering.
875
979
 
876
980
  Supports any order of modifiers, types, non-null (*), and global (@):
@@ -900,6 +1004,8 @@ class CSSLParser:
900
1004
  non_null = False
901
1005
  exclude_type = None
902
1006
  is_const = False
1007
+ # v4.2.5: embedded = immediate &target replacement (can come from param or modifier)
1008
+ _is_embedded = is_embedded
903
1009
 
904
1010
  # Phase 1: Collect all modifiers, type, non-null, and global indicators
905
1011
  # These can appear in any order before the function name
@@ -914,6 +1020,9 @@ class CSSLParser:
914
1020
  elif mod == 'const':
915
1021
  is_const = True
916
1022
  modifiers.append(mod)
1023
+ elif mod == 'embedded':
1024
+ _is_embedded = True
1025
+ modifiers.append(mod)
917
1026
  else:
918
1027
  modifiers.append(mod)
919
1028
  continue
@@ -1153,6 +1262,7 @@ class CSSLParser:
1153
1262
  'name': name,
1154
1263
  'is_global': is_global,
1155
1264
  'is_const': is_const,
1265
+ 'is_embedded': _is_embedded, # v4.2.5: immediate &target replacement
1156
1266
  'params': params,
1157
1267
  'return_type': return_type,
1158
1268
  'generic_type': generic_type,
@@ -1250,19 +1360,16 @@ class CSSLParser:
1250
1360
 
1251
1361
  The * prefix indicates a non-nullable variable (can never be None/null).
1252
1362
  Example: vector<dynamic> *MyVector - can never contain None values.
1363
+ Supports nested generics: datastruct<map<string, dynamic>> zipped;
1253
1364
  """
1254
1365
  # Get type name
1255
1366
  type_name = self._advance().value # Consume type keyword
1256
1367
 
1257
- # Check for generic type <T> or instance<"name">
1368
+ # Check for generic type <T> or instance<"name"> or nested <map<K,V>>
1258
1369
  element_type = None
1259
1370
  if self._match(TokenType.COMPARE_LT):
1260
- # For instance<"name">, element_type can be a string literal
1261
- if type_name == 'instance' and self._check(TokenType.STRING):
1262
- element_type = self._advance().value
1263
- elif self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
1264
- element_type = self._advance().value
1265
- self._expect(TokenType.COMPARE_GT)
1371
+ # Use helper to parse nested generic content
1372
+ element_type = self._parse_generic_type_content()
1266
1373
 
1267
1374
  # Check for * prefix (non-nullable indicator)
1268
1375
  non_null = False
@@ -1307,6 +1414,10 @@ class CSSLParser:
1307
1414
  root.children.append(self._parse_struct())
1308
1415
  elif self._match_keyword('class'):
1309
1416
  root.children.append(self._parse_class())
1417
+ elif self._match_keyword('enum'):
1418
+ root.children.append(self._parse_enum())
1419
+ elif self._match_keyword('bytearrayed'):
1420
+ root.children.append(self._parse_bytearrayed())
1310
1421
  elif self._match_keyword('define'):
1311
1422
  root.children.append(self._parse_define())
1312
1423
  # Check for C-style typed function declarations
@@ -1329,6 +1440,14 @@ class CSSLParser:
1329
1440
  elif self._match_keyword('package-includes'):
1330
1441
  root.children.append(self._parse_package_includes())
1331
1442
  # Handle global declarations
1443
+ # v4.2.5: Handle 'embedded' keyword for immediate &target replacement
1444
+ elif self._match_keyword('embedded'):
1445
+ if self._match_keyword('class'):
1446
+ root.children.append(self._parse_class(is_embedded=True))
1447
+ elif self._looks_like_function_declaration():
1448
+ root.children.append(self._parse_typed_function(is_embedded=True))
1449
+ else:
1450
+ self.error("Expected 'class' or function declaration after 'embedded'")
1332
1451
  elif self._match_keyword('global'):
1333
1452
  # Check if followed by class or define (global class/function)
1334
1453
  if self._match_keyword('class'):
@@ -1350,13 +1469,7 @@ class CSSLParser:
1350
1469
  # Wrap in global_assignment to mark as global variable (same as 'global' keyword)
1351
1470
  global_stmt = ASTNode('global_assignment', value=stmt)
1352
1471
  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)
1472
+ # Control flow keywords must be checked BEFORE generic KEYWORD handling
1360
1473
  elif self._match_keyword('if'):
1361
1474
  root.children.append(self._parse_if())
1362
1475
  elif self._match_keyword('while'):
@@ -1365,6 +1478,14 @@ class CSSLParser:
1365
1478
  root.children.append(self._parse_for())
1366
1479
  elif self._match_keyword('foreach'):
1367
1480
  root.children.append(self._parse_foreach())
1481
+ # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1482
+ # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements (js$GameData.score = 1337)
1483
+ elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1484
+ self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
1485
+ self._check(TokenType.KEYWORD) or self._check(TokenType.LANG_INSTANCE_REF)):
1486
+ stmt = self._parse_expression_statement()
1487
+ if stmt:
1488
+ root.children.append(stmt)
1368
1489
  # Skip comments and newlines
1369
1490
  elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
1370
1491
  self._advance()
@@ -1590,7 +1711,236 @@ class CSSLParser:
1590
1711
  self._expect(TokenType.BLOCK_END)
1591
1712
  return node
1592
1713
 
1593
- def _parse_class(self, is_global: bool = False) -> ASTNode:
1714
+ def _parse_enum(self) -> ASTNode:
1715
+ """Parse enum declaration.
1716
+
1717
+ Syntax:
1718
+ enum EnumName {
1719
+ VALUE1, // Auto value 0
1720
+ VALUE2, // Auto value 1
1721
+ VALUE3 = 10, // Explicit value 10
1722
+ VALUE4 // Auto value 11
1723
+ }
1724
+
1725
+ Access values via EnumName::VALUE1
1726
+ """
1727
+ enum_name = self._advance().value
1728
+ self._expect(TokenType.BLOCK_START)
1729
+
1730
+ members = []
1731
+ current_value = 0
1732
+
1733
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1734
+ # Skip newlines/whitespace between enum values
1735
+ if self._check(TokenType.NEWLINE):
1736
+ self._advance()
1737
+ continue
1738
+
1739
+ # Get member name
1740
+ if self._check(TokenType.IDENTIFIER):
1741
+ member_name = self._advance().value
1742
+
1743
+ # Check for explicit value: VALUE = 10
1744
+ if self._match(TokenType.EQUALS):
1745
+ value_node = self._parse_expression()
1746
+ if isinstance(value_node, ASTNode) and value_node.type == 'literal':
1747
+ val = value_node.value
1748
+ if isinstance(val, dict) and 'value' in val:
1749
+ current_value = val['value']
1750
+ else:
1751
+ current_value = val
1752
+ else:
1753
+ current_value = value_node
1754
+
1755
+ members.append({'name': member_name, 'value': current_value})
1756
+ current_value = current_value + 1 if isinstance(current_value, int) else current_value
1757
+
1758
+ # Skip comma if present
1759
+ self._match(TokenType.COMMA)
1760
+ else:
1761
+ self._advance()
1762
+
1763
+ self._expect(TokenType.BLOCK_END)
1764
+
1765
+ return ASTNode('enum', value={
1766
+ 'name': enum_name,
1767
+ 'members': members
1768
+ })
1769
+
1770
+ def _parse_bytearrayed(self) -> ASTNode:
1771
+ """Parse bytearrayed declaration - function-to-byte mapping with pattern matching.
1772
+
1773
+ Syntax:
1774
+ bytearrayed MyBytes {
1775
+ &func1; // Position 0x0
1776
+ &func2; // Position 0x1
1777
+ &func3; // Position 0x2
1778
+ case {0, 1, 0} { // Pattern match on return values
1779
+ // Execute if func1=0, func2=1, func3=0
1780
+ }
1781
+ case {1, _, _} { // Wildcards with _
1782
+ // Execute if func1=1, others any value
1783
+ }
1784
+ default {
1785
+ // Execute if no case matches
1786
+ }
1787
+ }
1788
+
1789
+ Access:
1790
+ MyBytes() // Execute pattern matching
1791
+ MyBytes["0x0"] // Get value at position 0
1792
+ MyBytes[0] // Get value at position 0
1793
+ """
1794
+ bytearrayed_name = self._advance().value
1795
+ self._expect(TokenType.BLOCK_START)
1796
+
1797
+ func_refs = [] # List of function references at each byte position
1798
+ cases = [] # List of case blocks with patterns
1799
+ default_block = None # Default block if no case matches
1800
+
1801
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1802
+ # Skip newlines
1803
+ if self._check(TokenType.NEWLINE):
1804
+ self._advance()
1805
+ continue
1806
+
1807
+ # Parse case block
1808
+ if self._match_keyword('case'):
1809
+ pattern = []
1810
+ self._expect(TokenType.BLOCK_START)
1811
+
1812
+ # Parse pattern: {value, value, value, ...}
1813
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1814
+ if self._check(TokenType.COMMA):
1815
+ self._advance()
1816
+ continue
1817
+ if self._check(TokenType.NEWLINE):
1818
+ self._advance()
1819
+ continue
1820
+
1821
+ # Parse pattern element
1822
+ if self._check(TokenType.IDENTIFIER) and self._current().value == '_':
1823
+ # Wildcard - matches any value
1824
+ pattern.append({'type': 'wildcard'})
1825
+ self._advance()
1826
+ elif self._check(TokenType.NUMBER):
1827
+ # Check for hex format like 0x28
1828
+ token = self._current()
1829
+ value = token.value
1830
+ if isinstance(value, dict) and 'value' in value:
1831
+ value = value['value']
1832
+ # Check for position=value syntax: 0x28="Gut"
1833
+ self._advance()
1834
+ if self._match(TokenType.EQUALS):
1835
+ match_value = self._parse_expression()
1836
+ pattern.append({'type': 'indexed', 'index': value, 'value': match_value})
1837
+ else:
1838
+ pattern.append({'type': 'value', 'value': value})
1839
+ elif self._check(TokenType.STRING):
1840
+ value = self._advance().value
1841
+ pattern.append({'type': 'value', 'value': value})
1842
+ elif self._check(TokenType.KEYWORD):
1843
+ kw = self._current().value
1844
+ if kw in ('true', 'True'):
1845
+ pattern.append({'type': 'value', 'value': True})
1846
+ self._advance()
1847
+ elif kw in ('false', 'False'):
1848
+ pattern.append({'type': 'value', 'value': False})
1849
+ self._advance()
1850
+ elif kw in TYPE_GENERICS or kw in ('int', 'string', 'float', 'bool', 'dynamic'):
1851
+ # Type pattern: vector<string>, int, etc.
1852
+ type_name = self._advance().value
1853
+ if self._check(TokenType.COMPARE_LT):
1854
+ # Generic type
1855
+ self._advance()
1856
+ inner = self._parse_generic_type_content()
1857
+ type_name = f"{type_name}<{inner}>"
1858
+ pattern.append({'type': 'type_match', 'type_name': type_name})
1859
+ else:
1860
+ # Skip unknown keywords
1861
+ self._advance()
1862
+ elif self._check(TokenType.IDENTIFIER):
1863
+ # Could be a variable reference or type
1864
+ ident = self._advance().value
1865
+ # Check for indexed syntax: 2x35="value"
1866
+ if self._check(TokenType.IDENTIFIER) and ident.isdigit():
1867
+ # Pattern like 2x35 (position 2, repeat 35)
1868
+ second = self._advance().value
1869
+ if self._match(TokenType.EQUALS):
1870
+ match_value = self._parse_expression()
1871
+ pattern.append({'type': 'repeat', 'pos': int(ident), 'repeat': second, 'value': match_value})
1872
+ else:
1873
+ pattern.append({'type': 'repeat', 'pos': int(ident), 'repeat': second, 'value': None})
1874
+ else:
1875
+ pattern.append({'type': 'variable', 'name': ident})
1876
+ else:
1877
+ self._advance()
1878
+
1879
+ self._expect(TokenType.BLOCK_END)
1880
+
1881
+ # Parse case body - supports both:
1882
+ # case {pattern}: statement; (colon syntax)
1883
+ # case {pattern} { statements } (block syntax)
1884
+ body_children = []
1885
+ if self._match(TokenType.COLON):
1886
+ # Colon syntax: single statement or until next case/default/}
1887
+ stmt = self._parse_statement()
1888
+ if stmt:
1889
+ body_children.append(stmt)
1890
+ elif self._check(TokenType.BLOCK_START):
1891
+ self._advance() # consume {
1892
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1893
+ stmt = self._parse_statement()
1894
+ if stmt:
1895
+ body_children.append(stmt)
1896
+ self._expect(TokenType.BLOCK_END)
1897
+
1898
+ cases.append({'pattern': pattern, 'body': body_children})
1899
+
1900
+ # Parse default block - supports both:
1901
+ # default: statement; (colon syntax)
1902
+ # default { statements } (block syntax)
1903
+ elif self._match_keyword('default'):
1904
+ body_children = []
1905
+ if self._match(TokenType.COLON):
1906
+ # Colon syntax: single statement
1907
+ stmt = self._parse_statement()
1908
+ if stmt:
1909
+ body_children.append(stmt)
1910
+ elif self._check(TokenType.BLOCK_START):
1911
+ self._advance() # consume {
1912
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1913
+ stmt = self._parse_statement()
1914
+ if stmt:
1915
+ body_children.append(stmt)
1916
+ self._expect(TokenType.BLOCK_END)
1917
+ default_block = body_children
1918
+
1919
+ # Parse function reference: &funcName;
1920
+ elif self._check(TokenType.AMPERSAND):
1921
+ self._advance() # consume &
1922
+ if self._check(TokenType.IDENTIFIER):
1923
+ func_name = self._advance().value
1924
+ func_refs.append({
1925
+ 'position': len(func_refs),
1926
+ 'hex_pos': f"0x{len(func_refs):x}",
1927
+ 'func_ref': func_name
1928
+ })
1929
+ self._match(TokenType.SEMICOLON)
1930
+
1931
+ else:
1932
+ self._advance()
1933
+
1934
+ self._expect(TokenType.BLOCK_END)
1935
+
1936
+ return ASTNode('bytearrayed', value={
1937
+ 'name': bytearrayed_name,
1938
+ 'func_refs': func_refs,
1939
+ 'cases': cases,
1940
+ 'default': default_block
1941
+ })
1942
+
1943
+ def _parse_class(self, is_global: bool = False, is_embedded: bool = False) -> ASTNode:
1594
1944
  """Parse class declaration with members and methods.
1595
1945
 
1596
1946
  Syntax:
@@ -1598,6 +1948,7 @@ class CSSLParser:
1598
1948
  global class ClassName { ... } // Global class
1599
1949
  class @ClassName { ... } // Global class (alternative)
1600
1950
  class *ClassName { ... } // Non-null class
1951
+ embedded class ClassName &$Target { ... } // Immediate replacement (v4.2.5)
1601
1952
 
1602
1953
  Non-null class (all methods return non-null):
1603
1954
  class *MyClass { ... }
@@ -1620,6 +1971,28 @@ class CSSLParser:
1620
1971
  class_params = self._parse_parameter_list()
1621
1972
  self._expect(TokenType.PAREN_END)
1622
1973
 
1974
+ # v4.2.5: Check for &target reference for class replacement
1975
+ # Syntax: embedded class BetterGame() &$Game { ... }
1976
+ # class NewClass &OldClass { ... }
1977
+ append_ref_class = None
1978
+ append_ref_member = None
1979
+ if self._match(TokenType.AMPERSAND):
1980
+ if self._check(TokenType.IDENTIFIER):
1981
+ append_ref_class = self._advance().value
1982
+ elif self._check(TokenType.AT):
1983
+ self._advance()
1984
+ if self._check(TokenType.IDENTIFIER):
1985
+ append_ref_class = '@' + self._advance().value
1986
+ elif self._check(TokenType.SHARED_REF):
1987
+ append_ref_class = f'${self._advance().value}'
1988
+ else:
1989
+ raise CSSLSyntaxError("Expected class name after '&' in class reference")
1990
+
1991
+ # Check for ::member or .member (for targeting specific members)
1992
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1993
+ if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1994
+ append_ref_member = self._advance().value
1995
+
1623
1996
  # Check for inheritance and overwrites:
1624
1997
  # class Child : extends Parent { ... }
1625
1998
  # class Child : extends $PythonObject { ... }
@@ -1627,15 +2000,22 @@ class CSSLParser:
1627
2000
  # class Child : extends Parent (param1, param2) { ... } <- constructor args for parent
1628
2001
  extends_class = None
1629
2002
  extends_is_python = False
2003
+ extends_lang_ref = None # v4.1.0: Cross-language inheritance (cpp$ClassName)
1630
2004
  extends_args = []
1631
2005
  overwrites_class = None
1632
2006
  overwrites_is_python = False
2007
+ supports_language = None # v4.1.0: Multi-language syntax support
1633
2008
 
1634
2009
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1635
2010
  # Parse extends and/or overwrites (can be chained with : or ::)
1636
2011
  while True:
1637
2012
  if self._match_keyword('extends'):
1638
- if self._check(TokenType.IDENTIFIER):
2013
+ # v4.1.0: Check for cross-language inheritance: extends cpp$ClassName
2014
+ if self._check(TokenType.LANG_INSTANCE_REF):
2015
+ ref = self._advance().value
2016
+ extends_lang_ref = ref # {'lang': 'cpp', 'instance': 'ClassName'}
2017
+ extends_class = ref['instance']
2018
+ elif self._check(TokenType.IDENTIFIER):
1639
2019
  extends_class = self._advance().value
1640
2020
  elif self._check(TokenType.SHARED_REF):
1641
2021
  extends_class = self._advance().value
@@ -1660,23 +2040,62 @@ class CSSLParser:
1660
2040
  # Skip optional () after class name
1661
2041
  if self._match(TokenType.PAREN_START):
1662
2042
  self._expect(TokenType.PAREN_END)
2043
+ # v4.1.0: Parse 'supports' keyword for multi-language syntax
2044
+ elif self._match_keyword('supports'):
2045
+ if self._check(TokenType.AT):
2046
+ self._advance() # consume @
2047
+ if self._check(TokenType.IDENTIFIER):
2048
+ supports_language = '@' + self._advance().value
2049
+ else:
2050
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
2051
+ elif self._check(TokenType.IDENTIFIER):
2052
+ supports_language = self._advance().value
2053
+ else:
2054
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
1663
2055
  else:
1664
- raise CSSLSyntaxError("Expected 'extends' or 'overwrites' after ':' or '::' in class declaration")
2056
+ raise CSSLSyntaxError("Expected 'extends', 'overwrites', or 'supports' after ':' or '::' in class declaration")
1665
2057
  # Check for another : or :: for chaining
1666
2058
  if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
1667
2059
  break
1668
2060
 
2061
+ # v4.2.0: If supports_language is set, capture raw body for runtime transformation
2062
+ raw_body = None
2063
+ if supports_language:
2064
+ raw_body = self._extract_raw_block_body()
2065
+
1669
2066
  node = ASTNode('class', value={
1670
2067
  'name': class_name,
1671
2068
  'is_global': is_global,
2069
+ 'is_embedded': is_embedded, # v4.2.5: immediate &target replacement
1672
2070
  'non_null': non_null,
1673
2071
  'class_params': class_params,
1674
2072
  'extends': extends_class,
1675
2073
  'extends_is_python': extends_is_python,
2074
+ 'extends_lang_ref': extends_lang_ref, # v4.1.0
1676
2075
  'extends_args': extends_args,
1677
2076
  'overwrites': overwrites_class,
1678
- 'overwrites_is_python': overwrites_is_python
2077
+ 'overwrites_is_python': overwrites_is_python,
2078
+ 'supports_language': supports_language, # v4.1.0
2079
+ 'raw_body': raw_body, # v4.2.0: Raw body for language transformation
2080
+ 'append_ref_class': append_ref_class, # v4.2.5: &target class reference
2081
+ 'append_ref_member': append_ref_member # v4.2.5: &target member reference
1679
2082
  }, children=[])
2083
+
2084
+ # v4.2.0: If we have raw_body for language transformation, skip regular parsing
2085
+ if raw_body is not None:
2086
+ # Skip the block entirely - runtime will transform and parse
2087
+ self._expect(TokenType.BLOCK_START)
2088
+ brace_count = 1
2089
+ while brace_count > 0 and not self._is_at_end():
2090
+ if self._check(TokenType.BLOCK_START):
2091
+ brace_count += 1
2092
+ elif self._check(TokenType.BLOCK_END):
2093
+ brace_count -= 1
2094
+ if brace_count > 0:
2095
+ self._advance()
2096
+ self._expect(TokenType.BLOCK_END)
2097
+ return node
2098
+
1680
2099
  self._expect(TokenType.BLOCK_START)
1681
2100
 
1682
2101
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
@@ -1907,13 +2326,14 @@ class CSSLParser:
1907
2326
  """Parse define function declaration.
1908
2327
 
1909
2328
  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
2329
+ define MyFunc(args) { } // Local function
2330
+ global define MyFunc(args) { } // Global function
2331
+ define @MyFunc(args) { } // Global function (alternative)
2332
+ define *MyFunc(args) { } // Non-null: must never return None
2333
+ define MyFunc(args) : extends OtherFunc { } // Inherit local vars
2334
+ define MyFunc(args) : overwrites OtherFunc { } // Replace OtherFunc
2335
+ define MyFunc(args) : supports python { } // Multi-language syntax
2336
+ define MyFunc(args) :: extends Parent::Method { } // Method-level inheritance
1917
2337
  """
1918
2338
  # Check for * prefix (non-null function - must return non-null)
1919
2339
  # Also *[type] for type exclusion (must NOT return that type)
@@ -1935,8 +2355,56 @@ class CSSLParser:
1935
2355
 
1936
2356
  name = self._advance().value
1937
2357
 
1938
- # Check for extends/overwrites: define func : extends/overwrites target() { }
1939
- # Also supports method-level :: syntax: define func :: extends Parent::method
2358
+ # Parse parameters FIRST (before :extends/:overwrites/:supports)
2359
+ # Syntax: define funcName(params) : extends/overwrites/supports { }
2360
+ params = []
2361
+
2362
+ if self._match(TokenType.PAREN_START):
2363
+ while not self._check(TokenType.PAREN_END):
2364
+ param_info = {}
2365
+ # Handle 'open' keyword for open parameters
2366
+ if self._match_keyword('open'):
2367
+ param_info['open'] = True
2368
+ # Handle type annotations (e.g., string, int, dynamic, etc.)
2369
+ if self._check(TokenType.KEYWORD):
2370
+ param_info['type'] = self._advance().value
2371
+ # Handle reference operator &
2372
+ if self._match(TokenType.AMPERSAND):
2373
+ param_info['ref'] = True
2374
+ # Handle * prefix for non-null parameters
2375
+ if self._match(TokenType.MULTIPLY):
2376
+ param_info['non_null'] = True
2377
+ # Get parameter name
2378
+ if self._check(TokenType.IDENTIFIER):
2379
+ param_name = self._advance().value
2380
+ # v4.2.0: Handle default parameter values (param = value)
2381
+ if self._match(TokenType.EQUALS):
2382
+ default_value = self._parse_expression()
2383
+ param_info['default'] = default_value
2384
+ if param_info:
2385
+ params.append({'name': param_name, **param_info})
2386
+ else:
2387
+ params.append(param_name)
2388
+ self._match(TokenType.COMMA)
2389
+ elif self._check(TokenType.KEYWORD):
2390
+ # Parameter name could be a keyword like 'Params'
2391
+ param_name = self._advance().value
2392
+ # v4.2.0: Handle default parameter values (param = value)
2393
+ if self._match(TokenType.EQUALS):
2394
+ default_value = self._parse_expression()
2395
+ param_info['default'] = default_value
2396
+ if param_info:
2397
+ params.append({'name': param_name, **param_info})
2398
+ else:
2399
+ params.append(param_name)
2400
+ self._match(TokenType.COMMA)
2401
+ else:
2402
+ break
2403
+ self._expect(TokenType.PAREN_END)
2404
+
2405
+ # Check for extends/overwrites/supports AFTER parameters
2406
+ # Syntax: define func(params) : extends/overwrites target { }
2407
+ # Also supports method-level :: syntax: define func() :: extends Parent::method
1940
2408
  extends_func = None
1941
2409
  overwrites_func = None
1942
2410
  extends_is_python = False
@@ -1945,6 +2413,7 @@ class CSSLParser:
1945
2413
  extends_method_ref = None
1946
2414
  overwrites_class_ref = None
1947
2415
  overwrites_method_ref = None
2416
+ supports_language = None # v4.1.0: Multi-language syntax support
1948
2417
 
1949
2418
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1950
2419
  # Parse extends and/or overwrites (supports :: method-level syntax)
@@ -2005,49 +2474,24 @@ class CSSLParser:
2005
2474
  # Skip optional () after function/method name
2006
2475
  if self._match(TokenType.PAREN_START):
2007
2476
  self._expect(TokenType.PAREN_END)
2477
+ # v4.1.0: Parse 'supports' keyword for multi-language syntax
2478
+ elif self._match_keyword('supports'):
2479
+ if self._check(TokenType.AT):
2480
+ self._advance() # consume @
2481
+ if self._check(TokenType.IDENTIFIER):
2482
+ supports_language = '@' + self._advance().value
2483
+ else:
2484
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
2485
+ elif self._check(TokenType.IDENTIFIER):
2486
+ supports_language = self._advance().value
2487
+ else:
2488
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
2008
2489
  else:
2009
2490
  break
2010
2491
  # Check for another :: or : for chaining extends/overwrites
2011
2492
  if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
2012
2493
  break
2013
2494
 
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
2495
  # New: Append mode and reference tracking for functions
2052
2496
  # Syntax: define XYZ(int zahl) &overwrittenclass::functionyouwanttokeep ++ { ... }
2053
2497
  append_mode = False
@@ -2074,9 +2518,41 @@ class CSSLParser:
2074
2518
  if self._match(TokenType.PLUS_PLUS):
2075
2519
  append_mode = True
2076
2520
 
2521
+ # v4.2.0: Allow 'supports' AFTER &Class::member reference
2522
+ # Syntax: define func() &$pyclass::method : supports python { }
2523
+ if self._match(TokenType.COLON) or self._match(TokenType.DOUBLE_COLON):
2524
+ if self._match_keyword('supports'):
2525
+ if self._check(TokenType.AT):
2526
+ self._advance()
2527
+ if self._check(TokenType.IDENTIFIER):
2528
+ supports_language = '@' + self._advance().value
2529
+ else:
2530
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
2531
+ elif self._check(TokenType.IDENTIFIER):
2532
+ supports_language = self._advance().value
2533
+ else:
2534
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
2535
+
2536
+ self._expect(TokenType.BLOCK_START)
2537
+
2538
+ # v4.2.0: Extract raw body when supports_language is set for transformation
2539
+ raw_body = None
2540
+ children = []
2541
+ if supports_language:
2542
+ raw_body = self._extract_raw_block_body()
2543
+ # _extract_raw_block_body positions cursor at BLOCK_END
2544
+ else:
2545
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
2546
+ stmt = self._parse_statement()
2547
+ if stmt:
2548
+ children.append(stmt)
2549
+
2550
+ self._expect(TokenType.BLOCK_END)
2551
+
2077
2552
  node = ASTNode('function', value={
2078
2553
  'name': name,
2079
2554
  'is_global': is_global,
2555
+ 'is_embedded': False, # v4.2.5: define uses delayed &target replacement
2080
2556
  'params': params,
2081
2557
  'non_null': non_null,
2082
2558
  'exclude_type': exclude_type, # *[type] - must NOT return this type
@@ -2092,16 +2568,13 @@ class CSSLParser:
2092
2568
  # New append mode fields
2093
2569
  'append_mode': append_mode,
2094
2570
  '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)
2571
+ 'append_ref_member': append_ref_member,
2572
+ # v4.1.0: Multi-language support
2573
+ 'supports_language': supports_language,
2574
+ # v4.2.0: Raw body for language transformation
2575
+ 'raw_body': raw_body
2576
+ }, children=children)
2103
2577
 
2104
- self._expect(TokenType.BLOCK_END)
2105
2578
  return node
2106
2579
 
2107
2580
  def _parse_statement(self) -> Optional[ASTNode]:
@@ -2127,6 +2600,9 @@ class CSSLParser:
2127
2600
  return self._parse_try()
2128
2601
  elif self._match_keyword('await'):
2129
2602
  return self._parse_await()
2603
+ elif self._match_keyword('supports'):
2604
+ # v4.2.0: Standalone supports block for multi-language syntax
2605
+ return self._parse_supports_block()
2130
2606
  elif self._match_keyword('define'):
2131
2607
  # Nested define function
2132
2608
  return self._parse_define()
@@ -2144,9 +2620,11 @@ class CSSLParser:
2144
2620
  self._peek(1).type == TokenType.DOUBLE_COLON)):
2145
2621
  # super() or super::method() call - calls parent constructor/method
2146
2622
  return self._parse_super_call()
2623
+ # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
2147
2624
  elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
2148
2625
  self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
2149
2626
  self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
2627
+ self._check(TokenType.LANG_INSTANCE_REF) or
2150
2628
  (self._check(TokenType.KEYWORD) and self._current().value in ('this', 'new')) or
2151
2629
  self._looks_like_namespace_call()):
2152
2630
  return self._parse_expression_statement()
@@ -2352,42 +2830,40 @@ class CSSLParser:
2352
2830
 
2353
2831
  Supports: i = i + 1, i++, ++i, i += 1, i -= 1
2354
2832
  """
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})
2833
+ # Check for prefix increment/decrement: ++i or --i (as single PLUS_PLUS/MINUS_MINUS token)
2834
+ if self._check(TokenType.PLUS_PLUS):
2835
+ self._advance()
2836
+ var_name = self._advance().value
2837
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2838
+ elif self._check(TokenType.MINUS_MINUS):
2839
+ self._advance()
2840
+ var_name = self._advance().value
2841
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2364
2842
 
2365
2843
  # Regular variable assignment or postfix
2366
2844
  var_name = self._advance().value
2367
2845
 
2368
- # Check for postfix increment/decrement: i++ or i--
2369
- if self._check(TokenType.PLUS):
2846
+ # Check for postfix increment/decrement: i++ or i-- (as single PLUS_PLUS/MINUS_MINUS token)
2847
+ if self._check(TokenType.PLUS_PLUS):
2848
+ self._advance()
2849
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2850
+ elif self._check(TokenType.MINUS_MINUS):
2851
+ self._advance()
2852
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2853
+ # i += value
2854
+ elif self._check(TokenType.PLUS):
2370
2855
  self._advance()
2371
- if self._check(TokenType.PLUS):
2856
+ if self._check(TokenType.EQUALS):
2372
2857
  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})
2858
+ value = self._parse_expression()
2859
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
2860
+ # i -= value
2380
2861
  elif self._check(TokenType.MINUS):
2381
2862
  self._advance()
2382
- if self._check(TokenType.MINUS):
2863
+ if self._check(TokenType.EQUALS):
2383
2864
  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})
2865
+ value = self._parse_expression()
2866
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
2391
2867
 
2392
2868
  # Regular assignment: i = expression
2393
2869
  if self._check(TokenType.EQUALS):
@@ -2512,6 +2988,14 @@ class CSSLParser:
2512
2988
  value = self._parse_expression()
2513
2989
  self._expect(TokenType.PAREN_END)
2514
2990
 
2991
+ # v4.2.5: Check if this is a param switch (switch on open params identifier)
2992
+ # Syntax: switch(Params): case name: ... except age: ... always: ... finally: ...
2993
+ is_param_switch = (value.type == 'identifier')
2994
+
2995
+ if is_param_switch:
2996
+ return self._parse_param_switch(value)
2997
+
2998
+ # Regular switch
2515
2999
  node = ASTNode('switch', value={'value': value}, children=[])
2516
3000
  self._expect(TokenType.BLOCK_START)
2517
3001
 
@@ -2545,6 +3029,139 @@ class CSSLParser:
2545
3029
  self._expect(TokenType.BLOCK_END)
2546
3030
  return node
2547
3031
 
3032
+ def _parse_param_switch(self, params_identifier: ASTNode) -> ASTNode:
3033
+ """Parse param switch for open parameters.
3034
+
3035
+ v4.2.5: Switch on which parameters were provided.
3036
+
3037
+ Syntax:
3038
+ switch(Params): or switch(Params) {
3039
+ case name: // if 'name' param exists
3040
+ ...
3041
+ break;
3042
+ case name & age: // if both 'name' AND 'age' exist
3043
+ ...
3044
+ break;
3045
+ case name & not age: // if 'name' exists but 'age' doesn't
3046
+ ...
3047
+ break;
3048
+ except name: // if 'name' does NOT exist (alias for 'case not name')
3049
+ ...
3050
+ break;
3051
+ default: // fallback if no case matches
3052
+ ...
3053
+ always: // always runs after a matching case
3054
+ ...
3055
+ finally: // cleanup, runs last regardless
3056
+ ...
3057
+ }
3058
+ """
3059
+ # Allow both : and { as block start
3060
+ if self._match(TokenType.COLON):
3061
+ pass # Python-style with :
3062
+ self._expect(TokenType.BLOCK_START)
3063
+
3064
+ node = ASTNode('param_switch', value={
3065
+ 'params': params_identifier.value # The open params variable name
3066
+ }, children=[])
3067
+
3068
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3069
+ if self._match_keyword('case'):
3070
+ # Parse param condition: name, name & age, name & not age
3071
+ condition = self._parse_param_condition()
3072
+ self._expect(TokenType.COLON)
3073
+
3074
+ case_node = ASTNode('param_case', value={'condition': condition}, children=[])
3075
+ self._parse_case_body(case_node)
3076
+ node.children.append(case_node)
3077
+
3078
+ elif self._match_keyword('except'):
3079
+ # except name: is alias for case not name:
3080
+ param_name = self._advance().value
3081
+ self._expect(TokenType.COLON)
3082
+
3083
+ condition = {'type': 'not', 'param': param_name}
3084
+ case_node = ASTNode('param_case', value={'condition': condition}, children=[])
3085
+ self._parse_case_body(case_node)
3086
+ node.children.append(case_node)
3087
+
3088
+ elif self._match_keyword('default'):
3089
+ self._expect(TokenType.COLON)
3090
+ default_node = ASTNode('param_default', children=[])
3091
+ self._parse_case_body(default_node)
3092
+ node.children.append(default_node)
3093
+
3094
+ elif self._match_keyword('always'):
3095
+ self._expect(TokenType.COLON)
3096
+ always_node = ASTNode('param_always', children=[])
3097
+ self._parse_case_body(always_node)
3098
+ node.children.append(always_node)
3099
+
3100
+ elif self._match_keyword('finally'):
3101
+ self._expect(TokenType.COLON)
3102
+ finally_node = ASTNode('param_finally', children=[])
3103
+ self._parse_case_body(finally_node)
3104
+ node.children.append(finally_node)
3105
+
3106
+ else:
3107
+ self._advance()
3108
+
3109
+ self._expect(TokenType.BLOCK_END)
3110
+ return node
3111
+
3112
+ def _parse_param_condition(self) -> dict:
3113
+ """Parse param switch condition.
3114
+
3115
+ Syntax:
3116
+ name -> {'type': 'exists', 'param': 'name'}
3117
+ not name -> {'type': 'not', 'param': 'name'}
3118
+ name & age -> {'type': 'and', 'left': {...}, 'right': {...}}
3119
+ name & not age -> {'type': 'and', 'left': {...}, 'right': {'type': 'not', ...}}
3120
+ """
3121
+ # Check for 'not' prefix
3122
+ negated = self._match_keyword('not')
3123
+ param_name = self._advance().value
3124
+
3125
+ if negated:
3126
+ condition = {'type': 'not', 'param': param_name}
3127
+ else:
3128
+ condition = {'type': 'exists', 'param': param_name}
3129
+
3130
+ # Check for & (AND) combinations
3131
+ while self._match(TokenType.AMPERSAND):
3132
+ # Parse right side
3133
+ right_negated = self._match_keyword('not')
3134
+ right_param = self._advance().value
3135
+
3136
+ if right_negated:
3137
+ right_condition = {'type': 'not', 'param': right_param}
3138
+ else:
3139
+ right_condition = {'type': 'exists', 'param': right_param}
3140
+
3141
+ condition = {'type': 'and', 'left': condition, 'right': right_condition}
3142
+
3143
+ return condition
3144
+
3145
+ def _parse_case_body(self, case_node: ASTNode):
3146
+ """Parse the body of a case/except/default/always/finally block."""
3147
+ stop_keywords = ['case', 'except', 'default', 'always', 'finally']
3148
+
3149
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3150
+ # Check if we hit another case keyword
3151
+ if any(self._check_keyword(kw) for kw in stop_keywords):
3152
+ break
3153
+
3154
+ stmt = self._parse_statement()
3155
+ if stmt:
3156
+ case_node.children.append(stmt)
3157
+
3158
+ # Check for break
3159
+ if self._check_keyword('break'):
3160
+ break_stmt = self._parse_statement()
3161
+ if break_stmt:
3162
+ case_node.children.append(break_stmt)
3163
+ break
3164
+
2548
3165
  def _parse_return(self) -> ASTNode:
2549
3166
  """Parse return statement, supporting multiple values for shuffled functions.
2550
3167
 
@@ -2609,6 +3226,9 @@ class CSSLParser:
2609
3226
  self._expect(TokenType.BLOCK_END)
2610
3227
  node.children.append(try_block)
2611
3228
 
3229
+ # v4.2.6: Skip optional semicolon between try block and catch
3230
+ self._match(TokenType.SEMICOLON)
3231
+
2612
3232
  if self._match_keyword('catch'):
2613
3233
  error_var = None
2614
3234
  if self._match(TokenType.PAREN_START):
@@ -2624,6 +3244,20 @@ class CSSLParser:
2624
3244
  self._expect(TokenType.BLOCK_END)
2625
3245
  node.children.append(catch_block)
2626
3246
 
3247
+ # v4.2.6: Skip optional semicolon between catch and finally
3248
+ self._match(TokenType.SEMICOLON)
3249
+
3250
+ # v4.2.6: Add finally support
3251
+ if self._match_keyword('finally'):
3252
+ finally_block = ASTNode('finally-block', children=[])
3253
+ self._expect(TokenType.BLOCK_START)
3254
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3255
+ stmt = self._parse_statement()
3256
+ if stmt:
3257
+ finally_block.children.append(stmt)
3258
+ self._expect(TokenType.BLOCK_END)
3259
+ node.children.append(finally_block)
3260
+
2627
3261
  return node
2628
3262
 
2629
3263
  def _parse_await(self) -> ASTNode:
@@ -2632,6 +3266,163 @@ class CSSLParser:
2632
3266
  self._match(TokenType.SEMICOLON)
2633
3267
  return ASTNode('await', value=expr)
2634
3268
 
3269
+ def _parse_supports_block(self) -> ASTNode:
3270
+ """Parse standalone supports block for multi-language syntax.
3271
+
3272
+ v4.2.0: Allows 'supports' to be used anywhere, not just in class/function.
3273
+
3274
+ Syntax:
3275
+ supports py { } // Python syntax block
3276
+ supports @py { } // With @ prefix
3277
+ supports python { } // Full language name
3278
+ supports cpp { } // C++ syntax block
3279
+ supports javascript { } // JavaScript syntax block
3280
+
3281
+ Example:
3282
+ supports py {
3283
+ for i in range(10):
3284
+ print(i)
3285
+ }
3286
+
3287
+ supports cpp {
3288
+ std::cout << "Hello" << std::endl;
3289
+ int x = 42;
3290
+ }
3291
+ """
3292
+ # Parse language identifier
3293
+ language = None
3294
+ if self._check(TokenType.AT):
3295
+ self._advance()
3296
+ if self._check(TokenType.IDENTIFIER):
3297
+ language = '@' + self._advance().value
3298
+ else:
3299
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
3300
+ elif self._check(TokenType.IDENTIFIER):
3301
+ language = self._advance().value
3302
+ else:
3303
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
3304
+
3305
+ # Extract raw block source for language transformation (preserves indentation)
3306
+ raw_source = None
3307
+ if self._check(TokenType.BLOCK_START):
3308
+ raw_source = self._extract_raw_block_body()
3309
+
3310
+ # Skip parsing body if we have raw_source - runtime will transform and parse
3311
+ body = []
3312
+ self._expect(TokenType.BLOCK_START)
3313
+ if raw_source is None:
3314
+ # No raw source (e.g., already CSSL syntax), parse normally
3315
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3316
+ stmt = self._parse_statement()
3317
+ if stmt:
3318
+ body.append(stmt)
3319
+ else:
3320
+ # Skip the block - runtime will transform and parse
3321
+ brace_count = 1
3322
+ while brace_count > 0 and not self._is_at_end():
3323
+ if self._check(TokenType.BLOCK_START):
3324
+ brace_count += 1
3325
+ elif self._check(TokenType.BLOCK_END):
3326
+ brace_count -= 1
3327
+ if brace_count > 0:
3328
+ self._advance()
3329
+ self._expect(TokenType.BLOCK_END)
3330
+
3331
+ return ASTNode('supports_block', value={
3332
+ 'language': language,
3333
+ 'raw_source': raw_source
3334
+ }, children=body)
3335
+
3336
+ def _extract_raw_block_source(self) -> Optional[str]:
3337
+ """Extract raw source code from a {} block before parsing.
3338
+
3339
+ Used for 'supports' blocks to allow language transformation.
3340
+ """
3341
+ if not self._check(TokenType.BLOCK_START):
3342
+ return None
3343
+
3344
+ # Find the matching block end by counting braces
3345
+ start_pos = self.pos
3346
+ brace_count = 0
3347
+ found_start = False
3348
+
3349
+ # Walk through tokens to find matching close brace
3350
+ temp_pos = self.pos
3351
+ while temp_pos < len(self.tokens):
3352
+ token = self.tokens[temp_pos]
3353
+ if token.type == TokenType.BLOCK_START:
3354
+ if not found_start:
3355
+ found_start = True
3356
+ brace_count += 1
3357
+ elif token.type == TokenType.BLOCK_END:
3358
+ brace_count -= 1
3359
+ if brace_count == 0:
3360
+ break
3361
+ temp_pos += 1
3362
+
3363
+ # Build source from tokens between braces (excluding braces)
3364
+ source_parts = []
3365
+ for i in range(start_pos + 1, temp_pos):
3366
+ token = self.tokens[i]
3367
+ if token.type == TokenType.STRING:
3368
+ source_parts.append(f'"{token.value}"')
3369
+ elif token.type == TokenType.NEWLINE:
3370
+ source_parts.append('\n')
3371
+ else:
3372
+ source_parts.append(str(token.value))
3373
+
3374
+ return ' '.join(source_parts)
3375
+
3376
+ def _extract_raw_block_body(self) -> Optional[str]:
3377
+ """Extract raw source code body from a {} block for language transformation.
3378
+
3379
+ v4.2.0: Used for 'supports' blocks to preserve original source (including indentation).
3380
+ This extracts the raw text between { and } from the original source string.
3381
+
3382
+ Returns the raw body content without the surrounding braces.
3383
+ """
3384
+ if not self._check(TokenType.BLOCK_START):
3385
+ return None
3386
+
3387
+ # Get the { token's position
3388
+ start_token = self._current()
3389
+ start_line = start_token.line
3390
+ start_col = start_token.column
3391
+
3392
+ # Find the { character position in source
3393
+ # Line numbers are 1-indexed, columns are 1-indexed
3394
+ brace_start_pos = 0
3395
+ current_line = 1
3396
+ for i, char in enumerate(self.source):
3397
+ if current_line == start_line:
3398
+ # Found the right line, now find the column
3399
+ col_in_line = i - brace_start_pos + 1
3400
+ if col_in_line >= start_col:
3401
+ # Search for { from here
3402
+ for j in range(i, len(self.source)):
3403
+ if self.source[j] == '{':
3404
+ brace_start_pos = j
3405
+ break
3406
+ break
3407
+ if char == '\n':
3408
+ current_line += 1
3409
+ brace_start_pos = i + 1
3410
+
3411
+ # Now find the matching closing brace
3412
+ brace_count = 1
3413
+ pos = brace_start_pos + 1
3414
+ while pos < len(self.source) and brace_count > 0:
3415
+ char = self.source[pos]
3416
+ if char == '{':
3417
+ brace_count += 1
3418
+ elif char == '}':
3419
+ brace_count -= 1
3420
+ pos += 1
3421
+
3422
+ # Extract the body (everything between { and })
3423
+ body = self.source[brace_start_pos + 1:pos - 1]
3424
+ return body.strip()
3425
+
2635
3426
  def _parse_action_block(self) -> ASTNode:
2636
3427
  """Parse an action block { ... } containing statements for createcmd"""
2637
3428
  node = ASTNode('action_block', children=[])
@@ -2948,6 +3739,14 @@ class CSSLParser:
2948
3739
  if self._match(TokenType.MINUS):
2949
3740
  operand = self._parse_unary()
2950
3741
  return ASTNode('unary', value={'op': '-', 'operand': operand})
3742
+ # Prefix increment: ++i
3743
+ if self._match(TokenType.PLUS_PLUS):
3744
+ operand = self._parse_unary()
3745
+ return ASTNode('increment', value={'op': 'prefix', 'operand': operand})
3746
+ # Prefix decrement: --i
3747
+ if self._match(TokenType.MINUS_MINUS):
3748
+ operand = self._parse_unary()
3749
+ return ASTNode('decrement', value={'op': 'prefix', 'operand': operand})
2951
3750
  if self._match(TokenType.AMPERSAND):
2952
3751
  # Reference operator: &variable or &@module
2953
3752
  operand = self._parse_unary()
@@ -3009,7 +3808,7 @@ class CSSLParser:
3009
3808
  # Just 'this' keyword alone - return as identifier for now
3010
3809
  return ASTNode('identifier', value='this')
3011
3810
 
3012
- # Handle 'new ClassName(args)' or 'new @ClassName(args)' instantiation
3811
+ # Handle 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' instantiation
3013
3812
  if self._check(TokenType.KEYWORD) and self._current().value == 'new':
3014
3813
  self._advance() # consume 'new'
3015
3814
  # Check for @ prefix (global class reference)
@@ -3017,13 +3816,19 @@ class CSSLParser:
3017
3816
  if self._check(TokenType.AT):
3018
3817
  self._advance() # consume @
3019
3818
  is_global_ref = True
3020
- class_name = self._advance().value # get class name
3819
+ class_name = self._advance().value # get class name or namespace
3820
+ # v4.2.6: Handle Namespace::ClassName syntax
3821
+ namespace = None
3822
+ if self._check(TokenType.DOUBLE_COLON):
3823
+ self._advance() # consume ::
3824
+ namespace = class_name
3825
+ class_name = self._advance().value # get actual class name
3021
3826
  args = []
3022
3827
  kwargs = {}
3023
3828
  if self._match(TokenType.PAREN_START):
3024
3829
  args, kwargs = self._parse_call_arguments()
3025
3830
  self._expect(TokenType.PAREN_END)
3026
- node = ASTNode('new', value={'class': class_name, 'args': args, 'kwargs': kwargs, 'is_global_ref': is_global_ref})
3831
+ node = ASTNode('new', value={'class': class_name, 'namespace': namespace, 'args': args, 'kwargs': kwargs, 'is_global_ref': is_global_ref})
3027
3832
  # Continue to check for member access, calls on the new object
3028
3833
  while True:
3029
3834
  if self._match(TokenType.DOT):
@@ -3090,12 +3895,13 @@ class CSSLParser:
3090
3895
  token = self._advance()
3091
3896
  node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
3092
3897
  # Check for member access, calls, indexing - with kwargs support
3898
+ # Support both . and -> for member access
3093
3899
  while True:
3094
3900
  if self._match(TokenType.PAREN_START):
3095
3901
  args, kwargs = self._parse_call_arguments()
3096
3902
  self._expect(TokenType.PAREN_END)
3097
3903
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3098
- elif self._match(TokenType.DOT):
3904
+ elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
3099
3905
  member = self._advance().value
3100
3906
  node = ASTNode('member_access', value={'object': node, 'member': member})
3101
3907
  elif self._match(TokenType.BRACKET_START):
@@ -3111,12 +3917,14 @@ class CSSLParser:
3111
3917
  token = self._advance()
3112
3918
  node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
3113
3919
  # Check for member access, calls, indexing - with kwargs support
3920
+ # Support both . and -> for member access (like this->member)
3114
3921
  while True:
3115
3922
  if self._match(TokenType.PAREN_START):
3116
3923
  args, kwargs = self._parse_call_arguments()
3117
3924
  self._expect(TokenType.PAREN_END)
3118
3925
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3119
- elif self._match(TokenType.DOT):
3926
+ elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
3927
+ # Support both $obj.member and $obj->member syntax
3120
3928
  member = self._advance().value
3121
3929
  node = ASTNode('member_access', value={'object': node, 'member': member})
3122
3930
  elif self._match(TokenType.BRACKET_START):
@@ -3132,12 +3940,36 @@ class CSSLParser:
3132
3940
  token = self._advance()
3133
3941
  node = ASTNode('captured_ref', value=token.value, line=token.line, column=token.column)
3134
3942
  # Check for member access, calls, indexing - with kwargs support
3943
+ # Support both . and -> for member access
3135
3944
  while True:
3136
3945
  if self._match(TokenType.PAREN_START):
3137
3946
  args, kwargs = self._parse_call_arguments()
3138
3947
  self._expect(TokenType.PAREN_END)
3139
3948
  node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3140
- elif self._match(TokenType.DOT):
3949
+ elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
3950
+ member = self._advance().value
3951
+ node = ASTNode('member_access', value={'object': node, 'member': member})
3952
+ elif self._match(TokenType.BRACKET_START):
3953
+ index = self._parse_expression()
3954
+ self._expect(TokenType.BRACKET_END)
3955
+ node = ASTNode('index_access', value={'object': node, 'index': index})
3956
+ else:
3957
+ break
3958
+ return node
3959
+
3960
+ # v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
3961
+ if self._check(TokenType.LANG_INSTANCE_REF):
3962
+ token = self._advance()
3963
+ ref = token.value # {'lang': 'cpp', 'instance': 'ClassName'}
3964
+ node = ASTNode('lang_instance_ref', value=ref, line=token.line, column=token.column)
3965
+ # Check for member access, calls, indexing
3966
+ # Support both . and -> for member access
3967
+ while True:
3968
+ if self._match(TokenType.PAREN_START):
3969
+ args, kwargs = self._parse_call_arguments()
3970
+ self._expect(TokenType.PAREN_END)
3971
+ node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3972
+ elif self._match(TokenType.DOT) or self._match(TokenType.FLOW_RIGHT):
3141
3973
  member = self._advance().value
3142
3974
  node = ASTNode('member_access', value={'object': node, 'member': member})
3143
3975
  elif self._match(TokenType.BRACKET_START):
@@ -3366,23 +4198,24 @@ class CSSLParser:
3366
4198
 
3367
4199
  self._expect(TokenType.COMPARE_GT) # consume >
3368
4200
 
3369
- # Must be followed by ()
4201
+ # Optional () - for named parameter search, () is not required
4202
+ args = []
3370
4203
  if self._check(TokenType.PAREN_START):
3371
4204
  self._advance() # consume (
3372
- args = []
3373
4205
  while not self._check(TokenType.PAREN_END):
3374
4206
  args.append(self._parse_expression())
3375
4207
  if not self._check(TokenType.PAREN_END):
3376
4208
  self._expect(TokenType.COMMA)
3377
4209
  self._expect(TokenType.PAREN_END)
3378
4210
 
3379
- # Return as typed function call
3380
- return ASTNode('typed_call', value={
3381
- 'name': name,
3382
- 'type_param': type_param,
3383
- 'param_name': param_name, # Named parameter for OpenFind
3384
- 'args': args
3385
- })
4211
+ # Return as typed function call (works with or without ())
4212
+ # v4.2.5: OpenFind<type, "name"> now works without ()
4213
+ return ASTNode('typed_call', value={
4214
+ 'name': name,
4215
+ 'type_param': type_param,
4216
+ 'param_name': param_name, # Named parameter for OpenFind
4217
+ 'args': args
4218
+ })
3386
4219
 
3387
4220
  node = ASTNode('identifier', value=name)
3388
4221
 
@@ -3398,6 +4231,12 @@ class CSSLParser:
3398
4231
  index = self._parse_expression()
3399
4232
  self._expect(TokenType.BRACKET_END)
3400
4233
  node = ASTNode('index_access', value={'object': node, 'index': index})
4234
+ # Postfix increment: i++
4235
+ elif self._match(TokenType.PLUS_PLUS):
4236
+ node = ASTNode('increment', value={'op': 'postfix', 'operand': node})
4237
+ # Postfix decrement: i--
4238
+ elif self._match(TokenType.MINUS_MINUS):
4239
+ node = ASTNode('decrement', value={'op': 'postfix', 'operand': node})
3401
4240
  else:
3402
4241
  break
3403
4242
 
@@ -3437,9 +4276,11 @@ class CSSLParser:
3437
4276
 
3438
4277
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
3439
4278
  # Parse statement or expression
4279
+ # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements
3440
4280
  if (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
3441
4281
  self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
3442
4282
  self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
4283
+ self._check(TokenType.LANG_INSTANCE_REF) or
3443
4284
  self._check(TokenType.STRING) or self._check(TokenType.NUMBER) or
3444
4285
  self._check(TokenType.BOOLEAN) or self._check(TokenType.NULL) or
3445
4286
  self._check(TokenType.PAREN_START)):
@@ -3508,7 +4349,7 @@ def parse_cssl(source: str) -> ASTNode:
3508
4349
  """Parse CSSL source code into an AST - auto-detects service vs program format"""
3509
4350
  lexer = CSSLLexer(source)
3510
4351
  tokens = lexer.tokenize()
3511
- parser = CSSLParser(tokens, lexer.source_lines)
4352
+ parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
3512
4353
 
3513
4354
  # Auto-detect: if first token is '{', it's a service file
3514
4355
  # Otherwise treat as standalone program (whitespace is already filtered by lexer)
@@ -3522,7 +4363,7 @@ def parse_cssl_program(source: str) -> ASTNode:
3522
4363
  """Parse standalone CSSL program (no service wrapper) into an AST"""
3523
4364
  lexer = CSSLLexer(source)
3524
4365
  tokens = lexer.tokenize()
3525
- parser = CSSLParser(tokens, lexer.source_lines)
4366
+ parser = CSSLParser(tokens, lexer.source_lines, source) # v4.2.0: Pass source for raw extraction
3526
4367
  return parser.parse_program()
3527
4368
 
3528
4369