IncludeCPP 3.8.0__py3-none-any.whl → 3.8.9__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.
@@ -715,17 +715,60 @@ class CSSLRuntime:
715
715
 
716
716
  Parses class members and methods, creating a CSSLClass object
717
717
  that can be instantiated with 'new'.
718
+ Supports inheritance via 'extends' keyword and method overwriting via 'overwrites'.
718
719
  """
719
720
  class_info = node.value
720
721
  class_name = class_info.get('name')
722
+ extends_class_name = class_info.get('extends')
723
+ extends_is_python = class_info.get('extends_is_python', False)
724
+ overwrites_class_name = class_info.get('overwrites')
725
+ overwrites_is_python = class_info.get('overwrites_is_python', False)
726
+
727
+ # Resolve parent class if extends is specified
728
+ parent_class = None
729
+ if extends_class_name:
730
+ if extends_is_python:
731
+ # extends $PythonObject - look up in shared objects
732
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
733
+ if extends_class_name in _live_objects:
734
+ parent_class = _live_objects[extends_class_name]
735
+ # Unwrap SharedObjectProxy if needed
736
+ if isinstance(parent_class, SharedObjectProxy):
737
+ parent_class = parent_class._obj
738
+ else:
739
+ # Also check scope with $ prefix
740
+ parent_class = self.global_scope.get(f'${extends_class_name}')
741
+ else:
742
+ # Try to resolve from scope (could be CSSL class or variable holding Python object)
743
+ parent_class = self.scope.get(extends_class_name)
744
+ if parent_class is None:
745
+ parent_class = self.global_scope.get(extends_class_name)
746
+
747
+ if parent_class is None:
748
+ raise ValueError(f"Cannot extend unknown class '{extends_class_name}'")
749
+
750
+ # Auto-wrap Python objects for inheritance
751
+ from .cssl_builtins import CSSLizedPythonObject
752
+ if not isinstance(parent_class, (CSSLClass, CSSLizedPythonObject)):
753
+ # Wrap raw Python object
754
+ parent_class = CSSLizedPythonObject(parent_class, self)
721
755
 
722
756
  members = {} # Member variable defaults/types
723
757
  methods = {} # Method AST nodes
724
- constructor = None
758
+ constructors = [] # List of constructors (multiple allowed with constr keyword)
759
+ constructor = None # Primary constructor (backward compatibility)
760
+
761
+ # Get class parameters and extends args
762
+ class_params = class_info.get('class_params', [])
763
+ extends_args = class_info.get('extends_args', [])
725
764
 
726
765
  for child in node.children:
727
- if child.type == 'function':
728
- # This is a method
766
+ if child.type == 'constructor':
767
+ # New-style constructor from 'constr' keyword
768
+ constructors.append(child)
769
+
770
+ elif child.type == 'function':
771
+ # This is a method or old-style constructor
729
772
  func_info = child.value
730
773
  method_name = func_info.get('name')
731
774
 
@@ -752,32 +795,170 @@ class CSSLRuntime:
752
795
  name=class_name,
753
796
  members=members,
754
797
  methods=methods,
755
- constructor=constructor
798
+ constructor=constructor,
799
+ parent=parent_class
756
800
  )
801
+ # Store additional constructor info
802
+ class_def.constructors = constructors # Multiple constructors from 'constr' keyword
803
+ class_def.class_params = class_params # Class-level constructor parameters
804
+ class_def.extends_args = extends_args # Arguments to pass to parent constructor
757
805
 
758
806
  # Register class in scope
759
807
  self.scope.set(class_name, class_def)
760
808
  self.global_scope.set(class_name, class_def)
761
809
 
810
+ # Handle class overwrites - replace methods in target class
811
+ if overwrites_class_name:
812
+ self._apply_class_overwrites(
813
+ class_def, overwrites_class_name, overwrites_is_python
814
+ )
815
+
762
816
  return class_def
763
817
 
818
+ def _apply_class_overwrites(self, new_class: CSSLClass, target_name: str, is_python: bool):
819
+ """Apply method overwrites from new_class to target class/object.
820
+
821
+ When a class has 'overwrites' specified, all methods defined in new_class
822
+ will replace the corresponding methods in the target.
823
+ """
824
+ from .cssl_builtins import CSSLizedPythonObject
825
+
826
+ # Resolve target
827
+ target = None
828
+ if is_python:
829
+ from ..cssl_bridge import _live_objects
830
+ if target_name in _live_objects:
831
+ target = _live_objects[target_name]
832
+ else:
833
+ target = self.scope.get(target_name)
834
+ if target is None:
835
+ target = self.global_scope.get(target_name)
836
+
837
+ if target is None:
838
+ return # Target not found, silently skip
839
+
840
+ # Get methods to overwrite
841
+ methods_to_overwrite = new_class.methods
842
+
843
+ if is_python and hasattr(target, '__class__'):
844
+ # Python object - overwrite methods on the object/class
845
+ for method_name, method_node in methods_to_overwrite.items():
846
+ # Create a Python-callable wrapper for the CSSL method
847
+ wrapper = self._create_method_wrapper(method_node, target)
848
+ # Set on the object
849
+ try:
850
+ setattr(target, method_name, wrapper)
851
+ except AttributeError:
852
+ # Try setting on class instead
853
+ try:
854
+ setattr(target.__class__, method_name, wrapper)
855
+ except:
856
+ pass
857
+ elif isinstance(target, CSSLClass):
858
+ # CSSL class - directly replace methods
859
+ for method_name, method_node in methods_to_overwrite.items():
860
+ target.methods[method_name] = method_node
861
+ elif isinstance(target, CSSLizedPythonObject):
862
+ # CSSLized Python object - get underlying object and overwrite
863
+ py_obj = target.get_python_obj()
864
+ for method_name, method_node in methods_to_overwrite.items():
865
+ wrapper = self._create_method_wrapper(method_node, py_obj)
866
+ try:
867
+ setattr(py_obj, method_name, wrapper)
868
+ except AttributeError:
869
+ try:
870
+ setattr(py_obj.__class__, method_name, wrapper)
871
+ except:
872
+ pass
873
+
874
+ def _create_method_wrapper(self, method_node: ASTNode, instance: Any):
875
+ """Create a Python-callable wrapper for a CSSL method that works with an instance."""
876
+ def wrapper(*args, **kwargs):
877
+ # Set up instance context for this->
878
+ old_instance = self._current_instance
879
+ # Create a fake CSSLInstance-like wrapper if needed
880
+ self._current_instance = instance
881
+ try:
882
+ return self._call_function(method_node, list(args), kwargs)
883
+ finally:
884
+ self._current_instance = old_instance
885
+ return wrapper
886
+
764
887
  def _exec_function(self, node: ASTNode) -> Any:
765
- """Execute function definition - just registers it"""
888
+ """Execute function definition - registers it and handles extends/overwrites.
889
+
890
+ Syntax:
891
+ define func() { ... }
892
+ define func : extends otherFunc() { ... } - Inherit local vars
893
+ define func : overwrites otherFunc() { ... } - Replace otherFunc
894
+ """
766
895
  func_info = node.value
767
896
  func_name = func_info.get('name')
897
+ extends_func = func_info.get('extends')
898
+ extends_is_python = func_info.get('extends_is_python', False)
899
+ overwrites_func = func_info.get('overwrites')
900
+ overwrites_is_python = func_info.get('overwrites_is_python', False)
901
+
902
+ # Store function extends info for runtime use
903
+ if extends_func:
904
+ node.value['_extends_resolved'] = self._resolve_function_target(
905
+ extends_func, extends_is_python
906
+ )
907
+
908
+ # Handle overwrites - replace the target function
909
+ if overwrites_func:
910
+ target = self._resolve_function_target(overwrites_func, overwrites_is_python)
911
+ if target is not None:
912
+ # Store original for reference
913
+ node.value['_overwrites_original'] = target
914
+ # Replace the target function with this one
915
+ if overwrites_is_python:
916
+ from ..cssl_bridge import _live_objects
917
+ if overwrites_func in _live_objects:
918
+ # Create a wrapper that calls the CSSL function
919
+ _live_objects[overwrites_func] = self._create_python_wrapper(node)
920
+ else:
921
+ # Replace in CSSL scope
922
+ self.scope.set(overwrites_func, node)
923
+ self.global_scope.set(overwrites_func, node)
924
+
925
+ # Register the function
768
926
  self.scope.set(func_name, node)
769
927
  return None
770
928
 
929
+ def _resolve_function_target(self, name: str, is_python: bool) -> Any:
930
+ """Resolve a function target for extends/overwrites."""
931
+ if is_python:
932
+ from ..cssl_bridge import _live_objects
933
+ if name in _live_objects:
934
+ return _live_objects[name]
935
+ return self.global_scope.get(f'${name}')
936
+ else:
937
+ target = self.scope.get(name)
938
+ if target is None:
939
+ target = self.global_scope.get(name)
940
+ return target
941
+
942
+ def _create_python_wrapper(self, func_node: ASTNode):
943
+ """Create a Python-callable wrapper for a CSSL function."""
944
+ def wrapper(*args, **kwargs):
945
+ return self._call_function(func_node, list(args), kwargs)
946
+ return wrapper
947
+
771
948
  def _exec_typed_declaration(self, node: ASTNode) -> Any:
772
949
  """Execute typed variable declaration: type<T> varName = value;
773
950
 
774
951
  Creates appropriate type instances for stack, vector, datastruct, etc.
952
+
953
+ The * prefix indicates a non-nullable variable (can never be None/null).
954
+ Example: vector<dynamic> *MyVector - can never contain None values.
775
955
  """
776
956
  decl = node.value
777
957
  type_name = decl.get('type')
778
958
  element_type = decl.get('element_type', 'dynamic')
779
959
  var_name = decl.get('name')
780
960
  value_node = decl.get('value')
961
+ non_null = decl.get('non_null', False)
781
962
 
782
963
  # Create the appropriate type instance
783
964
  if type_name == 'stack':
@@ -825,11 +1006,32 @@ class CSSLRuntime:
825
1006
  # For container types, the value might be initialization data
826
1007
  init_value = self._evaluate(value_node)
827
1008
  if isinstance(init_value, (list, tuple)):
1009
+ # For non-null containers, filter out None values
1010
+ if non_null:
1011
+ init_value = [v for v in init_value if v is not None]
828
1012
  instance.extend(init_value)
829
1013
  elif init_value is not None:
830
1014
  if hasattr(instance, 'append'):
831
1015
  instance.append(init_value)
832
1016
 
1017
+ # Non-null enforcement: container types get wrapped to filter None on operations
1018
+ if non_null:
1019
+ # Mark the instance as non-null for runtime checks
1020
+ if hasattr(instance, '_non_null'):
1021
+ instance._non_null = True
1022
+ # Track non-null variables for assignment enforcement
1023
+ if not hasattr(self, '_non_null_vars'):
1024
+ self._non_null_vars = set()
1025
+ self._non_null_vars.add(var_name)
1026
+
1027
+ # Ensure initial value is not None for non-null variables
1028
+ if instance is None:
1029
+ raise CSSLRuntimeError(
1030
+ f"Non-null variable '*{var_name}' cannot be initialized to None",
1031
+ node.line,
1032
+ hint="Use a default value or remove the * prefix"
1033
+ )
1034
+
833
1035
  # Check for global modifier
834
1036
  modifiers = decl.get('modifiers', [])
835
1037
  is_global = 'global' in modifiers
@@ -1006,6 +1208,9 @@ class CSSLRuntime:
1006
1208
  func_node: The function AST node
1007
1209
  args: List of positional arguments
1008
1210
  kwargs: Dict of named arguments (param_name -> value)
1211
+
1212
+ Supports:
1213
+ define func : extends otherFunc() { ... } - Inherit local vars from otherFunc
1009
1214
  """
1010
1215
  func_info = func_node.value
1011
1216
  params = func_info.get('params', [])
@@ -1018,6 +1223,34 @@ class CSSLRuntime:
1018
1223
  # Create new scope
1019
1224
  new_scope = Scope(parent=self.scope)
1020
1225
 
1226
+ # Handle function extends - inherit local vars from extended function
1227
+ extends_resolved = func_info.get('_extends_resolved')
1228
+ if extends_resolved:
1229
+ if callable(extends_resolved):
1230
+ # Python function - call it first to populate any state
1231
+ try:
1232
+ extends_resolved(*args, **kwargs)
1233
+ except:
1234
+ pass
1235
+ elif hasattr(extends_resolved, 'value'):
1236
+ # CSSL function - execute it in a temporary scope to get local vars
1237
+ old_scope = self.scope
1238
+ temp_scope = Scope(parent=self.scope)
1239
+ self.scope = temp_scope
1240
+ try:
1241
+ # Execute extended function body to populate local vars
1242
+ for child in extends_resolved.children:
1243
+ if not self._running:
1244
+ break
1245
+ self._execute_node(child)
1246
+ # Copy all local vars to new scope
1247
+ for name, value in temp_scope._vars.items():
1248
+ new_scope.set(name, value)
1249
+ except:
1250
+ pass
1251
+ finally:
1252
+ self.scope = old_scope
1253
+
1021
1254
  # Bind parameters - handle both positional and named arguments
1022
1255
  for i, param in enumerate(params):
1023
1256
  # Extract param name and type from dict format: {'name': 'a', 'type': 'int'}
@@ -1029,10 +1262,20 @@ class CSSLRuntime:
1029
1262
  param_type = ''
1030
1263
 
1031
1264
  # Check if this is an 'open' parameter - receives all args as a list
1032
- if param_type == 'open' or param_name == 'Params':
1265
+ # The parser sets param['open'] = True for 'open' keyword
1266
+ is_open_param = (isinstance(param, dict) and param.get('open', False)) or param_name == 'Params'
1267
+ if is_open_param:
1033
1268
  # 'open Params' receives all arguments as a list
1034
- new_scope.set(param_name, list(args))
1035
- new_scope.set('Params', list(args)) # Also set 'Params' for OpenFind
1269
+ # Check for non_null flag: open *Params filters out None values
1270
+ is_non_null = isinstance(param, dict) and param.get('non_null', False)
1271
+ args_list = list(args)
1272
+ if is_non_null:
1273
+ args_list = [a for a in args_list if a is not None]
1274
+ # Also filter kwargs
1275
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
1276
+ new_scope.set(param_name, args_list)
1277
+ new_scope.set('Params', args_list) # Also set 'Params' for OpenFind
1278
+ new_scope.set('_OpenKwargs', kwargs) # Store kwargs for OpenFind<type, "name">
1036
1279
  elif param_name in kwargs:
1037
1280
  # Named argument takes priority
1038
1281
  new_scope.set(param_name, kwargs[param_name])
@@ -1047,6 +1290,17 @@ class CSSLRuntime:
1047
1290
  self.scope = new_scope
1048
1291
 
1049
1292
  try:
1293
+ # Handle append mode (++) - execute referenced function first
1294
+ append_mode = func_info.get('append_mode', False)
1295
+ append_ref_class = func_info.get('append_ref_class')
1296
+ append_ref_member = func_info.get('append_ref_member')
1297
+
1298
+ if append_mode and append_ref_class:
1299
+ self._execute_append_reference(
1300
+ None, append_ref_class, append_ref_member,
1301
+ args, kwargs, {}, is_constructor=False
1302
+ )
1303
+
1050
1304
  for child in func_node.children:
1051
1305
  # Check if exit() was called
1052
1306
  if not self._running:
@@ -1267,6 +1521,82 @@ class CSSLRuntime:
1267
1521
  """Execute continue statement"""
1268
1522
  raise CSSLContinue()
1269
1523
 
1524
+ def _exec_constructor(self, node: ASTNode) -> Any:
1525
+ """Execute constructor node - only called when encountered directly.
1526
+
1527
+ Normally constructors are executed through _call_constructor in _eval_new.
1528
+ This handles cases where a constructor node is executed in other contexts.
1529
+ """
1530
+ # Constructor nodes should be handled during class instantiation
1531
+ # If we reach here, it's in a context where the constructor is stored but not executed
1532
+ return None
1533
+
1534
+ def _exec_super_call(self, node: ASTNode) -> Any:
1535
+ """Execute super() call to invoke parent constructor or method.
1536
+
1537
+ Syntax:
1538
+ super() - Call parent constructor with no args
1539
+ super(arg1, arg2) - Call parent constructor with args
1540
+ super::method() - Call specific parent method
1541
+ super::method(args) - Call specific parent method with args
1542
+ """
1543
+ if self._current_instance is None:
1544
+ raise CSSLRuntimeError(
1545
+ "super() called outside of class context",
1546
+ node.line if hasattr(node, 'line') else 0,
1547
+ hint="super() can only be used inside class constructors and methods"
1548
+ )
1549
+
1550
+ instance = self._current_instance
1551
+
1552
+ # Try to get parent from instance first, then from class definition
1553
+ parent = getattr(instance, '_parent_class', None)
1554
+ if parent is None and hasattr(instance, '_class') and instance._class:
1555
+ parent = getattr(instance._class, 'parent', None)
1556
+
1557
+ if parent is None:
1558
+ raise CSSLRuntimeError(
1559
+ "super() called but class has no parent",
1560
+ node.line if hasattr(node, 'line') else 0,
1561
+ hint="super() requires the class to extend another class"
1562
+ )
1563
+
1564
+ method_name = node.value.get('method')
1565
+ args = [self._evaluate(arg) for arg in node.value.get('args', [])]
1566
+
1567
+ from .cssl_builtins import CSSLizedPythonObject
1568
+
1569
+ if method_name:
1570
+ # super::method() - call specific parent method
1571
+ if isinstance(parent, CSSLClass):
1572
+ method = parent.methods.get(method_name)
1573
+ if method:
1574
+ return self._call_method(instance, method, args, {})
1575
+ else:
1576
+ raise CSSLRuntimeError(
1577
+ f"Parent class has no method '{method_name}'",
1578
+ node.line if hasattr(node, 'line') else 0
1579
+ )
1580
+ elif isinstance(parent, CSSLizedPythonObject):
1581
+ py_obj = parent.get_python_obj()
1582
+ if hasattr(py_obj, method_name):
1583
+ method = getattr(py_obj, method_name)
1584
+ return method(*args)
1585
+ else:
1586
+ raise CSSLRuntimeError(
1587
+ f"Parent Python object has no method '{method_name}'",
1588
+ node.line if hasattr(node, 'line') else 0
1589
+ )
1590
+ elif hasattr(parent, method_name):
1591
+ method = getattr(parent, method_name)
1592
+ return method(*args)
1593
+ else:
1594
+ # super() - call parent constructor
1595
+ self._call_parent_constructor(instance, args)
1596
+ instance._parent_constructor_called = True
1597
+
1598
+ return None
1599
+
1270
1600
  def _exec_try(self, node: ASTNode) -> Any:
1271
1601
  """Execute try/catch block"""
1272
1602
  try:
@@ -2308,6 +2638,9 @@ class CSSLRuntime:
2308
2638
 
2309
2639
  if node.type == 'literal':
2310
2640
  value = node.value
2641
+ # Handle dict-format literals from parser: {'type': 'int', 'value': 0}
2642
+ if isinstance(value, dict) and 'value' in value:
2643
+ value = value['value']
2311
2644
  # String interpolation - replace {var} or <var> with scope values
2312
2645
  if isinstance(value, str):
2313
2646
  has_fstring = '{' in value and '}' in value
@@ -2783,19 +3116,20 @@ class CSSLRuntime:
2783
3116
  )
2784
3117
 
2785
3118
  def _eval_typed_call(self, node: ASTNode) -> Any:
2786
- """Evaluate typed function call like OpenFind<string>(0)"""
3119
+ """Evaluate typed function call like OpenFind<string>(0) or OpenFind<dynamic, "name">"""
2787
3120
  name = node.value.get('name')
2788
3121
  type_param = node.value.get('type_param', 'dynamic')
3122
+ param_name = node.value.get('param_name') # For OpenFind<type, "name">
2789
3123
  args = [self._evaluate(a) for a in node.value.get('args', [])]
2790
3124
 
2791
- # Handle OpenFind<type>(index)
3125
+ # Handle OpenFind<type>(index) or OpenFind<type, "name">
2792
3126
  if name == 'OpenFind':
2793
3127
  # OpenFind searches for a value of the specified type
2794
3128
  # from the open parameters in scope
2795
3129
  open_params = self.scope.get('Params') or []
2796
- index = args[0] if args else 0
3130
+ open_kwargs = self.scope.get('_OpenKwargs') or {}
2797
3131
 
2798
- # Search for value of matching type at or near the index
3132
+ # Type mapping for type checking
2799
3133
  type_map = {
2800
3134
  'string': str, 'str': str,
2801
3135
  'int': int, 'integer': int,
@@ -2803,10 +3137,23 @@ class CSSLRuntime:
2803
3137
  'bool': bool, 'boolean': bool,
2804
3138
  'list': list, 'array': list,
2805
3139
  'dict': dict, 'json': dict,
3140
+ 'dynamic': None, # Accept any type
2806
3141
  }
2807
-
2808
3142
  target_type = type_map.get(type_param.lower())
2809
3143
 
3144
+ # If param_name is specified, search by name in kwargs
3145
+ # OpenFind<dynamic, "tasks"> -> searches for MyFunc(tasks="value")
3146
+ if param_name:
3147
+ if param_name in open_kwargs:
3148
+ value = open_kwargs[param_name]
3149
+ # Type check if not dynamic
3150
+ if target_type is None or isinstance(value, target_type):
3151
+ return value
3152
+ return None
3153
+
3154
+ # Otherwise, search by index in positional args
3155
+ index = args[0] if args else 0
3156
+
2810
3157
  if isinstance(open_params, (list, tuple)):
2811
3158
  # Find first matching type starting from index
2812
3159
  for i in range(index, len(open_params)):
@@ -2827,14 +3174,16 @@ class CSSLRuntime:
2827
3174
  raise CSSLRuntimeError(
2828
3175
  f"Unknown typed function: {name}<{type_param}>",
2829
3176
  node.line,
2830
- context=f"Available typed functions: OpenFind<type>",
2831
- hint="Typed functions use format: FunctionName<Type>(args)"
3177
+ context=f"Available typed functions: OpenFind<type>, OpenFind<type, \"name\">",
3178
+ hint="Use OpenFind<type>(index) for positional or OpenFind<type, \"name\"> for named params"
2832
3179
  )
2833
3180
 
2834
3181
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
2835
3182
  """Evaluate 'new ClassName(args)' expression.
2836
3183
 
2837
3184
  Creates a new instance of a CSSL class and calls its constructor.
3185
+ Supports multiple constructors (constr keyword), class parameters,
3186
+ and automatic parent constructor calling.
2838
3187
  """
2839
3188
  class_name = node.value.get('class')
2840
3189
  args = [self._evaluate(arg) for arg in node.value.get('args', [])]
@@ -2862,12 +3211,242 @@ class CSSLRuntime:
2862
3211
  # Create new instance
2863
3212
  instance = CSSLInstance(class_def)
2864
3213
 
2865
- # Call constructor if defined
3214
+ # Store parent class reference for super() calls
3215
+ instance._parent_class = class_def.parent
3216
+ instance._parent_constructor_called = False
3217
+
3218
+ # Get class params and extends args
3219
+ class_params = getattr(class_def, 'class_params', [])
3220
+ extends_args = getattr(class_def, 'extends_args', [])
3221
+ constructors = getattr(class_def, 'constructors', [])
3222
+
3223
+ # Bind class_params to instance scope (they receive values from constructor args)
3224
+ # These are the implicit constructor parameters defined in class declaration
3225
+ param_values = {}
3226
+ for i, param in enumerate(class_params):
3227
+ param_name = param.get('name') if isinstance(param, dict) else param
3228
+ if i < len(args):
3229
+ param_values[param_name] = args[i]
3230
+ else:
3231
+ param_values[param_name] = None
3232
+
3233
+ # Call parent constructor with extends_args if parent exists and args specified
3234
+ if class_def.parent and extends_args:
3235
+ evaluated_extends_args = [self._evaluate(arg) for arg in extends_args]
3236
+ self._call_parent_constructor(instance, evaluated_extends_args)
3237
+ instance._parent_constructor_called = True
3238
+
3239
+ # Execute all constructors defined with 'constr' keyword (in order)
3240
+ for constr in constructors:
3241
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3242
+
3243
+ # Call primary constructor (old-style) if defined
2866
3244
  if class_def.constructor:
2867
3245
  self._call_method(instance, class_def.constructor, args, kwargs)
2868
3246
 
2869
3247
  return instance
2870
3248
 
3249
+ def _call_parent_constructor(self, instance: CSSLInstance, args: list, kwargs: dict = None):
3250
+ """Call the parent class constructor on an instance.
3251
+
3252
+ Used for automatic parent constructor calling and super() calls.
3253
+ """
3254
+ kwargs = kwargs or {}
3255
+ parent = instance._parent_class
3256
+
3257
+ if parent is None:
3258
+ return
3259
+
3260
+ from .cssl_builtins import CSSLizedPythonObject
3261
+
3262
+ if isinstance(parent, CSSLClass):
3263
+ # CSSL parent class
3264
+ if parent.constructor:
3265
+ self._call_method(instance, parent.constructor, args, kwargs)
3266
+ # Also call parent's constr constructors
3267
+ for constr in getattr(parent, 'constructors', []):
3268
+ self._call_constructor(instance, constr, args, kwargs, {})
3269
+ elif isinstance(parent, CSSLizedPythonObject):
3270
+ # Python parent - call __init__ if it's a class
3271
+ py_obj = parent.get_python_obj()
3272
+ if isinstance(py_obj, type):
3273
+ # It's a class - we need to initialize it
3274
+ try:
3275
+ py_obj.__init__(instance, *args, **kwargs)
3276
+ except TypeError:
3277
+ pass # Initialization might not be needed
3278
+ elif isinstance(parent, type):
3279
+ # Raw Python class
3280
+ try:
3281
+ parent.__init__(instance, *args, **kwargs)
3282
+ except TypeError:
3283
+ pass
3284
+
3285
+ def _execute_append_reference(self, instance: CSSLInstance, ref_class: str, ref_member: str,
3286
+ args: list, kwargs: dict, param_values: dict, is_constructor: bool = True):
3287
+ """Execute referenced parent constructor/method for append mode (++).
3288
+
3289
+ Resolves the class/instance reference and executes the specified member.
3290
+ Supports:
3291
+ - Static class references: &ClassName::member
3292
+ - Dynamic instance references: &$instanceVar::member
3293
+ - Direct function references: &FunctionName (for define functions)
3294
+
3295
+ Args:
3296
+ instance: Current class instance (can be None for standalone functions)
3297
+ ref_class: Referenced class name, $instanceVar, or function name
3298
+ ref_member: Member name (constructor name, 'constructors', or method name) - can be None
3299
+ args: Arguments to pass
3300
+ kwargs: Keyword arguments to pass
3301
+ param_values: Parameter values from class params
3302
+ is_constructor: True if looking for constructor, False for method
3303
+ """
3304
+ from .cssl_builtins import CSSLizedPythonObject
3305
+
3306
+ # Handle direct function reference: &FunctionName ++ (no ::member part)
3307
+ if ref_member is None and not is_constructor:
3308
+ # ref_class is actually a function name
3309
+ func_name = ref_class
3310
+ if func_name.startswith('$'):
3311
+ func_name = func_name[1:]
3312
+
3313
+ # Look for the function in scope
3314
+ func = self.scope.get(func_name) or self.global_scope.get(func_name)
3315
+ if func is not None:
3316
+ if isinstance(func, ASTNode) and func.type in ('function', 'FUNCTION'):
3317
+ # Execute the referenced function
3318
+ if instance:
3319
+ self._call_method(instance, func, args, kwargs)
3320
+ else:
3321
+ self._call_function(func, args, kwargs)
3322
+ elif callable(func):
3323
+ # It's a callable (Python function or lambda)
3324
+ func(*args, **kwargs)
3325
+ return
3326
+
3327
+ # Resolve the class/instance reference
3328
+ if ref_class.startswith('$'):
3329
+ # Dynamic instance reference: &$instanceVar::member
3330
+ var_name = ref_class[1:]
3331
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3332
+ if ref_obj is None:
3333
+ return # Instance not found, skip silently
3334
+
3335
+ if isinstance(ref_obj, CSSLInstance):
3336
+ # Get the class definition from the instance
3337
+ target_class = ref_obj.class_def
3338
+ elif isinstance(ref_obj, CSSLClass):
3339
+ target_class = ref_obj
3340
+ else:
3341
+ return # Not a valid reference
3342
+ else:
3343
+ # Static class reference: &ClassName::member
3344
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
3345
+
3346
+ if target_class is None:
3347
+ return # Class not found, skip silently
3348
+
3349
+ # Handle different target types
3350
+ if isinstance(target_class, CSSLInstance):
3351
+ # Referenced an instance variable directly
3352
+ target_class = target_class.class_def
3353
+
3354
+ if not isinstance(target_class, CSSLClass):
3355
+ return # Not a CSSL class
3356
+
3357
+ # Find and execute the referenced member
3358
+ if is_constructor:
3359
+ # Looking for a constructor
3360
+ if ref_member == 'constructors' or ref_member is None:
3361
+ # Execute all constructors from the referenced class
3362
+ for constr in getattr(target_class, 'constructors', []):
3363
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3364
+ else:
3365
+ # Execute specific constructor by name
3366
+ for constr in getattr(target_class, 'constructors', []):
3367
+ if constr.value.get('name') == ref_member:
3368
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3369
+ break
3370
+ else:
3371
+ # Looking for a method (define function)
3372
+ if ref_member:
3373
+ # Find method in class
3374
+ for member in getattr(target_class, 'members', []):
3375
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
3376
+ self._call_method(instance, member, args, kwargs)
3377
+ break
3378
+ # Also check in methods dict if available
3379
+ methods = getattr(target_class, 'methods', {})
3380
+ if ref_member in methods:
3381
+ method_node = methods[ref_member]
3382
+ self._call_method(instance, method_node, args, kwargs)
3383
+
3384
+ def _call_constructor(self, instance: CSSLInstance, constr_node: ASTNode,
3385
+ args: list, kwargs: dict, param_values: dict):
3386
+ """Call a constructor defined with 'constr' keyword.
3387
+
3388
+ Handles constructor extends/overwrites and sets up the instance scope.
3389
+ Supports append mode (++) for keeping parent code and adding new code.
3390
+ """
3391
+ constr_info = constr_node.value
3392
+ constr_params = constr_info.get('params', [])
3393
+ extends_class = constr_info.get('extends_class')
3394
+ extends_method = constr_info.get('extends_method')
3395
+
3396
+ # Append mode: ++ operator for keeping parent code + adding new
3397
+ append_mode = constr_info.get('append_mode', False)
3398
+ append_ref_class = constr_info.get('append_ref_class') # &ClassName or &$instanceVar
3399
+ append_ref_member = constr_info.get('append_ref_member') # ::constructorName or ::methodName
3400
+
3401
+ # Save previous instance context
3402
+ prev_instance = self._current_instance
3403
+ self._current_instance = instance
3404
+
3405
+ # Create new scope for constructor
3406
+ new_scope = Scope(parent=self.scope)
3407
+
3408
+ # Bind param_values (from class params) to constructor scope
3409
+ for name, value in param_values.items():
3410
+ new_scope.set(name, value)
3411
+
3412
+ # Bind constructor parameters
3413
+ for i, param in enumerate(constr_params):
3414
+ param_name = param.get('name') if isinstance(param, dict) else param
3415
+ if i < len(args):
3416
+ new_scope.set(param_name, args[i])
3417
+ elif param_name in kwargs:
3418
+ new_scope.set(param_name, kwargs[param_name])
3419
+ else:
3420
+ new_scope.set(param_name, None)
3421
+
3422
+ # If constructor extends another constructor, inherit its local vars
3423
+ if extends_class and extends_method:
3424
+ parent_class = self.scope.get(extends_class) or self.global_scope.get(extends_class)
3425
+ if parent_class and isinstance(parent_class, CSSLClass):
3426
+ for constr in getattr(parent_class, 'constructors', []):
3427
+ if constr.value.get('name') == extends_method:
3428
+ # Execute parent constructor first to get local vars
3429
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3430
+ break
3431
+
3432
+ # Handle append mode (++) - execute referenced parent member first
3433
+ if append_mode and append_ref_class:
3434
+ self._execute_append_reference(
3435
+ instance, append_ref_class, append_ref_member,
3436
+ args, kwargs, param_values, is_constructor=True
3437
+ )
3438
+
3439
+ # Execute constructor body
3440
+ prev_scope = self.scope
3441
+ self.scope = new_scope
3442
+
3443
+ try:
3444
+ for stmt in constr_node.children:
3445
+ self._execute_node(stmt)
3446
+ finally:
3447
+ self.scope = prev_scope
3448
+ self._current_instance = prev_instance
3449
+
2871
3450
  def _eval_this_access(self, node: ASTNode) -> Any:
2872
3451
  """Evaluate 'this->member' access.
2873
3452
 
@@ -2905,6 +3484,10 @@ class CSSLRuntime:
2905
3484
  if instance.has_method(member):
2906
3485
  # Return a callable that will invoke the method with instance context
2907
3486
  method_node = instance.get_method(member)
3487
+ # Check if this is an inherited Python method
3488
+ if isinstance(method_node, tuple) and method_node[0] == 'python_method':
3489
+ python_method = method_node[1]
3490
+ return lambda *args, **kwargs: python_method(*args, **kwargs)
2908
3491
  return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
2909
3492
 
2910
3493
  raise CSSLRuntimeError(
@@ -2916,12 +3499,18 @@ class CSSLRuntime:
2916
3499
  """Call a method on an instance with 'this' context.
2917
3500
 
2918
3501
  Sets up the instance as the current 'this' context and executes the method.
3502
+ Supports append mode (++) for keeping parent code and adding new code.
2919
3503
  """
2920
3504
  kwargs = kwargs or {}
2921
3505
  func_info = method_node.value
2922
3506
  params = func_info.get('params', [])
2923
3507
  modifiers = func_info.get('modifiers', [])
2924
3508
 
3509
+ # Append mode: ++ operator for keeping parent code + adding new
3510
+ append_mode = func_info.get('append_mode', False)
3511
+ append_ref_class = func_info.get('append_ref_class') # &ClassName or &$instanceVar
3512
+ append_ref_member = func_info.get('append_ref_member') # ::methodName
3513
+
2925
3514
  # Check for undefined modifier
2926
3515
  is_undefined = 'undefined' in modifiers
2927
3516
 
@@ -2948,6 +3537,13 @@ class CSSLRuntime:
2948
3537
  self._current_instance = instance
2949
3538
 
2950
3539
  try:
3540
+ # Handle append mode (++) - execute referenced parent method first
3541
+ if append_mode and append_ref_class:
3542
+ self._execute_append_reference(
3543
+ instance, append_ref_class, append_ref_member,
3544
+ args, kwargs, {}, is_constructor=False
3545
+ )
3546
+
2951
3547
  for child in method_node.children:
2952
3548
  if not self._running:
2953
3549
  break
@@ -2986,6 +3582,10 @@ class CSSLRuntime:
2986
3582
  # Check for method
2987
3583
  if obj.has_method(member):
2988
3584
  method_node = obj.get_method(member)
3585
+ # Check if this is an inherited Python method
3586
+ if isinstance(method_node, tuple) and method_node[0] == 'python_method':
3587
+ python_method = method_node[1]
3588
+ return lambda *args, **kwargs: python_method(*args, **kwargs)
2989
3589
  return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
2990
3590
  raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")
2991
3591