IncludeCPP 4.3.0__py3-none-any.whl → 4.6.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.
Files changed (30) hide show
  1. includecpp/CHANGELOG.md +22 -0
  2. includecpp/__init__.py +1 -1
  3. includecpp/__init__.pyi +1 -4
  4. includecpp/cli/commands.py +1218 -25
  5. includecpp/core/cpp_api_extensions.pyi +204 -200
  6. includecpp/core/cssl/__init__.py +317 -0
  7. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  8. includecpp/core/cssl/cpp/build/cssl_core.pyi +323 -0
  9. includecpp/core/cssl/cpp/build/libgcc_s_seh-1.dll +0 -0
  10. includecpp/core/cssl/cpp/build/libstdc++-6.dll +0 -0
  11. includecpp/core/cssl/cpp/build/libwinpthread-1.dll +0 -0
  12. includecpp/core/cssl/cpp/cssl_core.cp +108 -0
  13. includecpp/core/cssl/cpp/cssl_lexer.hpp +280 -0
  14. includecpp/core/cssl/cssl_builtins.py +142 -27
  15. includecpp/core/cssl/cssl_compiler.py +448 -0
  16. includecpp/core/cssl/cssl_optimizer.py +833 -0
  17. includecpp/core/cssl/cssl_parser.py +433 -38
  18. includecpp/core/cssl/cssl_runtime.py +294 -15
  19. includecpp/core/cssl/cssl_syntax.py +17 -0
  20. includecpp/core/cssl/cssl_types.py +143 -11
  21. includecpp/core/cssl_bridge.py +39 -2
  22. includecpp/generator/parser.cpp +38 -14
  23. includecpp/vscode/cssl/package.json +15 -0
  24. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +96 -0
  25. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/METADATA +1 -1
  26. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/RECORD +30 -21
  27. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/WHEEL +0 -0
  28. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/entry_points.txt +0 -0
  29. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/licenses/LICENSE +0 -0
  30. {includecpp-4.3.0.dist-info → includecpp-4.6.0.dist-info}/top_level.txt +0 -0
@@ -158,6 +158,7 @@ KEYWORDS = {
158
158
  'public', # Explicitly public (default)
159
159
  'static', # Static method/function
160
160
  'embedded', # Immediate &target replacement at registration (v4.2.5)
161
+ 'native', # Force C++ execution (no Python fallback)
161
162
  # CSSL Include Keywords
162
163
  'include', 'get',
163
164
  # Multi-language support (v4.1.0)
@@ -167,7 +168,8 @@ KEYWORDS = {
167
168
  # Function modifiers that can appear in any order before function name
168
169
  FUNCTION_MODIFIERS = {
169
170
  'undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual',
170
- 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled', 'embedded'
171
+ 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled', 'embedded',
172
+ 'native' # Force C++ execution
171
173
  }
172
174
 
173
175
  # Type literals that create empty instances
@@ -1280,7 +1282,8 @@ class CSSLParser:
1280
1282
  'append_mode': append_mode,
1281
1283
  'append_ref_class': append_ref_class,
1282
1284
  'append_ref_member': append_ref_member,
1283
- 'enforce_return_type': return_type is not None and 'meta' not in modifiers
1285
+ # v4.3.2: Also disable strict return type enforcement for 'shuffled' modifier (returns tuple)
1286
+ 'enforce_return_type': return_type is not None and 'meta' not in modifiers and 'shuffled' not in modifiers and return_type != 'shuffled'
1284
1287
  }, children=[])
1285
1288
 
1286
1289
  self._expect(TokenType.BLOCK_START)
@@ -1420,6 +1423,31 @@ class CSSLParser:
1420
1423
  root.children.append(self._parse_bytearrayed())
1421
1424
  elif self._match_keyword('define'):
1422
1425
  root.children.append(self._parse_define())
1426
+ # v4.5.1: Handle function modifiers (private, const, static, etc.) before define
1427
+ elif self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
1428
+ modifiers = []
1429
+ is_embedded = False
1430
+ is_global = False
1431
+ has_open_params = False
1432
+ while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
1433
+ mod = self._advance().value
1434
+ modifiers.append(mod)
1435
+ if mod == 'embedded':
1436
+ is_embedded = True
1437
+ elif mod == 'global':
1438
+ is_global = True
1439
+ elif mod == 'open':
1440
+ has_open_params = True
1441
+ # Now check what follows
1442
+ if self._match_keyword('define'):
1443
+ root.children.append(self._parse_define(
1444
+ is_global=is_global, is_embedded=is_embedded,
1445
+ has_open_params=has_open_params, modifiers=modifiers
1446
+ ))
1447
+ elif self._looks_like_function_declaration():
1448
+ root.children.append(self._parse_typed_function(modifiers=modifiers))
1449
+ else:
1450
+ self.error(f"Expected 'define' or function declaration after modifiers: {modifiers}")
1423
1451
  # Check for C-style typed function declarations
1424
1452
  elif self._looks_like_function_declaration():
1425
1453
  root.children.append(self._parse_typed_function())
@@ -1441,13 +1469,44 @@ class CSSLParser:
1441
1469
  root.children.append(self._parse_package_includes())
1442
1470
  # Handle global declarations
1443
1471
  # v4.2.5: Handle 'embedded' keyword for immediate &target replacement
1472
+ # v4.3.2: Extended to support enums: embedded EnumName &TargetEnum { ... }
1473
+ # v4.3.2: Support 'open embedded define' syntax
1474
+ elif self._match_keyword('open'):
1475
+ # open can be followed by embedded or define
1476
+ if self._match_keyword('embedded'):
1477
+ if self._match_keyword('define'):
1478
+ root.children.append(self._parse_define(is_embedded=True, has_open_params=True))
1479
+ elif self._looks_like_function_declaration():
1480
+ root.children.append(self._parse_typed_function(is_embedded=True, has_open_params=True))
1481
+ else:
1482
+ self.error("Expected 'define' or function declaration after 'open embedded'")
1483
+ elif self._match_keyword('define'):
1484
+ root.children.append(self._parse_define(has_open_params=True))
1485
+ else:
1486
+ self.error("Expected 'embedded' or 'define' after 'open'")
1444
1487
  elif self._match_keyword('embedded'):
1445
- if self._match_keyword('class'):
1488
+ # v4.3.2: Support both 'embedded open define' and 'embedded define'
1489
+ if self._match_keyword('open'):
1490
+ # embedded open define ...
1491
+ if self._match_keyword('define'):
1492
+ root.children.append(self._parse_define(is_embedded=True, has_open_params=True))
1493
+ elif self._looks_like_function_declaration():
1494
+ root.children.append(self._parse_typed_function(is_embedded=True, has_open_params=True))
1495
+ else:
1496
+ self.error("Expected 'define' or function declaration after 'embedded open'")
1497
+ elif self._match_keyword('class'):
1446
1498
  root.children.append(self._parse_class(is_embedded=True))
1499
+ elif self._match_keyword('enum'):
1500
+ root.children.append(self._parse_enum(is_embedded=True))
1501
+ elif self._match_keyword('define'):
1502
+ root.children.append(self._parse_define(is_embedded=True))
1447
1503
  elif self._looks_like_function_declaration():
1448
1504
  root.children.append(self._parse_typed_function(is_embedded=True))
1505
+ elif self._check(TokenType.IDENTIFIER):
1506
+ # embedded Name &Target { ... } - could be enum override
1507
+ root.children.append(self._parse_embedded_override())
1449
1508
  else:
1450
- self.error("Expected 'class' or function declaration after 'embedded'")
1509
+ self.error("Expected 'class', 'enum', 'open', 'define', function declaration, or identifier after 'embedded'")
1451
1510
  elif self._match_keyword('global'):
1452
1511
  # Check if followed by class or define (global class/function)
1453
1512
  if self._match_keyword('class'):
@@ -1478,6 +1537,11 @@ class CSSLParser:
1478
1537
  root.children.append(self._parse_for())
1479
1538
  elif self._match_keyword('foreach'):
1480
1539
  root.children.append(self._parse_foreach())
1540
+ # v4.3.2: Add try/catch and switch at top-level
1541
+ elif self._match_keyword('try'):
1542
+ root.children.append(self._parse_try())
1543
+ elif self._match_keyword('switch'):
1544
+ root.children.append(self._parse_switch())
1481
1545
  # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1482
1546
  # v4.2.1: Added LANG_INSTANCE_REF for lang$instance statements (js$GameData.score = 1337)
1483
1547
  elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
@@ -1711,7 +1775,7 @@ class CSSLParser:
1711
1775
  self._expect(TokenType.BLOCK_END)
1712
1776
  return node
1713
1777
 
1714
- def _parse_enum(self) -> ASTNode:
1778
+ def _parse_enum(self, is_embedded: bool = False) -> ASTNode:
1715
1779
  """Parse enum declaration.
1716
1780
 
1717
1781
  Syntax:
@@ -1722,9 +1786,22 @@ class CSSLParser:
1722
1786
  VALUE4 // Auto value 11
1723
1787
  }
1724
1788
 
1789
+ embedded enum NewEnum &OldEnum { ... } // Replace OldEnum with NewEnum values
1790
+
1725
1791
  Access values via EnumName::VALUE1
1726
1792
  """
1727
1793
  enum_name = self._advance().value
1794
+
1795
+ # v4.3.2: Check for &Target reference (enum replacement)
1796
+ replace_target = None
1797
+ if self._match(TokenType.AMPERSAND):
1798
+ if self._check(TokenType.IDENTIFIER):
1799
+ replace_target = self._advance().value
1800
+ elif self._check(TokenType.AT):
1801
+ self._advance()
1802
+ if self._check(TokenType.IDENTIFIER):
1803
+ replace_target = '@' + self._advance().value
1804
+
1728
1805
  self._expect(TokenType.BLOCK_START)
1729
1806
 
1730
1807
  members = []
@@ -1764,7 +1841,88 @@ class CSSLParser:
1764
1841
 
1765
1842
  return ASTNode('enum', value={
1766
1843
  'name': enum_name,
1767
- 'members': members
1844
+ 'members': members,
1845
+ 'is_embedded': is_embedded,
1846
+ 'replace_target': replace_target
1847
+ })
1848
+
1849
+ def _parse_embedded_override(self) -> ASTNode:
1850
+ """Parse embedded override for enums/structs without explicit type keyword.
1851
+
1852
+ Syntax:
1853
+ embedded __NewName &OldEnum { ... } // Replace OldEnum
1854
+ embedded __NewName &OldEnum ++ { ... } // Add to OldEnum
1855
+ embedded __NewName &OldEnum -- { ... } // Remove from OldEnum
1856
+
1857
+ This creates a new definition that modifies the target.
1858
+ """
1859
+ # Get the new name
1860
+ new_name = self._advance().value
1861
+
1862
+ # Expect &Target
1863
+ if not self._match(TokenType.AMPERSAND):
1864
+ self.error("Expected '&' followed by target name after embedded identifier")
1865
+
1866
+ # Get target name
1867
+ target_name = None
1868
+ if self._check(TokenType.IDENTIFIER):
1869
+ target_name = self._advance().value
1870
+ elif self._check(TokenType.AT):
1871
+ self._advance()
1872
+ if self._check(TokenType.IDENTIFIER):
1873
+ target_name = '@' + self._advance().value
1874
+ else:
1875
+ self.error("Expected target name after '&'")
1876
+
1877
+ # Check for mode modifier: ++ (add) or -- (remove)
1878
+ mode = 'replace'
1879
+ if self._match(TokenType.PLUS_PLUS):
1880
+ mode = 'add'
1881
+ elif self._match(TokenType.MINUS_MINUS):
1882
+ mode = 'remove'
1883
+
1884
+ self._expect(TokenType.BLOCK_START)
1885
+
1886
+ # Parse members (enum-style: NAME = value, NAME, etc.)
1887
+ members = []
1888
+ current_value = 0
1889
+
1890
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1891
+ if self._check(TokenType.NEWLINE):
1892
+ self._advance()
1893
+ continue
1894
+
1895
+ if self._check(TokenType.IDENTIFIER):
1896
+ member_name = self._advance().value
1897
+
1898
+ # Check for explicit value
1899
+ if self._match(TokenType.EQUALS):
1900
+ value_node = self._parse_expression()
1901
+ if isinstance(value_node, ASTNode) and value_node.type == 'literal':
1902
+ val = value_node.value
1903
+ if isinstance(val, dict) and 'value' in val:
1904
+ current_value = val['value']
1905
+ else:
1906
+ current_value = val
1907
+ else:
1908
+ current_value = value_node
1909
+
1910
+ members.append({'name': member_name, 'value': current_value})
1911
+ if isinstance(current_value, int):
1912
+ current_value = current_value + 1
1913
+
1914
+ self._match(TokenType.COMMA)
1915
+ else:
1916
+ self._advance()
1917
+
1918
+ self._expect(TokenType.BLOCK_END)
1919
+
1920
+ return ASTNode('enum', value={
1921
+ 'name': new_name,
1922
+ 'members': members,
1923
+ 'is_embedded': True,
1924
+ 'replace_target': target_name,
1925
+ 'mode': mode # 'replace', 'add', or 'remove'
1768
1926
  })
1769
1927
 
1770
1928
  def _parse_bytearrayed(self) -> ASTNode:
@@ -1823,6 +1981,29 @@ class CSSLParser:
1823
1981
  # Wildcard - matches any value
1824
1982
  pattern.append({'type': 'wildcard'})
1825
1983
  self._advance()
1984
+ elif self._check(TokenType.BRACKET_START):
1985
+ # v4.3.2: List pattern - matches a list value: ["read", "write"]
1986
+ self._advance() # consume [
1987
+ list_elements = []
1988
+ while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
1989
+ if self._check(TokenType.COMMA):
1990
+ self._advance()
1991
+ continue
1992
+ if self._check(TokenType.STRING):
1993
+ list_elements.append(self._advance().value)
1994
+ elif self._check(TokenType.NUMBER):
1995
+ list_elements.append(self._advance().value)
1996
+ elif self._check(TokenType.KEYWORD):
1997
+ kw = self._current().value
1998
+ if kw in ('true', 'True'):
1999
+ list_elements.append(True)
2000
+ elif kw in ('false', 'False'):
2001
+ list_elements.append(False)
2002
+ self._advance()
2003
+ else:
2004
+ self._advance()
2005
+ self._expect(TokenType.BRACKET_END)
2006
+ pattern.append({'type': 'list', 'values': list_elements})
1826
2007
  elif self._check(TokenType.NUMBER):
1827
2008
  # Check for hex format like 0x28
1828
2009
  token = self._current()
@@ -1839,6 +2020,10 @@ class CSSLParser:
1839
2020
  elif self._check(TokenType.STRING):
1840
2021
  value = self._advance().value
1841
2022
  pattern.append({'type': 'value', 'value': value})
2023
+ elif self._check(TokenType.BOOLEAN):
2024
+ # v4.3.2: Handle true/false which are tokenized as BOOLEAN
2025
+ value = self._advance().value
2026
+ pattern.append({'type': 'value', 'value': value})
1842
2027
  elif self._check(TokenType.KEYWORD):
1843
2028
  kw = self._current().value
1844
2029
  if kw in ('true', 'True'):
@@ -1916,15 +2101,26 @@ class CSSLParser:
1916
2101
  self._expect(TokenType.BLOCK_END)
1917
2102
  default_block = body_children
1918
2103
 
1919
- # Parse function reference: &funcName;
2104
+ # Parse function reference: &funcName; or &funcName(arg1, arg2);
1920
2105
  elif self._check(TokenType.AMPERSAND):
1921
2106
  self._advance() # consume &
1922
2107
  if self._check(TokenType.IDENTIFIER):
1923
2108
  func_name = self._advance().value
2109
+ # v4.3.2: Support function references with parameters: &testfunc(1, 2)
2110
+ func_args = []
2111
+ if self._check(TokenType.PAREN_START):
2112
+ self._advance() # consume (
2113
+ while not self._check(TokenType.PAREN_END) and not self._is_at_end():
2114
+ arg = self._parse_expression()
2115
+ func_args.append(arg)
2116
+ if not self._check(TokenType.PAREN_END):
2117
+ self._expect(TokenType.COMMA)
2118
+ self._expect(TokenType.PAREN_END)
1924
2119
  func_refs.append({
1925
2120
  'position': len(func_refs),
1926
2121
  'hex_pos': f"0x{len(func_refs):x}",
1927
- 'func_ref': func_name
2122
+ 'func_ref': func_name,
2123
+ 'args': func_args # v4.3.2: Store arguments for simulation
1928
2124
  })
1929
2125
  self._match(TokenType.SEMICOLON)
1930
2126
 
@@ -2322,18 +2518,21 @@ class CSSLParser:
2322
2518
 
2323
2519
  return params
2324
2520
 
2325
- def _parse_define(self, is_global: bool = False) -> ASTNode:
2521
+ def _parse_define(self, is_global: bool = False, is_embedded: bool = False, has_open_params: bool = False, modifiers: list = None) -> ASTNode:
2326
2522
  """Parse define function declaration.
2327
2523
 
2328
2524
  Syntax:
2329
2525
  define MyFunc(args) { } // Local function
2330
2526
  global define MyFunc(args) { } // Global function
2527
+ private define MyFunc(args) { } // Private function (v4.5.1)
2331
2528
  define @MyFunc(args) { } // Global function (alternative)
2332
2529
  define *MyFunc(args) { } // Non-null: must never return None
2333
2530
  define MyFunc(args) : extends OtherFunc { } // Inherit local vars
2334
2531
  define MyFunc(args) : overwrites OtherFunc { } // Replace OtherFunc
2335
2532
  define MyFunc(args) : supports python { } // Multi-language syntax
2336
2533
  define MyFunc(args) :: extends Parent::Method { } // Method-level inheritance
2534
+ embedded define MyFunc(args) &target { } // Immediate &target replacement
2535
+ open embedded define MyFunc(open Input) &target { } // Open params + embedded
2337
2536
  """
2338
2537
  # Check for * prefix (non-null function - must return non-null)
2339
2538
  # Also *[type] for type exclusion (must NOT return that type)
@@ -2552,7 +2751,8 @@ class CSSLParser:
2552
2751
  node = ASTNode('function', value={
2553
2752
  'name': name,
2554
2753
  'is_global': is_global,
2555
- 'is_embedded': False, # v4.2.5: define uses delayed &target replacement
2754
+ 'is_embedded': is_embedded, # v4.2.5: immediate &target replacement (v4.3.2: use param)
2755
+ 'has_open_params': has_open_params, # v4.3.2: open embedded define support
2556
2756
  'params': params,
2557
2757
  'non_null': non_null,
2558
2758
  'exclude_type': exclude_type, # *[type] - must NOT return this type
@@ -2572,7 +2772,9 @@ class CSSLParser:
2572
2772
  # v4.1.0: Multi-language support
2573
2773
  'supports_language': supports_language,
2574
2774
  # v4.2.0: Raw body for language transformation
2575
- 'raw_body': raw_body
2775
+ 'raw_body': raw_body,
2776
+ # v4.5.1: Function modifiers (private, const, static, etc.)
2777
+ 'modifiers': modifiers or []
2576
2778
  }, children=children)
2577
2779
 
2578
2780
  return node
@@ -2596,6 +2798,9 @@ class CSSLParser:
2596
2798
  elif self._match_keyword('continue'):
2597
2799
  self._match(TokenType.SEMICOLON)
2598
2800
  return ASTNode('continue')
2801
+ # v4.5.1: Add throw statement parsing
2802
+ elif self._match_keyword('throw'):
2803
+ return self._parse_throw()
2599
2804
  elif self._match_keyword('try'):
2600
2805
  return self._parse_try()
2601
2806
  elif self._match_keyword('await'):
@@ -2988,9 +3193,39 @@ class CSSLParser:
2988
3193
  value = self._parse_expression()
2989
3194
  self._expect(TokenType.PAREN_END)
2990
3195
 
2991
- # v4.2.5: Check if this is a param switch (switch on open params identifier)
3196
+ # v4.2.5: Check if this is a param switch (switch on open params)
2992
3197
  # Syntax: switch(Params): case name: ... except age: ... always: ... finally: ...
2993
- is_param_switch = (value.type == 'identifier')
3198
+ # v4.3.2: Detect param_switch by:
3199
+ # 1. Explicit "Params" identifier, OR
3200
+ # 2. Any identifier if case uses param-style conditions (& / not)
3201
+ is_param_switch = (value.type == 'identifier' and value.value == 'Params')
3202
+
3203
+ # v4.3.2: Also check if case syntax uses param-style conditions
3204
+ # Look ahead to see if first case uses & or 'not' operators
3205
+ if not is_param_switch and value.type == 'identifier':
3206
+ saved_pos = self.pos
3207
+ # Skip optional : and {
3208
+ if self._check(TokenType.COLON):
3209
+ self._advance()
3210
+ if self._check(TokenType.BLOCK_START):
3211
+ self._advance()
3212
+ # Check for 'case' keyword
3213
+ if self._check_keyword('case'):
3214
+ self._advance() # skip 'case'
3215
+ # Look for & or 'not' before : (indicates param switch)
3216
+ depth = 0
3217
+ while not self._is_at_end():
3218
+ if self._check(TokenType.COLON) and depth == 0:
3219
+ break
3220
+ if self._check(TokenType.PAREN_START):
3221
+ depth += 1
3222
+ if self._check(TokenType.PAREN_END):
3223
+ depth -= 1
3224
+ if self._check(TokenType.AMPERSAND) or self._check_keyword('not') or self._check(TokenType.NOT):
3225
+ is_param_switch = True
3226
+ break
3227
+ self._advance()
3228
+ self.pos = saved_pos # Restore position
2994
3229
 
2995
3230
  if is_param_switch:
2996
3231
  return self._parse_param_switch(value)
@@ -3112,14 +3347,17 @@ class CSSLParser:
3112
3347
  def _parse_param_condition(self) -> dict:
3113
3348
  """Parse param switch condition.
3114
3349
 
3115
- Syntax:
3350
+ v4.3.2: Enhanced to support multiple styles:
3116
3351
  name -> {'type': 'exists', 'param': 'name'}
3117
3352
  not name -> {'type': 'not', 'param': 'name'}
3353
+ !name -> {'type': 'not', 'param': 'name'}
3118
3354
  name & age -> {'type': 'and', 'left': {...}, 'right': {...}}
3119
3355
  name & not age -> {'type': 'and', 'left': {...}, 'right': {'type': 'not', ...}}
3356
+ name & !age -> {'type': 'and', 'left': {...}, 'right': {'type': 'not', ...}}
3357
+ name || age -> {'type': 'or', 'left': {...}, 'right': {...}}
3120
3358
  """
3121
- # Check for 'not' prefix
3122
- negated = self._match_keyword('not')
3359
+ # Check for 'not' or '!' prefix
3360
+ negated = self._match_keyword('not') or self._match(TokenType.NOT)
3123
3361
  param_name = self._advance().value
3124
3362
 
3125
3363
  if negated:
@@ -3127,18 +3365,33 @@ class CSSLParser:
3127
3365
  else:
3128
3366
  condition = {'type': 'exists', 'param': param_name}
3129
3367
 
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
3368
+ # Check for & (AND) or || (OR) combinations
3369
+ while True:
3370
+ if self._match(TokenType.AMPERSAND):
3371
+ # AND operator - check for 'not' or '!' prefix on right side
3372
+ right_negated = self._match_keyword('not') or self._match(TokenType.NOT)
3373
+ right_param = self._advance().value
3135
3374
 
3136
- if right_negated:
3137
- right_condition = {'type': 'not', 'param': right_param}
3138
- else:
3139
- right_condition = {'type': 'exists', 'param': right_param}
3375
+ if right_negated:
3376
+ right_condition = {'type': 'not', 'param': right_param}
3377
+ else:
3378
+ right_condition = {'type': 'exists', 'param': right_param}
3379
+
3380
+ condition = {'type': 'and', 'left': condition, 'right': right_condition}
3140
3381
 
3141
- condition = {'type': 'and', 'left': condition, 'right': right_condition}
3382
+ elif self._match(TokenType.OR):
3383
+ # OR operator (||) - check for 'not' or '!' prefix on right side
3384
+ right_negated = self._match_keyword('not') or self._match(TokenType.NOT)
3385
+ right_param = self._advance().value
3386
+
3387
+ if right_negated:
3388
+ right_condition = {'type': 'not', 'param': right_param}
3389
+ else:
3390
+ right_condition = {'type': 'exists', 'param': right_param}
3391
+
3392
+ condition = {'type': 'or', 'left': condition, 'right': right_condition}
3393
+ else:
3394
+ break
3142
3395
 
3143
3396
  return condition
3144
3397
 
@@ -3226,8 +3479,11 @@ class CSSLParser:
3226
3479
  self._expect(TokenType.BLOCK_END)
3227
3480
  node.children.append(try_block)
3228
3481
 
3229
- # v4.2.6: Skip optional semicolon between try block and catch
3230
- self._match(TokenType.SEMICOLON)
3482
+ # v4.2.6: Skip optional semicolons between try block and catch
3483
+ # v4.5.1: Also skip comments for better .cssl-pl file support
3484
+ while self._match(TokenType.SEMICOLON) or self._check(TokenType.COMMENT):
3485
+ if self._check(TokenType.COMMENT):
3486
+ self._advance()
3231
3487
 
3232
3488
  if self._match_keyword('catch'):
3233
3489
  error_var = None
@@ -3244,8 +3500,11 @@ class CSSLParser:
3244
3500
  self._expect(TokenType.BLOCK_END)
3245
3501
  node.children.append(catch_block)
3246
3502
 
3247
- # v4.2.6: Skip optional semicolon between catch and finally
3248
- self._match(TokenType.SEMICOLON)
3503
+ # v4.2.6: Skip optional semicolons between catch and finally
3504
+ # v4.5.1: Also skip comments for better .cssl-pl file support
3505
+ while self._match(TokenType.SEMICOLON) or self._check(TokenType.COMMENT):
3506
+ if self._check(TokenType.COMMENT):
3507
+ self._advance()
3249
3508
 
3250
3509
  # v4.2.6: Add finally support
3251
3510
  if self._match_keyword('finally'):
@@ -3260,6 +3519,26 @@ class CSSLParser:
3260
3519
 
3261
3520
  return node
3262
3521
 
3522
+ def _parse_throw(self) -> ASTNode:
3523
+ """Parse throw statement: throw expression;
3524
+
3525
+ v4.5.1: Added throw statement for exception handling.
3526
+
3527
+ Syntax:
3528
+ throw "Error message";
3529
+ throw error_variable;
3530
+ throw MyException("details");
3531
+ """
3532
+ # Parse the expression to throw (can be string, variable, or function call)
3533
+ if self._check(TokenType.SEMICOLON):
3534
+ # throw; - re-throw current exception
3535
+ self._advance()
3536
+ return ASTNode('throw', value=None)
3537
+
3538
+ expr = self._parse_expression()
3539
+ self._match(TokenType.SEMICOLON)
3540
+ return ASTNode('throw', value=expr)
3541
+
3263
3542
  def _parse_await(self) -> ASTNode:
3264
3543
  """Parse await statement: await expression;"""
3265
3544
  expr = self._parse_expression()
@@ -3458,7 +3737,8 @@ class CSSLParser:
3458
3737
  filter_info = {}
3459
3738
  # Parse type::helper=value patterns within this bracket
3460
3739
  while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
3461
- if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
3740
+ # Accept IDENTIFIER, KEYWORD, or TYPE_LITERAL (dict, list) as filter type
3741
+ if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD) or self._check(TokenType.TYPE_LITERAL):
3462
3742
  filter_type = self._advance().value
3463
3743
  if self._match(TokenType.DOUBLE_COLON):
3464
3744
  helper = self._advance().value
@@ -4245,7 +4525,7 @@ class CSSLParser:
4245
4525
  def _is_object_literal(self) -> bool:
4246
4526
  """Check if current position is an object literal { key = value } vs action block { expr; }
4247
4527
 
4248
- Object literal: { name = value; } or { "key" = value; }
4528
+ Object literal: { name = value } or { "key" = value } or { "key": value } (Python-style)
4249
4529
  Action block: { %version; } or { "1.0.0" } or { call(); }
4250
4530
  """
4251
4531
  # Empty block is action block
@@ -4255,12 +4535,12 @@ class CSSLParser:
4255
4535
  # Save position for lookahead
4256
4536
  saved_pos = self.pos
4257
4537
 
4258
- # Check if it looks like key = value pattern
4538
+ # Check if it looks like key = value or key: value pattern
4259
4539
  is_object = False
4260
4540
  if self._check(TokenType.IDENTIFIER) or self._check(TokenType.STRING):
4261
4541
  self._advance() # skip key
4262
- if self._check(TokenType.EQUALS):
4263
- # Looks like object literal: { key = ...
4542
+ if self._check(TokenType.EQUALS) or self._check(TokenType.COLON):
4543
+ # Looks like object literal: { key = ... } or { "key": ... }
4264
4544
  is_object = True
4265
4545
 
4266
4546
  # Restore position
@@ -4300,12 +4580,22 @@ class CSSLParser:
4300
4580
  return ASTNode('action_block', children=children)
4301
4581
 
4302
4582
  def _parse_object(self) -> ASTNode:
4583
+ """Parse object/dict literal.
4584
+
4585
+ Supports both CSSL-style and Python-style syntax:
4586
+ { key = value } // CSSL style
4587
+ { "key" = value } // CSSL style with string key
4588
+ { "key": value } // Python style
4589
+ { key: value } // Python style with identifier key
4590
+ """
4303
4591
  properties = {}
4304
4592
 
4305
4593
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
4306
4594
  if self._check(TokenType.IDENTIFIER) or self._check(TokenType.STRING):
4307
4595
  key = self._advance().value
4308
- self._expect(TokenType.EQUALS)
4596
+ # Accept both = (CSSL) and : (Python) as key-value separator
4597
+ if not self._match(TokenType.EQUALS) and not self._match(TokenType.COLON):
4598
+ self.error(f"Expected '=' or ':' after key '{key}' in object literal")
4309
4599
  value = self._parse_expression()
4310
4600
  properties[key] = value
4311
4601
  self._match(TokenType.SEMICOLON)
@@ -4367,12 +4657,117 @@ def parse_cssl_program(source: str) -> ASTNode:
4367
4657
  return parser.parse_program()
4368
4658
 
4369
4659
 
4370
- def tokenize_cssl(source: str) -> List[Token]:
4371
- """Tokenize CSSL source code (useful for syntax highlighting)"""
4660
+ def tokenize_cssl(source: str, use_cpp: bool = True) -> List[Token]:
4661
+ """
4662
+ Tokenize CSSL source code (useful for syntax highlighting).
4663
+
4664
+ Args:
4665
+ source: CSSL source code
4666
+ use_cpp: If True, use C++ acceleration when available (default True)
4667
+
4668
+ Returns:
4669
+ List of Token objects
4670
+ """
4671
+ # Try C++ tokenization if available and requested
4672
+ if use_cpp:
4673
+ try:
4674
+ from . import _CPP_AVAILABLE, _cpp_module
4675
+ if _CPP_AVAILABLE and _cpp_module and hasattr(_cpp_module, 'Lexer'):
4676
+ lexer = _cpp_module.Lexer(source)
4677
+ cpp_tokens = lexer.tokenize()
4678
+ # Convert C++ tokens to Python Token objects
4679
+ return _convert_cpp_tokens(cpp_tokens)
4680
+ except (ImportError, Exception):
4681
+ pass
4682
+
4683
+ # Fall back to Python lexer
4372
4684
  lexer = CSSLLexer(source)
4373
4685
  return lexer.tokenize()
4374
4686
 
4375
4687
 
4688
+ def _convert_cpp_tokens(cpp_tokens: list) -> List[Token]:
4689
+ """Convert C++ Token objects to Python Token objects."""
4690
+ result = []
4691
+ for ct in cpp_tokens:
4692
+ # Map C++ token type to Python TokenType
4693
+ token_type = _map_cpp_token_type(ct.type)
4694
+
4695
+ # Get value based on value_type
4696
+ if ct.value_type == 'string':
4697
+ value = ct.str_value
4698
+ elif ct.value_type == 'number':
4699
+ value = ct.num_value
4700
+ elif ct.value_type == 'bool':
4701
+ value = ct.bool_value
4702
+ else:
4703
+ value = ct.str_value
4704
+
4705
+ result.append(Token(token_type, value, ct.line, ct.column))
4706
+
4707
+ return result
4708
+
4709
+
4710
+ def _map_cpp_token_type(cpp_type: int) -> TokenType:
4711
+ """Map C++ token type integer to Python TokenType enum."""
4712
+ # This mapping must match the C++ TokenType enum in cssl_core.cpp
4713
+ type_map = {
4714
+ 0: TokenType.KEYWORD,
4715
+ 1: TokenType.IDENTIFIER,
4716
+ 2: TokenType.STRING,
4717
+ 3: TokenType.NUMBER,
4718
+ 4: TokenType.BOOLEAN,
4719
+ 5: TokenType.NULL,
4720
+ 6: TokenType.OPERATOR,
4721
+ 7: TokenType.INJECT_LEFT,
4722
+ 8: TokenType.INJECT_RIGHT,
4723
+ 9: TokenType.INJECT_PLUS_LEFT,
4724
+ 10: TokenType.INJECT_PLUS_RIGHT,
4725
+ 11: TokenType.INJECT_MINUS_LEFT,
4726
+ 12: TokenType.INJECT_MINUS_RIGHT,
4727
+ 13: TokenType.INFUSE_LEFT,
4728
+ 14: TokenType.INFUSE_RIGHT,
4729
+ 15: TokenType.FLOW_RIGHT,
4730
+ 16: TokenType.FLOW_LEFT,
4731
+ 17: TokenType.EQUALS,
4732
+ 18: TokenType.COMPARE_EQ,
4733
+ 19: TokenType.COMPARE_NE,
4734
+ 20: TokenType.COMPARE_LT,
4735
+ 21: TokenType.COMPARE_GT,
4736
+ 22: TokenType.COMPARE_LE,
4737
+ 23: TokenType.COMPARE_GE,
4738
+ 24: TokenType.PLUS,
4739
+ 25: TokenType.MINUS,
4740
+ 26: TokenType.MULTIPLY,
4741
+ 27: TokenType.DIVIDE,
4742
+ 28: TokenType.MODULO,
4743
+ 29: TokenType.AND,
4744
+ 30: TokenType.OR,
4745
+ 31: TokenType.NOT,
4746
+ 32: TokenType.AMPERSAND,
4747
+ 33: TokenType.BLOCK_START,
4748
+ 34: TokenType.BLOCK_END,
4749
+ 35: TokenType.PAREN_START,
4750
+ 36: TokenType.PAREN_END,
4751
+ 37: TokenType.BRACKET_START,
4752
+ 38: TokenType.BRACKET_END,
4753
+ 39: TokenType.SEMICOLON,
4754
+ 40: TokenType.COLON,
4755
+ 41: TokenType.DOUBLE_COLON,
4756
+ 42: TokenType.COMMA,
4757
+ 43: TokenType.DOT,
4758
+ 44: TokenType.AT,
4759
+ 45: TokenType.GLOBAL_REF,
4760
+ 46: TokenType.SNAPSHOT_REF,
4761
+ 47: TokenType.ARROW,
4762
+ 48: TokenType.LAMBDA,
4763
+ 49: TokenType.TERNARY,
4764
+ 50: TokenType.INCREMENT,
4765
+ 51: TokenType.DECREMENT,
4766
+ 52: TokenType.EOF,
4767
+ }
4768
+ return type_map.get(cpp_type, TokenType.IDENTIFIER)
4769
+
4770
+
4376
4771
  # Export public API
4377
4772
  __all__ = [
4378
4773
  'TokenType', 'Token', 'ASTNode',