IncludeCPP 4.2.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.
@@ -119,11 +119,12 @@ class TokenType(Enum):
119
119
 
120
120
  KEYWORDS = {
121
121
  # Service structure
122
- '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',
123
123
  # Control flow
124
124
  'if', 'else', 'elif', 'while', 'for', 'foreach', 'in', 'range',
125
125
  'switch', 'case', 'default', 'break', 'continue', 'return',
126
126
  'try', 'catch', 'finally', 'throw',
127
+ 'except', 'always', # v4.2.5: param switch keywords
127
128
  # Literals
128
129
  'True', 'False', 'null', 'None', 'true', 'false',
129
130
  # Logical operators
@@ -141,6 +142,7 @@ KEYWORDS = {
141
142
  'datastruct', # Universal container (lazy declarator)
142
143
  'dataspace', # SQL/data storage container
143
144
  'shuffled', # Unorganized fast storage (multiple returns)
145
+ 'bytearrayed', # Function-to-byte mapping with pattern matching (v4.2.5)
144
146
  'iterator', # Advanced iterator with tasks
145
147
  'combo', # Filter/search spaces
146
148
  'structure', # Advanced C++/Py Class
@@ -155,6 +157,7 @@ KEYWORDS = {
155
157
  'sqlbased', # SQL-based function
156
158
  'public', # Explicitly public (default)
157
159
  'static', # Static method/function
160
+ 'embedded', # Immediate &target replacement at registration (v4.2.5)
158
161
  # CSSL Include Keywords
159
162
  'include', 'get',
160
163
  # Multi-language support (v4.1.0)
@@ -164,7 +167,7 @@ KEYWORDS = {
164
167
  # Function modifiers that can appear in any order before function name
165
168
  FUNCTION_MODIFIERS = {
166
169
  'undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual',
167
- 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled'
170
+ 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled', 'embedded'
168
171
  }
169
172
 
170
173
  # Type literals that create empty instances
@@ -348,13 +351,16 @@ class CSSLLexer:
348
351
  self._add_token(TokenType.MULTIPLY, '*')
349
352
  self._advance()
350
353
  elif char == '/':
351
- # Check if this is a // comment (handled above) or division
352
- 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:
353
362
  self._add_token(TokenType.DIVIDE, '/')
354
363
  self._advance()
355
- else:
356
- # Already handled by // comment check above, but just in case
357
- self._skip_comment()
358
364
  elif char == '<':
359
365
  self._read_less_than()
360
366
  elif char == '>':
@@ -399,6 +405,26 @@ class CSSLLexer:
399
405
  while self.pos < len(self.source) and self.source[self.pos] != '\n':
400
406
  self._advance()
401
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
+
402
428
  def _read_string(self, quote_char: str):
403
429
  self._advance()
404
430
  start = self.pos
@@ -453,6 +479,19 @@ class CSSLLexer:
453
479
  start = self.pos
454
480
  if self.source[self.pos] == '-':
455
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
+
456
495
  while self.pos < len(self.source) and (self.source[self.pos].isdigit() or self.source[self.pos] == '.'):
457
496
  self._advance()
458
497
  value = self.source[start:self.pos]
@@ -766,6 +805,41 @@ class CSSLParser:
766
805
  'list', 'dictionary', 'dict', 'instance', 'map', 'openquote', 'parameter',
767
806
  'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
768
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
+
769
843
  def _looks_like_function_declaration(self) -> bool:
770
844
  """Check if current position looks like a C-style function declaration.
771
845
 
@@ -900,7 +974,7 @@ class CSSLParser:
900
974
  self.pos = saved_pos
901
975
  return False
902
976
 
903
- 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:
904
978
  """Parse C-style typed function declaration with flexible modifier ordering.
905
979
 
906
980
  Supports any order of modifiers, types, non-null (*), and global (@):
@@ -930,6 +1004,8 @@ class CSSLParser:
930
1004
  non_null = False
931
1005
  exclude_type = None
932
1006
  is_const = False
1007
+ # v4.2.5: embedded = immediate &target replacement (can come from param or modifier)
1008
+ _is_embedded = is_embedded
933
1009
 
934
1010
  # Phase 1: Collect all modifiers, type, non-null, and global indicators
935
1011
  # These can appear in any order before the function name
@@ -944,6 +1020,9 @@ class CSSLParser:
944
1020
  elif mod == 'const':
945
1021
  is_const = True
946
1022
  modifiers.append(mod)
1023
+ elif mod == 'embedded':
1024
+ _is_embedded = True
1025
+ modifiers.append(mod)
947
1026
  else:
948
1027
  modifiers.append(mod)
949
1028
  continue
@@ -1183,6 +1262,7 @@ class CSSLParser:
1183
1262
  'name': name,
1184
1263
  'is_global': is_global,
1185
1264
  'is_const': is_const,
1265
+ 'is_embedded': _is_embedded, # v4.2.5: immediate &target replacement
1186
1266
  'params': params,
1187
1267
  'return_type': return_type,
1188
1268
  'generic_type': generic_type,
@@ -1280,19 +1360,16 @@ class CSSLParser:
1280
1360
 
1281
1361
  The * prefix indicates a non-nullable variable (can never be None/null).
1282
1362
  Example: vector<dynamic> *MyVector - can never contain None values.
1363
+ Supports nested generics: datastruct<map<string, dynamic>> zipped;
1283
1364
  """
1284
1365
  # Get type name
1285
1366
  type_name = self._advance().value # Consume type keyword
1286
1367
 
1287
- # Check for generic type <T> or instance<"name">
1368
+ # Check for generic type <T> or instance<"name"> or nested <map<K,V>>
1288
1369
  element_type = None
1289
1370
  if self._match(TokenType.COMPARE_LT):
1290
- # For instance<"name">, element_type can be a string literal
1291
- if type_name == 'instance' and self._check(TokenType.STRING):
1292
- element_type = self._advance().value
1293
- elif self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
1294
- element_type = self._advance().value
1295
- self._expect(TokenType.COMPARE_GT)
1371
+ # Use helper to parse nested generic content
1372
+ element_type = self._parse_generic_type_content()
1296
1373
 
1297
1374
  # Check for * prefix (non-nullable indicator)
1298
1375
  non_null = False
@@ -1337,6 +1414,10 @@ class CSSLParser:
1337
1414
  root.children.append(self._parse_struct())
1338
1415
  elif self._match_keyword('class'):
1339
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())
1340
1421
  elif self._match_keyword('define'):
1341
1422
  root.children.append(self._parse_define())
1342
1423
  # Check for C-style typed function declarations
@@ -1359,6 +1440,14 @@ class CSSLParser:
1359
1440
  elif self._match_keyword('package-includes'):
1360
1441
  root.children.append(self._parse_package_includes())
1361
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'")
1362
1451
  elif self._match_keyword('global'):
1363
1452
  # Check if followed by class or define (global class/function)
1364
1453
  if self._match_keyword('class'):
@@ -1622,7 +1711,236 @@ class CSSLParser:
1622
1711
  self._expect(TokenType.BLOCK_END)
1623
1712
  return node
1624
1713
 
1625
- 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:
1626
1944
  """Parse class declaration with members and methods.
1627
1945
 
1628
1946
  Syntax:
@@ -1630,6 +1948,7 @@ class CSSLParser:
1630
1948
  global class ClassName { ... } // Global class
1631
1949
  class @ClassName { ... } // Global class (alternative)
1632
1950
  class *ClassName { ... } // Non-null class
1951
+ embedded class ClassName &$Target { ... } // Immediate replacement (v4.2.5)
1633
1952
 
1634
1953
  Non-null class (all methods return non-null):
1635
1954
  class *MyClass { ... }
@@ -1652,6 +1971,28 @@ class CSSLParser:
1652
1971
  class_params = self._parse_parameter_list()
1653
1972
  self._expect(TokenType.PAREN_END)
1654
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
+
1655
1996
  # Check for inheritance and overwrites:
1656
1997
  # class Child : extends Parent { ... }
1657
1998
  # class Child : extends $PythonObject { ... }
@@ -1725,6 +2066,7 @@ class CSSLParser:
1725
2066
  node = ASTNode('class', value={
1726
2067
  'name': class_name,
1727
2068
  'is_global': is_global,
2069
+ 'is_embedded': is_embedded, # v4.2.5: immediate &target replacement
1728
2070
  'non_null': non_null,
1729
2071
  'class_params': class_params,
1730
2072
  'extends': extends_class,
@@ -1734,7 +2076,9 @@ class CSSLParser:
1734
2076
  'overwrites': overwrites_class,
1735
2077
  'overwrites_is_python': overwrites_is_python,
1736
2078
  'supports_language': supports_language, # v4.1.0
1737
- 'raw_body': raw_body # v4.2.0: Raw body for language transformation
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
1738
2082
  }, children=[])
1739
2083
 
1740
2084
  # v4.2.0: If we have raw_body for language transformation, skip regular parsing
@@ -2208,6 +2552,7 @@ class CSSLParser:
2208
2552
  node = ASTNode('function', value={
2209
2553
  'name': name,
2210
2554
  'is_global': is_global,
2555
+ 'is_embedded': False, # v4.2.5: define uses delayed &target replacement
2211
2556
  'params': params,
2212
2557
  'non_null': non_null,
2213
2558
  'exclude_type': exclude_type, # *[type] - must NOT return this type
@@ -2643,6 +2988,14 @@ class CSSLParser:
2643
2988
  value = self._parse_expression()
2644
2989
  self._expect(TokenType.PAREN_END)
2645
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
2646
2999
  node = ASTNode('switch', value={'value': value}, children=[])
2647
3000
  self._expect(TokenType.BLOCK_START)
2648
3001
 
@@ -2676,6 +3029,139 @@ class CSSLParser:
2676
3029
  self._expect(TokenType.BLOCK_END)
2677
3030
  return node
2678
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
+
2679
3165
  def _parse_return(self) -> ASTNode:
2680
3166
  """Parse return statement, supporting multiple values for shuffled functions.
2681
3167
 
@@ -2740,6 +3226,9 @@ class CSSLParser:
2740
3226
  self._expect(TokenType.BLOCK_END)
2741
3227
  node.children.append(try_block)
2742
3228
 
3229
+ # v4.2.6: Skip optional semicolon between try block and catch
3230
+ self._match(TokenType.SEMICOLON)
3231
+
2743
3232
  if self._match_keyword('catch'):
2744
3233
  error_var = None
2745
3234
  if self._match(TokenType.PAREN_START):
@@ -2755,6 +3244,20 @@ class CSSLParser:
2755
3244
  self._expect(TokenType.BLOCK_END)
2756
3245
  node.children.append(catch_block)
2757
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
+
2758
3261
  return node
2759
3262
 
2760
3263
  def _parse_await(self) -> ASTNode:
@@ -3305,7 +3808,7 @@ class CSSLParser:
3305
3808
  # Just 'this' keyword alone - return as identifier for now
3306
3809
  return ASTNode('identifier', value='this')
3307
3810
 
3308
- # Handle 'new ClassName(args)' or 'new @ClassName(args)' instantiation
3811
+ # Handle 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' instantiation
3309
3812
  if self._check(TokenType.KEYWORD) and self._current().value == 'new':
3310
3813
  self._advance() # consume 'new'
3311
3814
  # Check for @ prefix (global class reference)
@@ -3313,13 +3816,19 @@ class CSSLParser:
3313
3816
  if self._check(TokenType.AT):
3314
3817
  self._advance() # consume @
3315
3818
  is_global_ref = True
3316
- 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
3317
3826
  args = []
3318
3827
  kwargs = {}
3319
3828
  if self._match(TokenType.PAREN_START):
3320
3829
  args, kwargs = self._parse_call_arguments()
3321
3830
  self._expect(TokenType.PAREN_END)
3322
- 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})
3323
3832
  # Continue to check for member access, calls on the new object
3324
3833
  while True:
3325
3834
  if self._match(TokenType.DOT):
@@ -3689,23 +4198,24 @@ class CSSLParser:
3689
4198
 
3690
4199
  self._expect(TokenType.COMPARE_GT) # consume >
3691
4200
 
3692
- # Must be followed by ()
4201
+ # Optional () - for named parameter search, () is not required
4202
+ args = []
3693
4203
  if self._check(TokenType.PAREN_START):
3694
4204
  self._advance() # consume (
3695
- args = []
3696
4205
  while not self._check(TokenType.PAREN_END):
3697
4206
  args.append(self._parse_expression())
3698
4207
  if not self._check(TokenType.PAREN_END):
3699
4208
  self._expect(TokenType.COMMA)
3700
4209
  self._expect(TokenType.PAREN_END)
3701
4210
 
3702
- # Return as typed function call
3703
- return ASTNode('typed_call', value={
3704
- 'name': name,
3705
- 'type_param': type_param,
3706
- 'param_name': param_name, # Named parameter for OpenFind
3707
- 'args': args
3708
- })
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
+ })
3709
4219
 
3710
4220
  node = ASTNode('identifier', value=name)
3711
4221