IncludeCPP 4.2.2__py3-none-any.whl → 4.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. includecpp/CHANGELOG.md +104 -115
  2. includecpp/DOCUMENTATION.md +208 -355
  3. includecpp/__init__.py +1 -1
  4. includecpp/__init__.pyi +1 -4
  5. includecpp/cli/commands.py +1220 -27
  6. includecpp/core/cpp_api_extensions.pyi +204 -200
  7. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1505 -1467
  8. includecpp/core/cssl/__init__.py +317 -0
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/cssl_core.pyi +323 -0
  11. includecpp/core/cssl/cpp/build/libgcc_s_seh-1.dll +0 -0
  12. includecpp/core/cssl/cpp/build/libstdc++-6.dll +0 -0
  13. includecpp/core/cssl/cpp/build/libwinpthread-1.dll +0 -0
  14. includecpp/core/cssl/cpp/cssl_core.cp +108 -0
  15. includecpp/core/cssl/cpp/cssl_lexer.hpp +280 -0
  16. includecpp/core/cssl/cssl_builtins.py +245 -20
  17. includecpp/core/cssl/cssl_compiler.py +448 -0
  18. includecpp/core/cssl/cssl_optimizer.py +833 -0
  19. includecpp/core/cssl/cssl_parser.py +945 -40
  20. includecpp/core/cssl/cssl_runtime.py +751 -38
  21. includecpp/core/cssl/cssl_syntax.py +17 -0
  22. includecpp/core/cssl/cssl_types.py +321 -0
  23. includecpp/core/cssl_bridge.py +36 -2
  24. includecpp/generator/parser.cpp +38 -14
  25. includecpp/vscode/cssl/package.json +15 -0
  26. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +134 -2
  27. includecpp-4.5.2.dist-info/METADATA +277 -0
  28. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/RECORD +32 -23
  29. includecpp-4.2.2.dist-info/METADATA +0 -1008
  30. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/WHEEL +0 -0
  31. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/entry_points.txt +0 -0
  32. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/licenses/LICENSE +0 -0
  33. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ from .cssl_modules import get_module_registry, get_standard_module
15
15
  from .cssl_types import (
16
16
  Parameter, DataStruct, Shuffled, Iterator, Combo,
17
17
  Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
18
- CSSLClass, CSSLInstance
18
+ CSSLClass, CSSLInstance, ByteArrayed
19
19
  )
20
20
 
21
21
 
@@ -199,6 +199,13 @@ class CSSLReturn(Exception):
199
199
  super().__init__()
200
200
 
201
201
 
202
+ class CSSLThrow(Exception):
203
+ """Throw statement - v4.5.1: User-thrown exceptions that propagate to catch blocks"""
204
+ def __init__(self, message: Any = None):
205
+ self.message = message
206
+ super().__init__(str(message) if message else "")
207
+
208
+
202
209
  @dataclass
203
210
  class Scope:
204
211
  """Variable scope"""
@@ -532,6 +539,10 @@ class CSSLRuntime:
532
539
  self._exec_struct(child)
533
540
  elif child.type == 'class':
534
541
  self._exec_class(child)
542
+ elif child.type == 'enum':
543
+ self._exec_enum(child)
544
+ elif child.type == 'bytearrayed':
545
+ self._exec_bytearrayed(child)
535
546
  elif child.type == 'function':
536
547
  self._exec_function(child)
537
548
  elif child.type == 'global_assignment':
@@ -813,6 +824,123 @@ class CSSLRuntime:
813
824
 
814
825
  return struct_data
815
826
 
827
+ def _exec_enum(self, node: ASTNode) -> Dict[str, Any]:
828
+ """Execute enum declaration - registers enum values in scope.
829
+
830
+ Creates a dictionary-like enum object accessible via EnumName::VALUE syntax.
831
+
832
+ Example:
833
+ enum Colors { RED, GREEN, BLUE }
834
+ Colors::RED // returns 0
835
+ Colors::GREEN // returns 1
836
+
837
+ v4.3.2: Support for embedded enum modification:
838
+ embedded __NewEnum &OldEnum { ... } // Replace OldEnum
839
+ embedded __NewEnum &OldEnum ++ { ... } // Add to OldEnum
840
+ embedded __NewEnum &OldEnum -- { ... } // Remove from OldEnum
841
+ """
842
+ enum_info = node.value
843
+ enum_name = enum_info.get('name')
844
+ members = enum_info.get('members', [])
845
+ is_embedded = enum_info.get('is_embedded', False)
846
+ replace_target = enum_info.get('replace_target')
847
+ mode = enum_info.get('mode', 'replace')
848
+
849
+ # Create enum object as a dict-like object with members
850
+ new_values = {}
851
+ for member in members:
852
+ member_name = member['name']
853
+ member_value = member['value']
854
+ # Evaluate if value is an ASTNode
855
+ if isinstance(member_value, ASTNode):
856
+ member_value = self._evaluate(member_value)
857
+ new_values[member_name] = member_value
858
+
859
+ # v4.3.2: Handle embedded modification
860
+ if is_embedded and replace_target:
861
+ target_name = replace_target.lstrip('@') if replace_target.startswith('@') else replace_target
862
+
863
+ if mode == 'add':
864
+ # Get existing enum and add new values
865
+ existing = self.scope.get(target_name) or self.global_scope.get(target_name) or {}
866
+ if isinstance(existing, dict):
867
+ # For add mode, auto-increment from highest existing int value
868
+ max_val = -1
869
+ for v in existing.values():
870
+ if isinstance(v, int) and v > max_val:
871
+ max_val = v
872
+ # Update new values that don't have explicit values
873
+ for name, val in new_values.items():
874
+ if isinstance(val, int) and val <= max_val:
875
+ max_val += 1
876
+ new_values[name] = max_val
877
+ enum_obj = {**existing, **new_values}
878
+ else:
879
+ enum_obj = new_values
880
+ elif mode == 'remove':
881
+ # Get existing enum and remove specified keys
882
+ existing = self.scope.get(target_name) or self.global_scope.get(target_name) or {}
883
+ if isinstance(existing, dict):
884
+ enum_obj = {k: v for k, v in existing.items() if k not in new_values}
885
+ else:
886
+ enum_obj = {}
887
+ else:
888
+ # Replace mode - just use new values
889
+ enum_obj = new_values
890
+
891
+ self.scope.set(target_name, enum_obj)
892
+ self.global_scope.set(target_name, enum_obj)
893
+ if replace_target.startswith('@'):
894
+ self._promoted_globals[target_name] = enum_obj
895
+ else:
896
+ # Regular enum - just register it
897
+ enum_obj = new_values
898
+ self.scope.set(enum_name, enum_obj)
899
+ self.global_scope.set(enum_name, enum_obj)
900
+
901
+ return enum_obj
902
+
903
+ def _exec_bytearrayed(self, node: ASTNode) -> 'ByteArrayed':
904
+ """Execute bytearrayed declaration - function-to-byte mapping with pattern matching.
905
+
906
+ Creates a ByteArrayed object that:
907
+ - Maps function references to byte positions (0x0, 0x1, etc.)
908
+ - Executes functions "invisibly" when called to get return values
909
+ - Matches case patterns based on return values
910
+ - Supports indexing: MyBytes["0x0"] or MyBytes[0]
911
+
912
+ Example:
913
+ bytearrayed MyBytes {
914
+ &func1; // 0x0
915
+ &func2; // 0x1
916
+ case {0, 1} { // func1=0, func2=1
917
+ printl("Match!");
918
+ }
919
+ }
920
+ MyBytes(); // Execute pattern matching
921
+ x = MyBytes["0x0"]; // Get value at position 0
922
+ """
923
+ info = node.value
924
+ name = info.get('name')
925
+ func_refs = info.get('func_refs', [])
926
+ cases = info.get('cases', [])
927
+ default_block = info.get('default')
928
+
929
+ # Create ByteArrayed object
930
+ bytearrayed_obj = ByteArrayed(
931
+ name=name,
932
+ func_refs=func_refs,
933
+ cases=cases,
934
+ default_block=default_block,
935
+ runtime=self
936
+ )
937
+
938
+ # Register in scope
939
+ self.scope.set(name, bytearrayed_obj)
940
+ self.global_scope.set(name, bytearrayed_obj)
941
+
942
+ return bytearrayed_obj
943
+
816
944
  def _exec_class(self, node: ASTNode) -> CSSLClass:
817
945
  """Execute class definition - registers class in scope.
818
946
 
@@ -942,6 +1070,22 @@ class CSSLRuntime:
942
1070
  self.global_scope.set(class_name, class_def)
943
1071
  self._promoted_globals[class_name] = class_def
944
1072
 
1073
+ # v4.2.5: Handle &target replacement for classes
1074
+ # embedded class MyClass &$Target { } - immediate replacement
1075
+ # class MyClass &$Target { } - deferred until instantiation
1076
+ append_ref_class = class_info.get('append_ref_class')
1077
+ is_embedded = class_info.get('is_embedded', False)
1078
+ if append_ref_class and is_embedded:
1079
+ append_ref_member = class_info.get('append_ref_member')
1080
+ self._overwrite_class_target(append_ref_class, append_ref_member, class_def)
1081
+ class_def._target_applied = True
1082
+ elif append_ref_class:
1083
+ # Store reference info for deferred replacement
1084
+ class_def._pending_target = {
1085
+ 'append_ref_class': append_ref_class,
1086
+ 'append_ref_member': class_info.get('append_ref_member')
1087
+ }
1088
+
945
1089
  # Handle class overwrites - replace methods in target class
946
1090
  if overwrites_class_name:
947
1091
  self._apply_class_overwrites(
@@ -1164,13 +1308,18 @@ class CSSLRuntime:
1164
1308
  # Handle &Class::method syntax
1165
1309
  # Without ++ = full replacement
1166
1310
  # With ++ = append (run original first, then new code)
1167
- if append_ref_class:
1311
+ # v4.2.5: Only do immediate replacement if is_embedded=True
1312
+ # For regular 'define', replacement is deferred until function is called
1313
+ is_embedded = func_info.get('is_embedded', False)
1314
+ if append_ref_class and is_embedded:
1168
1315
  if append_mode:
1169
1316
  # Append mode: wrap original to run original + new
1170
1317
  self._append_to_target(append_ref_class, append_ref_member, node)
1171
1318
  else:
1172
1319
  # Full replacement
1173
1320
  self._overwrite_target(append_ref_class, append_ref_member, node)
1321
+ # Mark as already applied so we don't apply again on call
1322
+ node.value['_target_applied'] = True
1174
1323
 
1175
1324
  # Handle overwrites keyword - replace the target function
1176
1325
  if overwrites_func:
@@ -1215,6 +1364,34 @@ class CSSLRuntime:
1215
1364
  return self._call_function(func_node, list(args), kwargs)
1216
1365
  return wrapper
1217
1366
 
1367
+ def _create_python_method_wrapper(self, func_node: ASTNode, python_obj):
1368
+ """Create a Python-callable wrapper that passes python_obj as 'this'.
1369
+
1370
+ v4.3.0: Used when replacing Python object methods with CSSL functions.
1371
+ The wrapper ensures 'this->' in CSSL refers to the Python object.
1372
+ """
1373
+ runtime = self
1374
+ _captured_obj = python_obj
1375
+
1376
+ def wrapper(*args, **kwargs):
1377
+ # Save current instance context
1378
+ old_instance = runtime._current_instance
1379
+ old_this = runtime.scope.get('this')
1380
+ try:
1381
+ # Set Python object as current instance for this-> access
1382
+ runtime._current_instance = _captured_obj
1383
+ runtime.scope.set('this', _captured_obj)
1384
+ return runtime._call_function(func_node, list(args), kwargs)
1385
+ finally:
1386
+ # Restore previous context
1387
+ runtime._current_instance = old_instance
1388
+ if old_this is not None:
1389
+ runtime.scope.set('this', old_this)
1390
+ elif 'this' in runtime.scope.variables:
1391
+ del runtime.scope.variables['this']
1392
+
1393
+ return wrapper
1394
+
1218
1395
  def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
1219
1396
  """Overwrite a class method or function with the replacement.
1220
1397
 
@@ -1222,9 +1399,50 @@ class CSSLRuntime:
1222
1399
  - &ClassName::method - Overwrite method in CSSL class
1223
1400
  - &$PyObject.method - Overwrite method in Python shared object
1224
1401
  - &functionName - Overwrite standalone function
1402
+
1403
+ v4.2.6: In namespace mode (_payload_namespace_mode), track replacements
1404
+ instead of applying globally. The namespace handler will scope them.
1405
+
1406
+ v4.5.1: Respects 'private' modifier - private functions/methods cannot be overwritten.
1225
1407
  """
1226
1408
  from ..cssl_bridge import _live_objects, SharedObjectProxy
1227
1409
 
1410
+ # v4.5.1: Check if target has 'private' modifier - prevent overwrite
1411
+ if not ref_class.startswith('$'):
1412
+ target_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1413
+ if target_func is not None:
1414
+ # Check for function with private modifier
1415
+ if hasattr(target_func, 'value') and isinstance(target_func.value, dict):
1416
+ modifiers = target_func.value.get('modifiers', [])
1417
+ if 'private' in modifiers:
1418
+ raise CSSLRuntimeError(
1419
+ f"Cannot overwrite private function '{ref_class}' - "
1420
+ f"private functions are protected from embedded replacements"
1421
+ )
1422
+ # Check for class with private method
1423
+ if ref_member and isinstance(target_func, CSSLClass):
1424
+ method = target_func.methods.get(ref_member) if hasattr(target_func, 'methods') else None
1425
+ if method and hasattr(method, 'value') and isinstance(method.value, dict):
1426
+ modifiers = method.value.get('modifiers', [])
1427
+ if 'private' in modifiers:
1428
+ raise CSSLRuntimeError(
1429
+ f"Cannot overwrite private method '{ref_class}::{ref_member}' - "
1430
+ f"private methods are protected from embedded replacements"
1431
+ )
1432
+
1433
+ # v4.2.6: Check for namespace mode
1434
+ namespace_mode = getattr(self, '_payload_namespace_mode', None)
1435
+ if namespace_mode and ref_member is None and not ref_class.startswith('$'):
1436
+ # Standalone function replacement in namespace mode
1437
+ # Track it for the namespace handler
1438
+ if not hasattr(self, '_namespace_replacements'):
1439
+ self._namespace_replacements = {}
1440
+ self._namespace_replacements[ref_class] = replacement_node
1441
+ # Still register the function normally (namespace handler will move it)
1442
+ self.scope.set(ref_class, replacement_node)
1443
+ self.global_scope.set(ref_class, replacement_node)
1444
+ return
1445
+
1228
1446
  # Handle Python shared objects
1229
1447
  if ref_class.startswith('$'):
1230
1448
  var_name = ref_class[1:]
@@ -1241,7 +1459,8 @@ class CSSLRuntime:
1241
1459
 
1242
1460
  # Overwrite Python object method
1243
1461
  if ref_member and hasattr(ref_obj, ref_member):
1244
- wrapper = self._create_python_wrapper(replacement_node)
1462
+ # v4.3.0: Create wrapper that passes Python object as 'this' context
1463
+ wrapper = self._create_python_method_wrapper(replacement_node, ref_obj)
1245
1464
  try:
1246
1465
  setattr(ref_obj, ref_member, wrapper)
1247
1466
  except (AttributeError, TypeError):
@@ -1250,12 +1469,49 @@ class CSSLRuntime:
1250
1469
 
1251
1470
  # Handle CSSL class method overwrite
1252
1471
  target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1253
- if target_class is None:
1254
- # Maybe it's a standalone function reference (no ::member)
1255
- if ref_member is None:
1256
- # &functionName - overwrite the function
1472
+
1473
+ # v4.2.3: Handle standalone function reference (&functionName) including builtins
1474
+ if ref_member is None:
1475
+ # Check if this is a builtin function
1476
+ is_builtin = hasattr(self, 'builtins') and self.builtins.has_function(ref_class)
1477
+
1478
+ # Check if target_class is NOT a CSSLClass (it's either a function/builtin or None)
1479
+ is_cssl_class = isinstance(target_class, CSSLClass)
1480
+ # v4.2.5: Also check if target is a CSSL function (ASTNode with type 'function')
1481
+ is_cssl_function = (hasattr(target_class, 'type') and target_class.type == 'function')
1482
+
1483
+ if is_builtin or (target_class is None) or (not is_cssl_class and callable(target_class)) or is_cssl_function:
1484
+ # v4.2.3: CRITICAL - Store original BEFORE overwriting for %name captures
1485
+ # This ensures %exit() refers to the ORIGINAL exit, not the replacement
1486
+ if ref_class not in self._original_functions:
1487
+ if is_builtin:
1488
+ # Store the original builtin from _functions dict
1489
+ # (handles aliases like printl -> builtin_println)
1490
+ original_builtin = self.builtins._functions.get(ref_class)
1491
+ if original_builtin is not None:
1492
+ self._original_functions[ref_class] = original_builtin
1493
+ elif is_cssl_function:
1494
+ # v4.2.5: Store original CSSL function (ASTNode)
1495
+ self._original_functions[ref_class] = target_class
1496
+ elif target_class is not None and callable(target_class):
1497
+ # Store the original function/callable
1498
+ self._original_functions[ref_class] = target_class
1499
+
1500
+ # &functionName - overwrite the function/builtin
1257
1501
  self.scope.set(ref_class, replacement_node)
1258
1502
  self.global_scope.set(ref_class, replacement_node)
1503
+
1504
+ # Also overwrite in builtins dict if it's a builtin
1505
+ if is_builtin:
1506
+ # Create wrapper that calls CSSL function instead of builtin
1507
+ def make_wrapper(node, runtime):
1508
+ def wrapper(*args, **kwargs):
1509
+ return runtime._call_function(node, list(args), kwargs)
1510
+ return wrapper
1511
+ self.builtins._functions[ref_class] = make_wrapper(replacement_node, self)
1512
+ return
1513
+
1514
+ if target_class is None:
1259
1515
  return
1260
1516
 
1261
1517
  if isinstance(target_class, CSSLClass) and ref_member:
@@ -1277,9 +1533,34 @@ class CSSLRuntime:
1277
1533
  - &ClassName::method ++ - Append to method in CSSL class
1278
1534
  - &$PyObject.method ++ - Append to method in Python shared object
1279
1535
  - &functionName ++ - Append to standalone function
1536
+
1537
+ v4.5.1: Respects 'private' modifier - private functions/methods cannot be appended to.
1280
1538
  """
1281
1539
  from ..cssl_bridge import _live_objects, SharedObjectProxy
1282
1540
 
1541
+ # v4.5.1: Check if target has 'private' modifier - prevent append
1542
+ if not ref_class.startswith('$'):
1543
+ target_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1544
+ if target_func is not None:
1545
+ # Check for function with private modifier
1546
+ if hasattr(target_func, 'value') and isinstance(target_func.value, dict):
1547
+ modifiers = target_func.value.get('modifiers', [])
1548
+ if 'private' in modifiers:
1549
+ raise CSSLRuntimeError(
1550
+ f"Cannot append to private function '{ref_class}' - "
1551
+ f"private functions are protected from embedded modifications"
1552
+ )
1553
+ # Check for class with private method
1554
+ if ref_member and isinstance(target_func, CSSLClass):
1555
+ method = target_func.methods.get(ref_member) if hasattr(target_func, 'methods') else None
1556
+ if method and hasattr(method, 'value') and isinstance(method.value, dict):
1557
+ modifiers = method.value.get('modifiers', [])
1558
+ if 'private' in modifiers:
1559
+ raise CSSLRuntimeError(
1560
+ f"Cannot append to private method '{ref_class}::{ref_member}' - "
1561
+ f"private methods are protected from embedded modifications"
1562
+ )
1563
+
1283
1564
  # Handle Python shared objects
1284
1565
  if ref_class.startswith('$'):
1285
1566
  var_name = ref_class[1:]
@@ -1357,6 +1638,58 @@ class CSSLRuntime:
1357
1638
  target_class.members[i] = append_node
1358
1639
  break
1359
1640
 
1641
+ def _overwrite_class_target(self, ref_class: str, ref_member: str, replacement_class: 'CSSLClass'):
1642
+ """Overwrite a target class with the replacement class.
1643
+
1644
+ v4.2.5: Used by 'embedded class' with &target syntax.
1645
+ Handles:
1646
+ - &ClassName - Overwrite CSSL class
1647
+ - &$PyObject - Overwrite Python shared object
1648
+
1649
+ v4.2.6: In namespace mode, track replacements instead of applying globally.
1650
+ """
1651
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1652
+
1653
+ # v4.2.6: Check for namespace mode
1654
+ namespace_mode = getattr(self, '_payload_namespace_mode', None)
1655
+ if namespace_mode and not ref_class.startswith('$') and not ref_class.startswith('@'):
1656
+ # Class replacement in namespace mode
1657
+ # Track it for the namespace handler
1658
+ if not hasattr(self, '_namespace_replacements'):
1659
+ self._namespace_replacements = {}
1660
+ self._namespace_replacements[ref_class] = replacement_class
1661
+ # Still register the class normally (namespace handler will move it)
1662
+ self.scope.set(ref_class, replacement_class)
1663
+ self.global_scope.set(ref_class, replacement_class)
1664
+ return
1665
+
1666
+ # Handle Python shared objects
1667
+ if ref_class.startswith('$'):
1668
+ var_name = ref_class[1:]
1669
+ # Replace the shared object with the new class
1670
+ _live_objects[var_name] = replacement_class
1671
+ self.scope.set(var_name, replacement_class)
1672
+ self.global_scope.set(var_name, replacement_class)
1673
+ return
1674
+
1675
+ # Handle @ prefix (global reference)
1676
+ if ref_class.startswith('@'):
1677
+ var_name = ref_class[1:]
1678
+ self.global_scope.set(var_name, replacement_class)
1679
+ self._promoted_globals[var_name] = replacement_class
1680
+ return
1681
+
1682
+ # Handle regular class reference
1683
+ target_class = self.scope.get(ref_class)
1684
+ if target_class is None:
1685
+ target_class = self.global_scope.get(ref_class)
1686
+
1687
+ # Replace the class definition
1688
+ self.scope.set(ref_class, replacement_class)
1689
+ self.global_scope.set(ref_class, replacement_class)
1690
+ if ref_class in self._promoted_globals:
1691
+ self._promoted_globals[ref_class] = replacement_class
1692
+
1360
1693
  def _exec_typed_declaration(self, node: ASTNode) -> Any:
1361
1694
  """Execute typed variable declaration: type<T> varName = value;
1362
1695
 
@@ -1638,6 +1971,18 @@ class CSSLRuntime:
1638
1971
  modifiers = func_info.get('modifiers', [])
1639
1972
  kwargs = kwargs or {}
1640
1973
 
1974
+ # v4.2.5: Deferred &target replacement for non-embedded functions
1975
+ # If function has &target and hasn't been applied yet, apply now on first call
1976
+ append_ref_class = func_info.get('append_ref_class')
1977
+ if append_ref_class and not func_info.get('_target_applied', False):
1978
+ append_mode = func_info.get('append_mode', False)
1979
+ append_ref_member = func_info.get('append_ref_member')
1980
+ if append_mode:
1981
+ self._append_to_target(append_ref_class, append_ref_member, func_node)
1982
+ else:
1983
+ self._overwrite_target(append_ref_class, append_ref_member, func_node)
1984
+ func_node.value['_target_applied'] = True
1985
+
1641
1986
  # Check for undefined modifier - suppress errors if present
1642
1987
  is_undefined = 'undefined' in modifiers
1643
1988
 
@@ -2012,6 +2357,141 @@ class CSSLRuntime:
2012
2357
 
2013
2358
  return None
2014
2359
 
2360
+ def _exec_param_switch(self, node: ASTNode) -> Any:
2361
+ """Execute param switch statement for open parameters.
2362
+
2363
+ v4.2.5: Switch on which parameters were provided.
2364
+
2365
+ Syntax:
2366
+ switch(Params) {
2367
+ case name: // if 'name' param exists
2368
+ case name & age: // if both exist
2369
+ case name & not age: // if 'name' exists but 'age' doesn't
2370
+ except name: // if 'name' does NOT exist
2371
+ default: // fallback
2372
+ always: // always runs after match
2373
+ finally: // cleanup, always runs
2374
+ }
2375
+ """
2376
+ params_name = node.value.get('params')
2377
+ # v4.3.2: Use the actual variable from switch(Variable), not just _OpenKwargs
2378
+ # This allows switch(Input) where Input is the open parameter
2379
+ if params_name == 'Params':
2380
+ open_kwargs = self.scope.get('_OpenKwargs') or {}
2381
+ else:
2382
+ # Try to get kwargs from the named variable
2383
+ open_kwargs = self.scope.get(params_name)
2384
+ if open_kwargs is None:
2385
+ open_kwargs = self.scope.get('_OpenKwargs') or {}
2386
+ elif not isinstance(open_kwargs, dict):
2387
+ # If it's not a dict, try _OpenKwargs as fallback
2388
+ open_kwargs = self.scope.get('_OpenKwargs') or {}
2389
+
2390
+ matched = False
2391
+ always_node = None
2392
+ finally_node = None
2393
+ default_node = None
2394
+
2395
+ # First pass: find always, finally, default nodes
2396
+ for child in node.children:
2397
+ if child.type == 'param_always':
2398
+ always_node = child
2399
+ elif child.type == 'param_finally':
2400
+ finally_node = child
2401
+ elif child.type == 'param_default':
2402
+ default_node = child
2403
+
2404
+ try:
2405
+ # Execute matching cases
2406
+ for child in node.children:
2407
+ if child.type == 'param_case':
2408
+ condition = child.value.get('condition')
2409
+ if self._eval_param_condition(condition, open_kwargs):
2410
+ matched = True
2411
+ try:
2412
+ for stmt in child.children:
2413
+ self._execute_node(stmt)
2414
+ except CSSLBreak:
2415
+ break
2416
+
2417
+ # Execute default if no match
2418
+ if not matched and default_node:
2419
+ try:
2420
+ for stmt in default_node.children:
2421
+ self._execute_node(stmt)
2422
+ except CSSLBreak:
2423
+ pass
2424
+
2425
+ # Execute always block (runs after a match, before finally)
2426
+ if matched and always_node:
2427
+ try:
2428
+ for stmt in always_node.children:
2429
+ self._execute_node(stmt)
2430
+ except CSSLBreak:
2431
+ pass
2432
+
2433
+ finally:
2434
+ # Execute finally block (always runs, even on break/exception)
2435
+ if finally_node:
2436
+ for stmt in finally_node.children:
2437
+ self._execute_node(stmt)
2438
+
2439
+ return None
2440
+
2441
+ def _eval_param_condition(self, condition: dict, kwargs: dict) -> bool:
2442
+ """Evaluate a param switch condition.
2443
+
2444
+ Condition types:
2445
+ {'type': 'exists', 'param': 'name'} -> 'name' in kwargs OR variable 'name' is truthy
2446
+ {'type': 'not', 'param': 'name'} -> 'name' not in kwargs AND variable 'name' is falsy
2447
+ {'type': 'and', 'left': {...}, 'right': {...}} -> left AND right
2448
+ {'type': 'or', 'left': {...}, 'right': {...}} -> left OR right
2449
+
2450
+ v4.3.2: Added 'or' type for || operator support.
2451
+ v4.3.2: Enhanced to check both kwargs AND scope variables (from OpenFind).
2452
+ This allows positional args found via OpenFind<type>(index) to work
2453
+ with param_switch conditions like 'case text & !error:'.
2454
+ """
2455
+ cond_type = condition.get('type')
2456
+
2457
+ if cond_type == 'exists':
2458
+ param = condition.get('param')
2459
+ # Check kwargs first
2460
+ if param in kwargs:
2461
+ return True
2462
+ # Also check if a variable with this name exists in scope and is truthy
2463
+ # This allows OpenFind results to work with param_switch
2464
+ var_value = self.scope.get(param)
2465
+ if var_value is not None and var_value != '' and var_value != 0 and var_value != False:
2466
+ # Make sure it's not a builtin function
2467
+ if not callable(var_value):
2468
+ return True
2469
+ return False
2470
+
2471
+ elif cond_type == 'not':
2472
+ param = condition.get('param')
2473
+ # Check kwargs first
2474
+ if param in kwargs:
2475
+ return False
2476
+ # Also check if variable is truthy (then it's "provided")
2477
+ var_value = self.scope.get(param)
2478
+ if var_value is not None and var_value != '' and var_value != 0 and var_value != False:
2479
+ if not callable(var_value):
2480
+ return False # Variable has value, so it IS provided
2481
+ return True # Not in kwargs and variable is null/empty
2482
+
2483
+ elif cond_type == 'and':
2484
+ left = self._eval_param_condition(condition.get('left'), kwargs)
2485
+ right = self._eval_param_condition(condition.get('right'), kwargs)
2486
+ return left and right
2487
+
2488
+ elif cond_type == 'or':
2489
+ left = self._eval_param_condition(condition.get('left'), kwargs)
2490
+ right = self._eval_param_condition(condition.get('right'), kwargs)
2491
+ return left or right
2492
+
2493
+ return False
2494
+
2015
2495
  def _exec_return(self, node: ASTNode) -> Any:
2016
2496
  """Execute return statement.
2017
2497
 
@@ -2037,6 +2517,23 @@ class CSSLRuntime:
2037
2517
  """Execute continue statement"""
2038
2518
  raise CSSLContinue()
2039
2519
 
2520
+ def _exec_throw(self, node: ASTNode) -> Any:
2521
+ """Execute throw statement - v4.5.1
2522
+
2523
+ Throws a CSSLThrow exception that propagates to the nearest catch block.
2524
+
2525
+ Syntax:
2526
+ throw "Error message";
2527
+ throw errorVar;
2528
+ throw; // re-throw current exception
2529
+ """
2530
+ if node.value is None:
2531
+ # throw; - re-throw current exception (should be in a catch block)
2532
+ raise CSSLThrow("Re-thrown exception")
2533
+
2534
+ message = self._evaluate(node.value)
2535
+ raise CSSLThrow(message)
2536
+
2040
2537
  def _exec_constructor(self, node: ASTNode) -> Any:
2041
2538
  """Execute constructor node - only called when encountered directly.
2042
2539
 
@@ -2114,12 +2611,26 @@ class CSSLRuntime:
2114
2611
  return None
2115
2612
 
2116
2613
  def _exec_try(self, node: ASTNode) -> Any:
2117
- """Execute try/catch block"""
2614
+ """Execute try/catch/finally block"""
2118
2615
  try:
2119
2616
  for child in node.children:
2120
2617
  if child.type == 'try-block':
2121
2618
  for stmt in child.children:
2122
2619
  self._execute_node(stmt)
2620
+ except CSSLThrow as e:
2621
+ # v4.5.1: Handle user-thrown exceptions from throw statement
2622
+ for child in node.children:
2623
+ if child.type == 'catch-block':
2624
+ error_var = child.value.get('error_var') if child.value else None
2625
+ if error_var:
2626
+ # Store the thrown message in the error variable
2627
+ self.scope.set(error_var, e.message if e.message else str(e))
2628
+ for stmt in child.children:
2629
+ self._execute_node(stmt)
2630
+ break # Only execute first matching catch block
2631
+ else:
2632
+ # No catch block found - re-raise for outer try-catch
2633
+ raise
2123
2634
  except CSSLRuntimeError as e:
2124
2635
  for child in node.children:
2125
2636
  if child.type == 'catch-block':
@@ -2128,6 +2639,27 @@ class CSSLRuntime:
2128
2639
  self.scope.set(error_var, str(e))
2129
2640
  for stmt in child.children:
2130
2641
  self._execute_node(stmt)
2642
+ break
2643
+ else:
2644
+ raise # Re-raise if no catch block
2645
+ except Exception as e:
2646
+ # v4.2.6: Also catch Python exceptions
2647
+ for child in node.children:
2648
+ if child.type == 'catch-block':
2649
+ error_var = child.value.get('error_var') if child.value else None
2650
+ if error_var:
2651
+ self.scope.set(error_var, str(e))
2652
+ for stmt in child.children:
2653
+ self._execute_node(stmt)
2654
+ break
2655
+ else:
2656
+ raise # Re-raise if no catch block
2657
+ finally:
2658
+ # v4.2.6: Execute finally block if present
2659
+ for child in node.children:
2660
+ if child.type == 'finally-block':
2661
+ for stmt in child.children:
2662
+ self._execute_node(stmt)
2131
2663
 
2132
2664
  return None
2133
2665
 
@@ -2831,6 +3363,15 @@ class CSSLRuntime:
2831
3363
  final_value = {**current_value, **source}
2832
3364
  elif isinstance(current_value, str) and isinstance(source, str):
2833
3365
  final_value = current_value + source
3366
+ # Handle CSSL container types (DataStruct, Vector, Stack, etc.)
3367
+ elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add'):
3368
+ if hasattr(current_value, 'append'):
3369
+ current_value.append(source)
3370
+ elif hasattr(current_value, 'push'):
3371
+ current_value.push(source)
3372
+ elif hasattr(current_value, 'add'):
3373
+ current_value.add(source)
3374
+ final_value = current_value
2834
3375
  elif current_value is None:
2835
3376
  final_value = [source] if not isinstance(source, list) else source
2836
3377
  else:
@@ -3353,6 +3894,35 @@ class CSSLRuntime:
3353
3894
 
3354
3895
  if node.type == 'identifier':
3355
3896
  name = node.value
3897
+
3898
+ # Handle enum/namespace access: Colors::RED, MyNamespace::func
3899
+ if '::' in name:
3900
+ parts = name.split('::', 1)
3901
+ container_name = parts[0]
3902
+ member_name = parts[1]
3903
+
3904
+ # Look up the container (enum, class, or namespace)
3905
+ container = self.scope.get(container_name)
3906
+ if container is None:
3907
+ container = self.global_scope.get(container_name)
3908
+ if container is None:
3909
+ container = self._promoted_globals.get(container_name)
3910
+
3911
+ if container is not None:
3912
+ # If it's a dict-like object (enum or namespace), get the member
3913
+ if isinstance(container, dict):
3914
+ return container.get(member_name)
3915
+ # If it's an object with the member as an attribute
3916
+ elif hasattr(container, member_name):
3917
+ return getattr(container, member_name)
3918
+
3919
+ # v4.3.2: Check if full name exists as builtin function (json::write, string::cut, etc.)
3920
+ if self.builtins.has_function(name):
3921
+ return self.builtins.get_function(name)
3922
+
3923
+ # Fall through to normal lookup if container not found
3924
+ return None
3925
+
3356
3926
  value = self.scope.get(name)
3357
3927
  # Check if it's a class member in current instance context
3358
3928
  # This allows accessing members without 'this->' inside methods
@@ -3426,14 +3996,22 @@ class CSSLRuntime:
3426
3996
 
3427
3997
  if node.type == 'captured_ref':
3428
3998
  # %<name> captured reference - use value captured at infusion registration time
3999
+ # Priority: The % prefix means "get the ORIGINAL value before any replacement"
3429
4000
  name = node.value
3430
- # First check captured values from current injection context
4001
+
4002
+ # 1. First check captured values from current injection context
3431
4003
  if name in self._current_captured_values:
3432
4004
  captured_value = self._current_captured_values[name]
3433
- # Only use captured value if it's not None
3434
4005
  if captured_value is not None:
3435
4006
  return captured_value
3436
- # Fall back to normal resolution if not captured or capture was None
4007
+
4008
+ # 2. v4.2.3: Check _original_functions FIRST - this is the pre-replacement value
4009
+ # This ensures %exit() refers to the ORIGINAL exit when using &exit
4010
+ value = self._original_functions.get(name)
4011
+ if value is not None:
4012
+ return value
4013
+
4014
+ # 3. Fall back to scope/builtins if no original was captured
3437
4015
  value = self.scope.get(name)
3438
4016
  if value is None:
3439
4017
  value = self.global_scope.get(name)
@@ -3444,9 +4022,6 @@ class CSSLRuntime:
3444
4022
  value = lambda code=0, rt=runtime: rt.exit(code)
3445
4023
  else:
3446
4024
  value = getattr(self.builtins, f'builtin_{name}', None)
3447
- if value is None:
3448
- # Check original functions (for replaced functions)
3449
- value = self._original_functions.get(name)
3450
4025
  if value is not None:
3451
4026
  return value
3452
4027
  # Build helpful error for captured reference
@@ -3782,15 +4357,21 @@ class CSSLRuntime:
3782
4357
 
3783
4358
  if op == '/':
3784
4359
  r = self._to_number(right)
3785
- return self._to_number(left) / r if r != 0 else 0
4360
+ if r == 0:
4361
+ raise CSSLRuntimeError("Division by zero")
4362
+ return self._to_number(left) / r
3786
4363
 
3787
4364
  if op == '//':
3788
4365
  r = self._to_number(right)
3789
- return self._to_number(left) // r if r != 0 else 0
4366
+ if r == 0:
4367
+ raise CSSLRuntimeError("Integer division by zero")
4368
+ return self._to_number(left) // r
3790
4369
 
3791
4370
  if op == '%':
3792
4371
  r = self._to_number(right)
3793
- return self._to_number(left) % r if r != 0 else 0
4372
+ if r == 0:
4373
+ raise CSSLRuntimeError("Modulo by zero")
4374
+ return self._to_number(left) % r
3794
4375
 
3795
4376
  if op == '**':
3796
4377
  return self._to_number(left) ** self._to_number(right)
@@ -4144,30 +4725,44 @@ class CSSLRuntime:
4144
4725
  )
4145
4726
 
4146
4727
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
4147
- """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
4728
+ """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' expression.
4148
4729
 
4149
4730
  Creates a new instance of a CSSL class and calls its constructor.
4150
4731
  Supports multiple constructors (constr keyword), class parameters,
4151
4732
  and automatic parent constructor calling.
4152
4733
 
4153
4734
  With '@' prefix (new @ClassName), looks only in global scope.
4735
+ With Namespace:: prefix, looks in the namespace dict first.
4154
4736
  """
4155
4737
  class_name = node.value.get('class')
4738
+ namespace = node.value.get('namespace') # v4.2.6: Namespace::ClassName support
4156
4739
  is_global_ref = node.value.get('is_global_ref', False)
4157
4740
  args = [self._evaluate(arg) for arg in node.value.get('args', [])]
4158
4741
  kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
4159
4742
 
4160
- # Get class definition from scope
4161
- if is_global_ref:
4162
- # With @ prefix, only look in global scope
4163
- class_def = self._promoted_globals.get(class_name)
4164
- if class_def is None:
4165
- class_def = self.global_scope.get(class_name)
4166
- else:
4167
- # Normal lookup: local scope first, then global
4168
- class_def = self.scope.get(class_name)
4169
- if class_def is None:
4170
- class_def = self.global_scope.get(class_name)
4743
+ class_def = None
4744
+
4745
+ # v4.2.6: Handle Namespace::ClassName lookup
4746
+ if namespace:
4747
+ # Look up namespace dict first
4748
+ ns_dict = self.scope.get(namespace)
4749
+ if ns_dict is None:
4750
+ ns_dict = self.global_scope.get(namespace)
4751
+ if isinstance(ns_dict, dict) and class_name in ns_dict:
4752
+ class_def = ns_dict[class_name]
4753
+
4754
+ # Get class definition from scope (if not found in namespace)
4755
+ if class_def is None:
4756
+ if is_global_ref:
4757
+ # With @ prefix, only look in global scope
4758
+ class_def = self._promoted_globals.get(class_name)
4759
+ if class_def is None:
4760
+ class_def = self.global_scope.get(class_name)
4761
+ else:
4762
+ # Normal lookup: local scope first, then global
4763
+ class_def = self.scope.get(class_name)
4764
+ if class_def is None:
4765
+ class_def = self.global_scope.get(class_name)
4171
4766
 
4172
4767
  if class_def is None:
4173
4768
  # Build detailed error with suggestions
@@ -4216,6 +4811,18 @@ class CSSLRuntime:
4216
4811
  hint=f"'{class_name}' is of type {type(class_def).__name__}"
4217
4812
  )
4218
4813
 
4814
+ # v4.2.5: Deferred &target replacement for non-embedded classes
4815
+ # Apply on first instantiation if pending
4816
+ if hasattr(class_def, '_pending_target') and class_def._pending_target:
4817
+ pending = class_def._pending_target
4818
+ self._overwrite_class_target(
4819
+ pending['append_ref_class'],
4820
+ pending.get('append_ref_member'),
4821
+ class_def
4822
+ )
4823
+ class_def._target_applied = True
4824
+ class_def._pending_target = None # Clear pending
4825
+
4219
4826
  # Create new instance
4220
4827
  instance = CSSLInstance(class_def)
4221
4828
 
@@ -4646,6 +5253,33 @@ class CSSLRuntime:
4646
5253
  if isinstance(obj, Parameter) and member == 'return':
4647
5254
  member = 'return_'
4648
5255
 
5256
+ # === ServiceDefinition (from include()) ===
5257
+ if isinstance(obj, ServiceDefinition):
5258
+ # Check functions dict first
5259
+ if member in obj.functions:
5260
+ func_node = obj.functions[member]
5261
+ return lambda *args, **kwargs: self._call_function(func_node, list(args), kwargs)
5262
+ # Check structs dict
5263
+ if member in obj.structs:
5264
+ return obj.structs[member]
5265
+ # Check regular attributes
5266
+ if hasattr(obj, member):
5267
+ return getattr(obj, member)
5268
+ # Build helpful error
5269
+ available = list(obj.functions.keys()) + list(obj.structs.keys())
5270
+ similar = _find_similar_names(member, available)
5271
+ if similar:
5272
+ hint = f"Did you mean: {', '.join(similar)}?"
5273
+ elif available:
5274
+ hint = f"Available: {', '.join(available[:10])}"
5275
+ else:
5276
+ hint = "No functions or structs defined in this module."
5277
+ raise self._format_error(
5278
+ node.line if hasattr(node, 'line') else 0,
5279
+ f"Module has no function or struct '{member}'",
5280
+ hint
5281
+ )
5282
+
4649
5283
  # === CSSL CLASS INSTANCE METHODS ===
4650
5284
  if isinstance(obj, CSSLInstance):
4651
5285
  # Check for member variable
@@ -4939,12 +5573,19 @@ class CSSLRuntime:
4939
5573
  index = self._evaluate(node.value.get('index'))
4940
5574
 
4941
5575
  if obj is None:
4942
- return None
5576
+ raise CSSLRuntimeError(f"Cannot index into None/null value")
4943
5577
 
4944
5578
  try:
4945
5579
  return obj[index]
4946
- except (IndexError, KeyError, TypeError):
4947
- return None
5580
+ except IndexError:
5581
+ # v4.2.6: Throw error instead of returning None
5582
+ length = len(obj) if hasattr(obj, '__len__') else 'unknown'
5583
+ raise CSSLRuntimeError(f"Index {index} out of bounds (length: {length})")
5584
+ except KeyError:
5585
+ # v4.2.6: Throw error for missing dict keys
5586
+ raise CSSLRuntimeError(f"Key '{index}' not found in dictionary")
5587
+ except TypeError as e:
5588
+ raise CSSLRuntimeError(f"Cannot index: {e}")
4948
5589
 
4949
5590
  def _set_member(self, node: ASTNode, value: Any):
4950
5591
  """Set member value"""
@@ -5238,10 +5879,17 @@ class CSSLRuntime:
5238
5879
 
5239
5880
  def _emit_output(self, text: str, level: str = 'normal'):
5240
5881
  """Emit output through callback or print"""
5882
+ import sys
5241
5883
  if self._output_callback:
5242
5884
  self._output_callback(text, level)
5243
5885
  else:
5244
- print(text, end='')
5886
+ # Handle encoding issues on Windows console
5887
+ try:
5888
+ print(text, end='')
5889
+ except UnicodeEncodeError:
5890
+ # Fallback: encode with errors='replace' for unsupported chars
5891
+ encoded = text.encode(sys.stdout.encoding or 'utf-8', errors='replace')
5892
+ print(encoded.decode(sys.stdout.encoding or 'utf-8', errors='replace'), end='')
5245
5893
 
5246
5894
  def output(self, text: str):
5247
5895
  """Output text"""
@@ -5369,14 +6017,79 @@ class CSSLServiceRunner:
5369
6017
  return list(self.running_services.keys())
5370
6018
 
5371
6019
 
5372
- # Convenience function
5373
- def run_cssl(source: str, service_engine=None) -> Any:
5374
- """Run CSSL source code"""
6020
+ # C++ interpreter cache
6021
+ _cpp_interpreter = None
6022
+
6023
+
6024
+ def _get_cpp_interpreter():
6025
+ """Get or create C++ interpreter instance."""
6026
+ global _cpp_interpreter
6027
+ if _cpp_interpreter is not None:
6028
+ return _cpp_interpreter
6029
+
6030
+ try:
6031
+ from . import _CPP_AVAILABLE, _cpp_module
6032
+ if _CPP_AVAILABLE and _cpp_module and hasattr(_cpp_module, 'Interpreter'):
6033
+ _cpp_interpreter = _cpp_module.Interpreter()
6034
+ return _cpp_interpreter
6035
+ except Exception:
6036
+ pass
6037
+ return None
6038
+
6039
+
6040
+ def run_cssl(source: str, service_engine=None, force_python: bool = False) -> Any:
6041
+ """Run CSSL source code.
6042
+
6043
+ Uses C++ interpreter for maximum performance when available.
6044
+ Falls back to Python interpreter for unsupported features.
6045
+
6046
+ Args:
6047
+ source: CSSL source code
6048
+ service_engine: Optional service engine for external integrations
6049
+ force_python: Force Python interpreter (for debugging)
6050
+
6051
+ Returns:
6052
+ Execution result
6053
+ """
6054
+ # Try C++ interpreter first (10-20x faster)
6055
+ if not force_python:
6056
+ cpp_interp = _get_cpp_interpreter()
6057
+ if cpp_interp:
6058
+ try:
6059
+ return cpp_interp.run_string(source)
6060
+ except Exception as e:
6061
+ # C++ doesn't support this feature, fall back to Python
6062
+ error_msg = str(e).lower()
6063
+ # Only fall back for unsupported features, not syntax errors
6064
+ if 'unsupported' in error_msg or 'not implemented' in error_msg:
6065
+ pass # Fall through to Python
6066
+ else:
6067
+ # Re-raise actual errors
6068
+ raise CSSLRuntimeError(str(e))
6069
+
6070
+ # Python fallback (full feature support)
5375
6071
  runtime = CSSLRuntime(service_engine)
5376
6072
  return runtime.execute(source)
5377
6073
 
5378
6074
 
5379
- def run_cssl_file(filepath: str, service_engine=None) -> Any:
5380
- """Run a CSSL file"""
6075
+ def run_cssl_file(filepath: str, service_engine=None, force_python: bool = False) -> Any:
6076
+ """Run a CSSL file.
6077
+
6078
+ Uses C++ interpreter for maximum performance when available.
6079
+ """
6080
+ # Try C++ interpreter first
6081
+ if not force_python:
6082
+ cpp_interp = _get_cpp_interpreter()
6083
+ if cpp_interp:
6084
+ try:
6085
+ return cpp_interp.run(filepath)
6086
+ except Exception as e:
6087
+ error_msg = str(e).lower()
6088
+ if 'unsupported' in error_msg or 'not implemented' in error_msg:
6089
+ pass # Fall through to Python
6090
+ else:
6091
+ raise CSSLRuntimeError(str(e))
6092
+
6093
+ # Python fallback
5381
6094
  runtime = CSSLRuntime(service_engine)
5382
6095
  return runtime.execute_file(filepath)