IncludeCPP 3.8.8__py3-none-any.whl → 4.0.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.
@@ -99,6 +99,89 @@ ERROR_HINTS = {
99
99
  }
100
100
 
101
101
 
102
+ def _find_similar_names(name: str, candidates: list, max_distance: int = 2) -> list:
103
+ """Find similar names for 'did you mean' suggestions using Levenshtein distance."""
104
+ if not candidates:
105
+ return []
106
+
107
+ def levenshtein(s1: str, s2: str) -> int:
108
+ if len(s1) < len(s2):
109
+ s1, s2 = s2, s1
110
+ if len(s2) == 0:
111
+ return len(s1)
112
+ prev_row = range(len(s2) + 1)
113
+ for i, c1 in enumerate(s1):
114
+ curr_row = [i + 1]
115
+ for j, c2 in enumerate(s2):
116
+ insertions = prev_row[j + 1] + 1
117
+ deletions = curr_row[j] + 1
118
+ substitutions = prev_row[j] + (c1.lower() != c2.lower())
119
+ curr_row.append(min(insertions, deletions, substitutions))
120
+ prev_row = curr_row
121
+ return prev_row[-1]
122
+
123
+ similar = []
124
+ name_lower = name.lower()
125
+ for candidate in candidates:
126
+ if candidate.startswith('_'):
127
+ continue
128
+ dist = levenshtein(name, candidate)
129
+ # Also check case-insensitive exact match
130
+ if name_lower == candidate.lower() and name != candidate:
131
+ similar.insert(0, candidate) # Exact case mismatch goes first
132
+ elif dist <= max_distance:
133
+ similar.append(candidate)
134
+
135
+ return similar[:3] # Return top 3 suggestions
136
+
137
+
138
+ def _get_available_classes(scope: 'Scope', global_scope: 'Scope', promoted_globals: dict) -> list:
139
+ """Get list of all available class names."""
140
+ classes = []
141
+ # Check current scope chain
142
+ current = scope
143
+ while current:
144
+ for name, val in current.variables.items():
145
+ if isinstance(val, CSSLClass) and name not in classes:
146
+ classes.append(name)
147
+ current = current.parent
148
+ # Check global scope
149
+ for name, val in global_scope.variables.items():
150
+ if isinstance(val, CSSLClass) and name not in classes:
151
+ classes.append(name)
152
+ # Check promoted globals
153
+ for name, val in promoted_globals.items():
154
+ if isinstance(val, CSSLClass) and name not in classes:
155
+ classes.append(name)
156
+ return classes
157
+
158
+
159
+ def _get_available_functions(scope: 'Scope', global_scope: 'Scope', builtins) -> list:
160
+ """Get list of all available function names."""
161
+ functions = []
162
+ # Check current scope chain
163
+ current = scope
164
+ while current:
165
+ for name, val in current.variables.items():
166
+ if callable(val) or (isinstance(val, ASTNode) and val.type == 'function'):
167
+ if name not in functions:
168
+ functions.append(name)
169
+ current = current.parent
170
+ # Check global scope
171
+ for name, val in global_scope.variables.items():
172
+ if callable(val) or (isinstance(val, ASTNode) and val.type == 'function'):
173
+ if name not in functions:
174
+ functions.append(name)
175
+ # Check builtins
176
+ if builtins:
177
+ for name in dir(builtins):
178
+ if name.startswith('builtin_'):
179
+ func_name = name[8:] # Remove 'builtin_' prefix
180
+ if func_name not in functions:
181
+ functions.append(func_name)
182
+ return functions
183
+
184
+
102
185
  class CSSLBreak(Exception):
103
186
  """Break statement"""
104
187
  pass
@@ -716,9 +799,12 @@ class CSSLRuntime:
716
799
  Parses class members and methods, creating a CSSLClass object
717
800
  that can be instantiated with 'new'.
718
801
  Supports inheritance via 'extends' keyword and method overwriting via 'overwrites'.
802
+
803
+ Classes are local by default. Use 'global class' or 'class @Name' for global classes.
719
804
  """
720
805
  class_info = node.value
721
806
  class_name = class_info.get('name')
807
+ is_global = class_info.get('is_global', False)
722
808
  extends_class_name = class_info.get('extends')
723
809
  extends_is_python = class_info.get('extends_is_python', False)
724
810
  overwrites_class_name = class_info.get('overwrites')
@@ -745,7 +831,22 @@ class CSSLRuntime:
745
831
  parent_class = self.global_scope.get(extends_class_name)
746
832
 
747
833
  if parent_class is None:
748
- raise ValueError(f"Cannot extend unknown class '{extends_class_name}'")
834
+ # Build detailed error for extends
835
+ available_classes = _get_available_classes(self.scope, self.global_scope, self._promoted_globals)
836
+ similar = _find_similar_names(extends_class_name, available_classes)
837
+
838
+ if similar:
839
+ hint = f"Did you mean: {', '.join(similar)}?"
840
+ elif extends_is_python:
841
+ hint = f"Python object '${extends_class_name}' not found. Use share() to share Python objects first."
842
+ else:
843
+ hint = f"Define class '{extends_class_name}' before this class, or use 'extends $PyObject' for Python objects"
844
+
845
+ raise self._format_error(
846
+ node.line,
847
+ f"Cannot extend unknown class '{extends_class_name}'",
848
+ hint
849
+ )
749
850
 
750
851
  # Auto-wrap Python objects for inheritance
751
852
  from .cssl_builtins import CSSLizedPythonObject
@@ -803,9 +904,11 @@ class CSSLRuntime:
803
904
  class_def.class_params = class_params # Class-level constructor parameters
804
905
  class_def.extends_args = extends_args # Arguments to pass to parent constructor
805
906
 
806
- # Register class in scope
907
+ # Register class in scope (local by default, global if marked)
807
908
  self.scope.set(class_name, class_def)
808
- self.global_scope.set(class_name, class_def)
909
+ if is_global:
910
+ self.global_scope.set(class_name, class_def)
911
+ self._promoted_globals[class_name] = class_def
809
912
 
810
913
  # Handle class overwrites - replace methods in target class
811
914
  if overwrites_class_name:
@@ -888,12 +991,15 @@ class CSSLRuntime:
888
991
  """Execute function definition - registers it and handles extends/overwrites.
889
992
 
890
993
  Syntax:
891
- define func() { ... }
892
- define func : extends otherFunc() { ... } - Inherit local vars
893
- define func : overwrites otherFunc() { ... } - Replace otherFunc
994
+ define func() { ... } - Local function
995
+ global define func() { ... } - Global function
996
+ define @func() { ... } - Global function (alternative)
997
+ define func : extends otherFunc() { ... } - Inherit local vars
998
+ define func : overwrites otherFunc() { ... } - Replace otherFunc
894
999
  """
895
1000
  func_info = node.value
896
1001
  func_name = func_info.get('name')
1002
+ is_global = func_info.get('is_global', False)
897
1003
  extends_func = func_info.get('extends')
898
1004
  extends_is_python = func_info.get('extends_is_python', False)
899
1005
  overwrites_func = func_info.get('overwrites')
@@ -922,8 +1028,11 @@ class CSSLRuntime:
922
1028
  self.scope.set(overwrites_func, node)
923
1029
  self.global_scope.set(overwrites_func, node)
924
1030
 
925
- # Register the function
1031
+ # Register the function (local by default, global if marked)
926
1032
  self.scope.set(func_name, node)
1033
+ if is_global:
1034
+ self.global_scope.set(func_name, node)
1035
+ self._promoted_globals[func_name] = node
927
1036
  return None
928
1037
 
929
1038
  def _resolve_function_target(self, name: str, is_python: bool) -> Any:
@@ -1290,13 +1399,97 @@ class CSSLRuntime:
1290
1399
  self.scope = new_scope
1291
1400
 
1292
1401
  try:
1402
+ # Handle append mode (++) - execute referenced function first
1403
+ append_mode = func_info.get('append_mode', False)
1404
+ append_ref_class = func_info.get('append_ref_class')
1405
+ append_ref_member = func_info.get('append_ref_member')
1406
+
1407
+ if append_mode and append_ref_class:
1408
+ self._execute_append_reference(
1409
+ None, append_ref_class, append_ref_member,
1410
+ args, kwargs, {}, is_constructor=False
1411
+ )
1412
+
1293
1413
  for child in func_node.children:
1294
1414
  # Check if exit() was called
1295
1415
  if not self._running:
1296
1416
  break
1297
1417
  self._execute_node(child)
1298
1418
  except CSSLReturn as ret:
1299
- return ret.value
1419
+ return_value = ret.value
1420
+
1421
+ # Check exclude_type: *[type] - must NOT return excluded type
1422
+ exclude_type = func_info.get('exclude_type')
1423
+ if exclude_type:
1424
+ type_map = {
1425
+ 'string': str, 'int': int, 'float': float, 'bool': bool,
1426
+ 'null': type(None), 'none': type(None),
1427
+ 'list': list, 'array': list, 'dict': dict, 'json': dict,
1428
+ }
1429
+ excluded_py_type = type_map.get(exclude_type.lower())
1430
+ # For shuffled returns (tuples), check each element
1431
+ if isinstance(return_value, tuple):
1432
+ for val in return_value:
1433
+ if excluded_py_type and isinstance(val, excluded_py_type):
1434
+ raise CSSLRuntimeError(f"Type exclusion: function must NOT return '{exclude_type}' values")
1435
+ elif excluded_py_type and isinstance(return_value, excluded_py_type):
1436
+ raise CSSLRuntimeError(f"Type exclusion: function must NOT return '{exclude_type}'")
1437
+
1438
+ # Enforce return type for typed functions (like C++)
1439
+ # Typed functions MUST return the declared type
1440
+ # Exception: 'meta' modifier allows any return type
1441
+ enforce_return_type = func_info.get('enforce_return_type', False)
1442
+ return_type = func_info.get('return_type')
1443
+
1444
+ if enforce_return_type and return_type and return_type != 'void':
1445
+ # Type mapping from CSSL types to Python types
1446
+ type_validate_map = {
1447
+ 'string': str, 'int': int, 'float': (int, float), 'bool': bool,
1448
+ 'list': list, 'array': list, 'dict': dict, 'json': dict,
1449
+ 'dynamic': object, # Any type
1450
+ 'void': type(None),
1451
+ }
1452
+
1453
+ # Generic container types - accept lists/tuples
1454
+ container_types = {
1455
+ 'vector', 'stack', 'datastruct', 'dataspace',
1456
+ 'shuffled', 'iterator', 'combo', 'openquote', 'map'
1457
+ }
1458
+
1459
+ if return_type in container_types:
1460
+ # Container types accept list, tuple, dict depending on type
1461
+ if return_type == 'map':
1462
+ expected = dict
1463
+ elif return_type == 'shuffled':
1464
+ expected = (list, tuple)
1465
+ else:
1466
+ expected = (list, tuple, object)
1467
+
1468
+ if not isinstance(return_value, expected):
1469
+ func_name = func_info.get('name', 'unknown')
1470
+ actual_type = type(return_value).__name__
1471
+ raise CSSLRuntimeError(
1472
+ f"Type error in '{func_name}': declared return type '{return_type}' "
1473
+ f"but returned '{actual_type}'. Typed functions must return declared type."
1474
+ )
1475
+ elif return_type in type_validate_map:
1476
+ expected = type_validate_map[return_type]
1477
+ if expected != object and return_value is not None:
1478
+ if not isinstance(return_value, expected):
1479
+ func_name = func_info.get('name', 'unknown')
1480
+ actual_type = type(return_value).__name__
1481
+ raise CSSLRuntimeError(
1482
+ f"Type error in '{func_name}': declared return type '{return_type}' "
1483
+ f"but returned '{actual_type}'. Typed functions must return declared type."
1484
+ )
1485
+
1486
+ # Check non_null: function must return a value (not None)
1487
+ non_null = func_info.get('non_null', False)
1488
+ if non_null and return_value is None:
1489
+ func_name = func_info.get('name', 'unknown')
1490
+ raise CSSLRuntimeError(f"Non-null function '{func_name}' returned null/None")
1491
+
1492
+ return return_value
1300
1493
  except Exception as e:
1301
1494
  # If undefined modifier, suppress all errors
1302
1495
  if is_undefined:
@@ -2704,7 +2897,21 @@ class CSSLRuntime:
2704
2897
  scoped_val = self.global_scope.get(f'${name}')
2705
2898
  if scoped_val is not None:
2706
2899
  return scoped_val
2707
- raise CSSLRuntimeError(f"Shared object '${name}' not found. Use share() to share objects.")
2900
+ # List available shared objects for helpful error
2901
+ available_shared = list(_live_objects.keys())
2902
+ similar = _find_similar_names(name, available_shared)
2903
+ if similar:
2904
+ hint = f"Did you mean: ${', $'.join(similar)}?"
2905
+ elif available_shared:
2906
+ hint = f"Available shared objects: ${', $'.join(available_shared[:5])}"
2907
+ else:
2908
+ hint = "Use share(name, object) from Python to share objects first."
2909
+
2910
+ raise self._format_error(
2911
+ node.line if hasattr(node, 'line') else 0,
2912
+ f"Shared object '${name}' not found",
2913
+ hint
2914
+ )
2708
2915
 
2709
2916
  if node.type == 'captured_ref':
2710
2917
  # %<name> captured reference - use value captured at infusion registration time
@@ -2731,7 +2938,13 @@ class CSSLRuntime:
2731
2938
  value = self._original_functions.get(name)
2732
2939
  if value is not None:
2733
2940
  return value
2734
- raise CSSLRuntimeError(f"Captured reference '%{name}' not found.")
2941
+ # Build helpful error for captured reference
2942
+ hint = f"Variable '{name}' must exist when the infusion is registered. Check that '%{name}' is defined before the <<== operator."
2943
+ raise self._format_error(
2944
+ node.line if hasattr(node, 'line') else 0,
2945
+ f"Captured reference '%{name}' not found",
2946
+ hint
2947
+ )
2735
2948
 
2736
2949
  if node.type == 'instance_ref':
2737
2950
  # instance<"name"> - get shared instance by name
@@ -2820,6 +3033,58 @@ class CSSLRuntime:
2820
3033
  if node.type == 'unary':
2821
3034
  return self._eval_unary(node)
2822
3035
 
3036
+ if node.type == 'non_null_assert':
3037
+ # *$var, *@module, *identifier - assert value is not null/None
3038
+ operand = node.value.get('operand')
3039
+ value = self._evaluate(operand)
3040
+ if value is None:
3041
+ # Get name of the operand for better error message
3042
+ operand_name = "unknown"
3043
+ if isinstance(operand, ASTNode):
3044
+ if operand.type == 'identifier':
3045
+ operand_name = operand.value
3046
+ elif operand.type == 'shared_ref':
3047
+ operand_name = f"${operand.value}"
3048
+ elif operand.type == 'module_ref':
3049
+ operand_name = f"@{operand.value}"
3050
+ elif operand.type == 'global_ref':
3051
+ operand_name = f"r@{operand.value}"
3052
+ raise self._format_error(
3053
+ node.line if hasattr(node, 'line') else 0,
3054
+ f"Non-null assertion failed: '{operand_name}' is null/None",
3055
+ f"The value accessed via '*{operand_name}' must not be null. Check that it is defined and initialized."
3056
+ )
3057
+ return value
3058
+
3059
+ if node.type == 'type_exclude_assert':
3060
+ # *[type]expr - assert value is NOT of excluded type
3061
+ exclude_type = node.value.get('exclude_type')
3062
+ operand = node.value.get('operand')
3063
+ value = self._evaluate(operand)
3064
+
3065
+ # Map CSSL types to Python types
3066
+ type_map = {
3067
+ 'string': str,
3068
+ 'int': int,
3069
+ 'float': float,
3070
+ 'bool': bool,
3071
+ 'null': type(None),
3072
+ 'none': type(None),
3073
+ 'list': list,
3074
+ 'array': list,
3075
+ 'dict': dict,
3076
+ 'json': dict,
3077
+ }
3078
+
3079
+ excluded_py_type = type_map.get(exclude_type.lower())
3080
+ if excluded_py_type and isinstance(value, excluded_py_type):
3081
+ raise self._format_error(
3082
+ node.line if hasattr(node, 'line') else 0,
3083
+ f"Type exclusion assertion failed: value is of excluded type '{exclude_type}'",
3084
+ f"The expression was marked *[{exclude_type}] meaning it must NOT return {exclude_type}, but it did."
3085
+ )
3086
+ return value
3087
+
2823
3088
  if node.type == 'call':
2824
3089
  return self._eval_call(node)
2825
3090
 
@@ -3097,12 +3362,33 @@ class CSSLRuntime:
3097
3362
  return self._call_function(callee, args, kwargs)
3098
3363
 
3099
3364
  callee_name = callee_node.value if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value') else str(callee_node)
3100
- raise CSSLRuntimeError(
3101
- f"Cannot call '{callee_name}' - it is not a function",
3102
- node.line,
3103
- context=f"Type: {type(callee).__name__}",
3104
- hint=ERROR_HINTS['undefined_function']
3105
- )
3365
+
3366
+ # Build detailed error with suggestions
3367
+ available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
3368
+ similar = _find_similar_names(callee_name, available_funcs)
3369
+
3370
+ if callee is None:
3371
+ # Function not found at all
3372
+ if similar:
3373
+ hint = f"Did you mean: {', '.join(similar)}?"
3374
+ else:
3375
+ hint = f"Function '{callee_name}' is not defined. Define it with: define {callee_name}() {{ }}"
3376
+ raise self._format_error(
3377
+ node.line,
3378
+ f"Function '{callee_name}' not found",
3379
+ hint
3380
+ )
3381
+ else:
3382
+ # Found something but it's not callable
3383
+ if similar:
3384
+ hint = f"'{callee_name}' is a {type(callee).__name__}, not a function. Did you mean: {', '.join(similar)}?"
3385
+ else:
3386
+ hint = f"'{callee_name}' is a {type(callee).__name__}. Functions must be defined with 'define' keyword."
3387
+ raise self._format_error(
3388
+ node.line,
3389
+ f"Cannot call '{callee_name}' - it is not a function",
3390
+ hint
3391
+ )
3106
3392
 
3107
3393
  def _eval_typed_call(self, node: ASTNode) -> Any:
3108
3394
  """Evaluate typed function call like OpenFind<string>(0) or OpenFind<dynamic, "name">"""
@@ -3168,26 +3454,53 @@ class CSSLRuntime:
3168
3454
  )
3169
3455
 
3170
3456
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
3171
- """Evaluate 'new ClassName(args)' expression.
3457
+ """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
3172
3458
 
3173
3459
  Creates a new instance of a CSSL class and calls its constructor.
3174
3460
  Supports multiple constructors (constr keyword), class parameters,
3175
3461
  and automatic parent constructor calling.
3462
+
3463
+ With '@' prefix (new @ClassName), looks only in global scope.
3176
3464
  """
3177
3465
  class_name = node.value.get('class')
3466
+ is_global_ref = node.value.get('is_global_ref', False)
3178
3467
  args = [self._evaluate(arg) for arg in node.value.get('args', [])]
3179
3468
  kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
3180
3469
 
3181
3470
  # Get class definition from scope
3182
- class_def = self.scope.get(class_name)
3183
- if class_def is None:
3184
- class_def = self.global_scope.get(class_name)
3471
+ if is_global_ref:
3472
+ # With @ prefix, only look in global scope
3473
+ class_def = self._promoted_globals.get(class_name)
3474
+ if class_def is None:
3475
+ class_def = self.global_scope.get(class_name)
3476
+ else:
3477
+ # Normal lookup: local scope first, then global
3478
+ class_def = self.scope.get(class_name)
3479
+ if class_def is None:
3480
+ class_def = self.global_scope.get(class_name)
3185
3481
 
3186
3482
  if class_def is None:
3187
- raise CSSLRuntimeError(
3188
- f"Class '{class_name}' not found",
3483
+ # Build detailed error with suggestions
3484
+ source_line = self._get_source_line(node.line)
3485
+ available_classes = _get_available_classes(self.scope, self.global_scope, self._promoted_globals)
3486
+ similar = _find_similar_names(class_name, available_classes)
3487
+
3488
+ # Check if class exists in global scope (user forgot @)
3489
+ global_class = self._promoted_globals.get(class_name) or self.global_scope.get(class_name)
3490
+ if global_class and isinstance(global_class, CSSLClass) and not is_global_ref:
3491
+ hint = f"Class '{class_name}' exists in global scope. Use: new @{class_name}()"
3492
+ elif similar:
3493
+ hint = f"Did you mean: {', '.join(similar)}?"
3494
+ elif available_classes:
3495
+ hint = f"Available classes: {', '.join(available_classes[:5])}"
3496
+ else:
3497
+ hint = "Define the class before instantiation, or use 'global class' / 'class @Name' for global classes"
3498
+
3499
+ context = f"in expression: {source_line.strip()}" if source_line else None
3500
+ raise self._format_error(
3189
3501
  node.line,
3190
- hint="Make sure the class is defined before instantiation"
3502
+ f"Class '{class_name}' not found",
3503
+ hint
3191
3504
  )
3192
3505
 
3193
3506
  if not isinstance(class_def, CSSLClass):
@@ -3271,17 +3584,122 @@ class CSSLRuntime:
3271
3584
  except TypeError:
3272
3585
  pass
3273
3586
 
3587
+ def _execute_append_reference(self, instance: CSSLInstance, ref_class: str, ref_member: str,
3588
+ args: list, kwargs: dict, param_values: dict, is_constructor: bool = True):
3589
+ """Execute referenced parent constructor/method for append mode (++).
3590
+
3591
+ Resolves the class/instance reference and executes the specified member.
3592
+ Supports:
3593
+ - Static class references: &ClassName::member
3594
+ - Dynamic instance references: &$instanceVar::member
3595
+ - Direct function references: &FunctionName (for define functions)
3596
+
3597
+ Args:
3598
+ instance: Current class instance (can be None for standalone functions)
3599
+ ref_class: Referenced class name, $instanceVar, or function name
3600
+ ref_member: Member name (constructor name, 'constructors', or method name) - can be None
3601
+ args: Arguments to pass
3602
+ kwargs: Keyword arguments to pass
3603
+ param_values: Parameter values from class params
3604
+ is_constructor: True if looking for constructor, False for method
3605
+ """
3606
+ from .cssl_builtins import CSSLizedPythonObject
3607
+
3608
+ # Handle direct function reference: &FunctionName ++ (no ::member part)
3609
+ if ref_member is None and not is_constructor:
3610
+ # ref_class is actually a function name
3611
+ func_name = ref_class
3612
+ if func_name.startswith('$'):
3613
+ func_name = func_name[1:]
3614
+
3615
+ # Look for the function in scope
3616
+ func = self.scope.get(func_name) or self.global_scope.get(func_name)
3617
+ if func is not None:
3618
+ if isinstance(func, ASTNode) and func.type in ('function', 'FUNCTION'):
3619
+ # Execute the referenced function
3620
+ if instance:
3621
+ self._call_method(instance, func, args, kwargs)
3622
+ else:
3623
+ self._call_function(func, args, kwargs)
3624
+ elif callable(func):
3625
+ # It's a callable (Python function or lambda)
3626
+ func(*args, **kwargs)
3627
+ return
3628
+
3629
+ # Resolve the class/instance reference
3630
+ if ref_class.startswith('$'):
3631
+ # Dynamic instance reference: &$instanceVar::member
3632
+ var_name = ref_class[1:]
3633
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3634
+ if ref_obj is None:
3635
+ return # Instance not found, skip silently
3636
+
3637
+ if isinstance(ref_obj, CSSLInstance):
3638
+ # Get the class definition from the instance
3639
+ target_class = ref_obj.class_def
3640
+ elif isinstance(ref_obj, CSSLClass):
3641
+ target_class = ref_obj
3642
+ else:
3643
+ return # Not a valid reference
3644
+ else:
3645
+ # Static class reference: &ClassName::member
3646
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
3647
+
3648
+ if target_class is None:
3649
+ return # Class not found, skip silently
3650
+
3651
+ # Handle different target types
3652
+ if isinstance(target_class, CSSLInstance):
3653
+ # Referenced an instance variable directly
3654
+ target_class = target_class.class_def
3655
+
3656
+ if not isinstance(target_class, CSSLClass):
3657
+ return # Not a CSSL class
3658
+
3659
+ # Find and execute the referenced member
3660
+ if is_constructor:
3661
+ # Looking for a constructor
3662
+ if ref_member == 'constructors' or ref_member is None:
3663
+ # Execute all constructors from the referenced class
3664
+ for constr in getattr(target_class, 'constructors', []):
3665
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3666
+ else:
3667
+ # Execute specific constructor by name
3668
+ for constr in getattr(target_class, 'constructors', []):
3669
+ if constr.value.get('name') == ref_member:
3670
+ self._call_constructor(instance, constr, args, kwargs, param_values)
3671
+ break
3672
+ else:
3673
+ # Looking for a method (define function)
3674
+ if ref_member:
3675
+ # Find method in class
3676
+ for member in getattr(target_class, 'members', []):
3677
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
3678
+ self._call_method(instance, member, args, kwargs)
3679
+ break
3680
+ # Also check in methods dict if available
3681
+ methods = getattr(target_class, 'methods', {})
3682
+ if ref_member in methods:
3683
+ method_node = methods[ref_member]
3684
+ self._call_method(instance, method_node, args, kwargs)
3685
+
3274
3686
  def _call_constructor(self, instance: CSSLInstance, constr_node: ASTNode,
3275
3687
  args: list, kwargs: dict, param_values: dict):
3276
3688
  """Call a constructor defined with 'constr' keyword.
3277
3689
 
3278
3690
  Handles constructor extends/overwrites and sets up the instance scope.
3691
+ Supports append mode (++) for keeping parent code and adding new code.
3279
3692
  """
3280
3693
  constr_info = constr_node.value
3281
3694
  constr_params = constr_info.get('params', [])
3282
3695
  extends_class = constr_info.get('extends_class')
3283
3696
  extends_method = constr_info.get('extends_method')
3284
3697
 
3698
+ # Append mode: ++ operator for keeping parent code + adding new
3699
+ append_mode = constr_info.get('append_mode', False)
3700
+ append_ref_class = constr_info.get('append_ref_class') # &ClassName or &$instanceVar
3701
+ append_ref_member = constr_info.get('append_ref_member') # ::constructorName or ::methodName
3702
+
3285
3703
  # Save previous instance context
3286
3704
  prev_instance = self._current_instance
3287
3705
  self._current_instance = instance
@@ -3313,6 +3731,13 @@ class CSSLRuntime:
3313
3731
  self._call_constructor(instance, constr, args, kwargs, param_values)
3314
3732
  break
3315
3733
 
3734
+ # Handle append mode (++) - execute referenced parent member first
3735
+ if append_mode and append_ref_class:
3736
+ self._execute_append_reference(
3737
+ instance, append_ref_class, append_ref_member,
3738
+ args, kwargs, param_values, is_constructor=True
3739
+ )
3740
+
3316
3741
  # Execute constructor body
3317
3742
  prev_scope = self.scope
3318
3743
  self.scope = new_scope
@@ -3367,21 +3792,42 @@ class CSSLRuntime:
3367
3792
  return lambda *args, **kwargs: python_method(*args, **kwargs)
3368
3793
  return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
3369
3794
 
3370
- raise CSSLRuntimeError(
3371
- f"'{instance._class.name}' has no member or method '{member}'",
3372
- node.line if hasattr(node, 'line') else 0
3795
+ # Build helpful error with available members
3796
+ class_name = instance._class.name
3797
+ available_members = list(instance._members.keys()) if hasattr(instance, '_members') else []
3798
+ available_methods = list(instance._methods.keys()) if hasattr(instance, '_methods') else []
3799
+ all_available = available_members + available_methods
3800
+ similar = _find_similar_names(member, all_available)
3801
+
3802
+ if similar:
3803
+ hint = f"Did you mean: {', '.join(similar)}?"
3804
+ elif all_available:
3805
+ hint = f"Available: {', '.join(all_available[:5])}"
3806
+ else:
3807
+ hint = f"Class '{class_name}' has no accessible members. Check class definition."
3808
+
3809
+ raise self._format_error(
3810
+ node.line if hasattr(node, 'line') else 0,
3811
+ f"'{class_name}' has no member or method '{member}'",
3812
+ hint
3373
3813
  )
3374
3814
 
3375
3815
  def _call_method(self, instance: CSSLInstance, method_node: ASTNode, args: list, kwargs: dict = None) -> Any:
3376
3816
  """Call a method on an instance with 'this' context.
3377
3817
 
3378
3818
  Sets up the instance as the current 'this' context and executes the method.
3819
+ Supports append mode (++) for keeping parent code and adding new code.
3379
3820
  """
3380
3821
  kwargs = kwargs or {}
3381
3822
  func_info = method_node.value
3382
3823
  params = func_info.get('params', [])
3383
3824
  modifiers = func_info.get('modifiers', [])
3384
3825
 
3826
+ # Append mode: ++ operator for keeping parent code + adding new
3827
+ append_mode = func_info.get('append_mode', False)
3828
+ append_ref_class = func_info.get('append_ref_class') # &ClassName or &$instanceVar
3829
+ append_ref_member = func_info.get('append_ref_member') # ::methodName
3830
+
3385
3831
  # Check for undefined modifier
3386
3832
  is_undefined = 'undefined' in modifiers
3387
3833
 
@@ -3408,6 +3854,13 @@ class CSSLRuntime:
3408
3854
  self._current_instance = instance
3409
3855
 
3410
3856
  try:
3857
+ # Handle append mode (++) - execute referenced parent method first
3858
+ if append_mode and append_ref_class:
3859
+ self._execute_append_reference(
3860
+ instance, append_ref_class, append_ref_member,
3861
+ args, kwargs, {}, is_constructor=False
3862
+ )
3863
+
3411
3864
  for child in method_node.children:
3412
3865
  if not self._running:
3413
3866
  break
@@ -3451,7 +3904,25 @@ class CSSLRuntime:
3451
3904
  python_method = method_node[1]
3452
3905
  return lambda *args, **kwargs: python_method(*args, **kwargs)
3453
3906
  return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
3454
- raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")
3907
+ # Build helpful error with available members
3908
+ class_name = obj._class.name
3909
+ available_members = list(obj._members.keys()) if hasattr(obj, '_members') else []
3910
+ available_methods = list(obj._methods.keys()) if hasattr(obj, '_methods') else []
3911
+ all_available = available_members + available_methods
3912
+ similar = _find_similar_names(member, all_available)
3913
+
3914
+ if similar:
3915
+ hint = f"Did you mean: {', '.join(similar)}?"
3916
+ elif all_available:
3917
+ hint = f"Available: {', '.join(all_available[:5])}"
3918
+ else:
3919
+ hint = f"Class '{class_name}' has no accessible members."
3920
+
3921
+ raise self._format_error(
3922
+ node.line,
3923
+ f"'{class_name}' has no member or method '{member}'",
3924
+ hint
3925
+ )
3455
3926
 
3456
3927
  # === STRING METHODS ===
3457
3928
  if isinstance(obj, str):