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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
 
@@ -532,6 +532,10 @@ class CSSLRuntime:
532
532
  self._exec_struct(child)
533
533
  elif child.type == 'class':
534
534
  self._exec_class(child)
535
+ elif child.type == 'enum':
536
+ self._exec_enum(child)
537
+ elif child.type == 'bytearrayed':
538
+ self._exec_bytearrayed(child)
535
539
  elif child.type == 'function':
536
540
  self._exec_function(child)
537
541
  elif child.type == 'global_assignment':
@@ -813,6 +817,74 @@ class CSSLRuntime:
813
817
 
814
818
  return struct_data
815
819
 
820
+ def _exec_enum(self, node: ASTNode) -> Dict[str, Any]:
821
+ """Execute enum declaration - registers enum values in scope.
822
+
823
+ Creates a dictionary-like enum object accessible via EnumName::VALUE syntax.
824
+
825
+ Example:
826
+ enum Colors { RED, GREEN, BLUE }
827
+ Colors::RED // returns 0
828
+ Colors::GREEN // returns 1
829
+ """
830
+ enum_info = node.value
831
+ enum_name = enum_info.get('name')
832
+ members = enum_info.get('members', [])
833
+
834
+ # Create enum object as a dict-like object with members
835
+ enum_obj = {}
836
+ for member in members:
837
+ member_name = member['name']
838
+ member_value = member['value']
839
+ enum_obj[member_name] = member_value
840
+
841
+ # Register the enum in scope (accessible as EnumName::VALUE)
842
+ self.scope.set(enum_name, enum_obj)
843
+ self.global_scope.set(enum_name, enum_obj)
844
+
845
+ return enum_obj
846
+
847
+ def _exec_bytearrayed(self, node: ASTNode) -> 'ByteArrayed':
848
+ """Execute bytearrayed declaration - function-to-byte mapping with pattern matching.
849
+
850
+ Creates a ByteArrayed object that:
851
+ - Maps function references to byte positions (0x0, 0x1, etc.)
852
+ - Executes functions "invisibly" when called to get return values
853
+ - Matches case patterns based on return values
854
+ - Supports indexing: MyBytes["0x0"] or MyBytes[0]
855
+
856
+ Example:
857
+ bytearrayed MyBytes {
858
+ &func1; // 0x0
859
+ &func2; // 0x1
860
+ case {0, 1} { // func1=0, func2=1
861
+ printl("Match!");
862
+ }
863
+ }
864
+ MyBytes(); // Execute pattern matching
865
+ x = MyBytes["0x0"]; // Get value at position 0
866
+ """
867
+ info = node.value
868
+ name = info.get('name')
869
+ func_refs = info.get('func_refs', [])
870
+ cases = info.get('cases', [])
871
+ default_block = info.get('default')
872
+
873
+ # Create ByteArrayed object
874
+ bytearrayed_obj = ByteArrayed(
875
+ name=name,
876
+ func_refs=func_refs,
877
+ cases=cases,
878
+ default_block=default_block,
879
+ runtime=self
880
+ )
881
+
882
+ # Register in scope
883
+ self.scope.set(name, bytearrayed_obj)
884
+ self.global_scope.set(name, bytearrayed_obj)
885
+
886
+ return bytearrayed_obj
887
+
816
888
  def _exec_class(self, node: ASTNode) -> CSSLClass:
817
889
  """Execute class definition - registers class in scope.
818
890
 
@@ -942,6 +1014,22 @@ class CSSLRuntime:
942
1014
  self.global_scope.set(class_name, class_def)
943
1015
  self._promoted_globals[class_name] = class_def
944
1016
 
1017
+ # v4.2.5: Handle &target replacement for classes
1018
+ # embedded class MyClass &$Target { } - immediate replacement
1019
+ # class MyClass &$Target { } - deferred until instantiation
1020
+ append_ref_class = class_info.get('append_ref_class')
1021
+ is_embedded = class_info.get('is_embedded', False)
1022
+ if append_ref_class and is_embedded:
1023
+ append_ref_member = class_info.get('append_ref_member')
1024
+ self._overwrite_class_target(append_ref_class, append_ref_member, class_def)
1025
+ class_def._target_applied = True
1026
+ elif append_ref_class:
1027
+ # Store reference info for deferred replacement
1028
+ class_def._pending_target = {
1029
+ 'append_ref_class': append_ref_class,
1030
+ 'append_ref_member': class_info.get('append_ref_member')
1031
+ }
1032
+
945
1033
  # Handle class overwrites - replace methods in target class
946
1034
  if overwrites_class_name:
947
1035
  self._apply_class_overwrites(
@@ -1164,13 +1252,18 @@ class CSSLRuntime:
1164
1252
  # Handle &Class::method syntax
1165
1253
  # Without ++ = full replacement
1166
1254
  # With ++ = append (run original first, then new code)
1167
- if append_ref_class:
1255
+ # v4.2.5: Only do immediate replacement if is_embedded=True
1256
+ # For regular 'define', replacement is deferred until function is called
1257
+ is_embedded = func_info.get('is_embedded', False)
1258
+ if append_ref_class and is_embedded:
1168
1259
  if append_mode:
1169
1260
  # Append mode: wrap original to run original + new
1170
1261
  self._append_to_target(append_ref_class, append_ref_member, node)
1171
1262
  else:
1172
1263
  # Full replacement
1173
1264
  self._overwrite_target(append_ref_class, append_ref_member, node)
1265
+ # Mark as already applied so we don't apply again on call
1266
+ node.value['_target_applied'] = True
1174
1267
 
1175
1268
  # Handle overwrites keyword - replace the target function
1176
1269
  if overwrites_func:
@@ -1215,6 +1308,34 @@ class CSSLRuntime:
1215
1308
  return self._call_function(func_node, list(args), kwargs)
1216
1309
  return wrapper
1217
1310
 
1311
+ def _create_python_method_wrapper(self, func_node: ASTNode, python_obj):
1312
+ """Create a Python-callable wrapper that passes python_obj as 'this'.
1313
+
1314
+ v4.3.0: Used when replacing Python object methods with CSSL functions.
1315
+ The wrapper ensures 'this->' in CSSL refers to the Python object.
1316
+ """
1317
+ runtime = self
1318
+ _captured_obj = python_obj
1319
+
1320
+ def wrapper(*args, **kwargs):
1321
+ # Save current instance context
1322
+ old_instance = runtime._current_instance
1323
+ old_this = runtime.scope.get('this')
1324
+ try:
1325
+ # Set Python object as current instance for this-> access
1326
+ runtime._current_instance = _captured_obj
1327
+ runtime.scope.set('this', _captured_obj)
1328
+ return runtime._call_function(func_node, list(args), kwargs)
1329
+ finally:
1330
+ # Restore previous context
1331
+ runtime._current_instance = old_instance
1332
+ if old_this is not None:
1333
+ runtime.scope.set('this', old_this)
1334
+ elif 'this' in runtime.scope.variables:
1335
+ del runtime.scope.variables['this']
1336
+
1337
+ return wrapper
1338
+
1218
1339
  def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
1219
1340
  """Overwrite a class method or function with the replacement.
1220
1341
 
@@ -1222,9 +1343,25 @@ class CSSLRuntime:
1222
1343
  - &ClassName::method - Overwrite method in CSSL class
1223
1344
  - &$PyObject.method - Overwrite method in Python shared object
1224
1345
  - &functionName - Overwrite standalone function
1346
+
1347
+ v4.2.6: In namespace mode (_payload_namespace_mode), track replacements
1348
+ instead of applying globally. The namespace handler will scope them.
1225
1349
  """
1226
1350
  from ..cssl_bridge import _live_objects, SharedObjectProxy
1227
1351
 
1352
+ # v4.2.6: Check for namespace mode
1353
+ namespace_mode = getattr(self, '_payload_namespace_mode', None)
1354
+ if namespace_mode and ref_member is None and not ref_class.startswith('$'):
1355
+ # Standalone function replacement in namespace mode
1356
+ # Track it for the namespace handler
1357
+ if not hasattr(self, '_namespace_replacements'):
1358
+ self._namespace_replacements = {}
1359
+ self._namespace_replacements[ref_class] = replacement_node
1360
+ # Still register the function normally (namespace handler will move it)
1361
+ self.scope.set(ref_class, replacement_node)
1362
+ self.global_scope.set(ref_class, replacement_node)
1363
+ return
1364
+
1228
1365
  # Handle Python shared objects
1229
1366
  if ref_class.startswith('$'):
1230
1367
  var_name = ref_class[1:]
@@ -1241,7 +1378,8 @@ class CSSLRuntime:
1241
1378
 
1242
1379
  # Overwrite Python object method
1243
1380
  if ref_member and hasattr(ref_obj, ref_member):
1244
- wrapper = self._create_python_wrapper(replacement_node)
1381
+ # v4.3.0: Create wrapper that passes Python object as 'this' context
1382
+ wrapper = self._create_python_method_wrapper(replacement_node, ref_obj)
1245
1383
  try:
1246
1384
  setattr(ref_obj, ref_member, wrapper)
1247
1385
  except (AttributeError, TypeError):
@@ -1250,12 +1388,49 @@ class CSSLRuntime:
1250
1388
 
1251
1389
  # Handle CSSL class method overwrite
1252
1390
  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
1391
+
1392
+ # v4.2.3: Handle standalone function reference (&functionName) including builtins
1393
+ if ref_member is None:
1394
+ # Check if this is a builtin function
1395
+ is_builtin = hasattr(self, 'builtins') and self.builtins.has_function(ref_class)
1396
+
1397
+ # Check if target_class is NOT a CSSLClass (it's either a function/builtin or None)
1398
+ is_cssl_class = isinstance(target_class, CSSLClass)
1399
+ # v4.2.5: Also check if target is a CSSL function (ASTNode with type 'function')
1400
+ is_cssl_function = (hasattr(target_class, 'type') and target_class.type == 'function')
1401
+
1402
+ if is_builtin or (target_class is None) or (not is_cssl_class and callable(target_class)) or is_cssl_function:
1403
+ # v4.2.3: CRITICAL - Store original BEFORE overwriting for %name captures
1404
+ # This ensures %exit() refers to the ORIGINAL exit, not the replacement
1405
+ if ref_class not in self._original_functions:
1406
+ if is_builtin:
1407
+ # Store the original builtin from _functions dict
1408
+ # (handles aliases like printl -> builtin_println)
1409
+ original_builtin = self.builtins._functions.get(ref_class)
1410
+ if original_builtin is not None:
1411
+ self._original_functions[ref_class] = original_builtin
1412
+ elif is_cssl_function:
1413
+ # v4.2.5: Store original CSSL function (ASTNode)
1414
+ self._original_functions[ref_class] = target_class
1415
+ elif target_class is not None and callable(target_class):
1416
+ # Store the original function/callable
1417
+ self._original_functions[ref_class] = target_class
1418
+
1419
+ # &functionName - overwrite the function/builtin
1257
1420
  self.scope.set(ref_class, replacement_node)
1258
1421
  self.global_scope.set(ref_class, replacement_node)
1422
+
1423
+ # Also overwrite in builtins dict if it's a builtin
1424
+ if is_builtin:
1425
+ # Create wrapper that calls CSSL function instead of builtin
1426
+ def make_wrapper(node, runtime):
1427
+ def wrapper(*args, **kwargs):
1428
+ return runtime._call_function(node, list(args), kwargs)
1429
+ return wrapper
1430
+ self.builtins._functions[ref_class] = make_wrapper(replacement_node, self)
1431
+ return
1432
+
1433
+ if target_class is None:
1259
1434
  return
1260
1435
 
1261
1436
  if isinstance(target_class, CSSLClass) and ref_member:
@@ -1357,6 +1532,58 @@ class CSSLRuntime:
1357
1532
  target_class.members[i] = append_node
1358
1533
  break
1359
1534
 
1535
+ def _overwrite_class_target(self, ref_class: str, ref_member: str, replacement_class: 'CSSLClass'):
1536
+ """Overwrite a target class with the replacement class.
1537
+
1538
+ v4.2.5: Used by 'embedded class' with &target syntax.
1539
+ Handles:
1540
+ - &ClassName - Overwrite CSSL class
1541
+ - &$PyObject - Overwrite Python shared object
1542
+
1543
+ v4.2.6: In namespace mode, track replacements instead of applying globally.
1544
+ """
1545
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1546
+
1547
+ # v4.2.6: Check for namespace mode
1548
+ namespace_mode = getattr(self, '_payload_namespace_mode', None)
1549
+ if namespace_mode and not ref_class.startswith('$') and not ref_class.startswith('@'):
1550
+ # Class replacement in namespace mode
1551
+ # Track it for the namespace handler
1552
+ if not hasattr(self, '_namespace_replacements'):
1553
+ self._namespace_replacements = {}
1554
+ self._namespace_replacements[ref_class] = replacement_class
1555
+ # Still register the class normally (namespace handler will move it)
1556
+ self.scope.set(ref_class, replacement_class)
1557
+ self.global_scope.set(ref_class, replacement_class)
1558
+ return
1559
+
1560
+ # Handle Python shared objects
1561
+ if ref_class.startswith('$'):
1562
+ var_name = ref_class[1:]
1563
+ # Replace the shared object with the new class
1564
+ _live_objects[var_name] = replacement_class
1565
+ self.scope.set(var_name, replacement_class)
1566
+ self.global_scope.set(var_name, replacement_class)
1567
+ return
1568
+
1569
+ # Handle @ prefix (global reference)
1570
+ if ref_class.startswith('@'):
1571
+ var_name = ref_class[1:]
1572
+ self.global_scope.set(var_name, replacement_class)
1573
+ self._promoted_globals[var_name] = replacement_class
1574
+ return
1575
+
1576
+ # Handle regular class reference
1577
+ target_class = self.scope.get(ref_class)
1578
+ if target_class is None:
1579
+ target_class = self.global_scope.get(ref_class)
1580
+
1581
+ # Replace the class definition
1582
+ self.scope.set(ref_class, replacement_class)
1583
+ self.global_scope.set(ref_class, replacement_class)
1584
+ if ref_class in self._promoted_globals:
1585
+ self._promoted_globals[ref_class] = replacement_class
1586
+
1360
1587
  def _exec_typed_declaration(self, node: ASTNode) -> Any:
1361
1588
  """Execute typed variable declaration: type<T> varName = value;
1362
1589
 
@@ -1638,6 +1865,18 @@ class CSSLRuntime:
1638
1865
  modifiers = func_info.get('modifiers', [])
1639
1866
  kwargs = kwargs or {}
1640
1867
 
1868
+ # v4.2.5: Deferred &target replacement for non-embedded functions
1869
+ # If function has &target and hasn't been applied yet, apply now on first call
1870
+ append_ref_class = func_info.get('append_ref_class')
1871
+ if append_ref_class and not func_info.get('_target_applied', False):
1872
+ append_mode = func_info.get('append_mode', False)
1873
+ append_ref_member = func_info.get('append_ref_member')
1874
+ if append_mode:
1875
+ self._append_to_target(append_ref_class, append_ref_member, func_node)
1876
+ else:
1877
+ self._overwrite_target(append_ref_class, append_ref_member, func_node)
1878
+ func_node.value['_target_applied'] = True
1879
+
1641
1880
  # Check for undefined modifier - suppress errors if present
1642
1881
  is_undefined = 'undefined' in modifiers
1643
1882
 
@@ -2012,6 +2251,101 @@ class CSSLRuntime:
2012
2251
 
2013
2252
  return None
2014
2253
 
2254
+ def _exec_param_switch(self, node: ASTNode) -> Any:
2255
+ """Execute param switch statement for open parameters.
2256
+
2257
+ v4.2.5: Switch on which parameters were provided.
2258
+
2259
+ Syntax:
2260
+ switch(Params) {
2261
+ case name: // if 'name' param exists
2262
+ case name & age: // if both exist
2263
+ case name & not age: // if 'name' exists but 'age' doesn't
2264
+ except name: // if 'name' does NOT exist
2265
+ default: // fallback
2266
+ always: // always runs after match
2267
+ finally: // cleanup, always runs
2268
+ }
2269
+ """
2270
+ params_name = node.value.get('params')
2271
+ open_kwargs = self.scope.get('_OpenKwargs') or {}
2272
+
2273
+ matched = False
2274
+ always_node = None
2275
+ finally_node = None
2276
+ default_node = None
2277
+
2278
+ # First pass: find always, finally, default nodes
2279
+ for child in node.children:
2280
+ if child.type == 'param_always':
2281
+ always_node = child
2282
+ elif child.type == 'param_finally':
2283
+ finally_node = child
2284
+ elif child.type == 'param_default':
2285
+ default_node = child
2286
+
2287
+ try:
2288
+ # Execute matching cases
2289
+ for child in node.children:
2290
+ if child.type == 'param_case':
2291
+ condition = child.value.get('condition')
2292
+ if self._eval_param_condition(condition, open_kwargs):
2293
+ matched = True
2294
+ try:
2295
+ for stmt in child.children:
2296
+ self._execute_node(stmt)
2297
+ except CSSLBreak:
2298
+ break
2299
+
2300
+ # Execute default if no match
2301
+ if not matched and default_node:
2302
+ try:
2303
+ for stmt in default_node.children:
2304
+ self._execute_node(stmt)
2305
+ except CSSLBreak:
2306
+ pass
2307
+
2308
+ # Execute always block (runs after a match, before finally)
2309
+ if matched and always_node:
2310
+ try:
2311
+ for stmt in always_node.children:
2312
+ self._execute_node(stmt)
2313
+ except CSSLBreak:
2314
+ pass
2315
+
2316
+ finally:
2317
+ # Execute finally block (always runs, even on break/exception)
2318
+ if finally_node:
2319
+ for stmt in finally_node.children:
2320
+ self._execute_node(stmt)
2321
+
2322
+ return None
2323
+
2324
+ def _eval_param_condition(self, condition: dict, kwargs: dict) -> bool:
2325
+ """Evaluate a param switch condition.
2326
+
2327
+ Condition types:
2328
+ {'type': 'exists', 'param': 'name'} -> 'name' in kwargs
2329
+ {'type': 'not', 'param': 'name'} -> 'name' not in kwargs
2330
+ {'type': 'and', 'left': {...}, 'right': {...}} -> left AND right
2331
+ """
2332
+ cond_type = condition.get('type')
2333
+
2334
+ if cond_type == 'exists':
2335
+ param = condition.get('param')
2336
+ return param in kwargs
2337
+
2338
+ elif cond_type == 'not':
2339
+ param = condition.get('param')
2340
+ return param not in kwargs
2341
+
2342
+ elif cond_type == 'and':
2343
+ left = self._eval_param_condition(condition.get('left'), kwargs)
2344
+ right = self._eval_param_condition(condition.get('right'), kwargs)
2345
+ return left and right
2346
+
2347
+ return False
2348
+
2015
2349
  def _exec_return(self, node: ASTNode) -> Any:
2016
2350
  """Execute return statement.
2017
2351
 
@@ -2114,7 +2448,7 @@ class CSSLRuntime:
2114
2448
  return None
2115
2449
 
2116
2450
  def _exec_try(self, node: ASTNode) -> Any:
2117
- """Execute try/catch block"""
2451
+ """Execute try/catch/finally block"""
2118
2452
  try:
2119
2453
  for child in node.children:
2120
2454
  if child.type == 'try-block':
@@ -2128,6 +2462,21 @@ class CSSLRuntime:
2128
2462
  self.scope.set(error_var, str(e))
2129
2463
  for stmt in child.children:
2130
2464
  self._execute_node(stmt)
2465
+ except Exception as e:
2466
+ # v4.2.6: Also catch Python exceptions
2467
+ for child in node.children:
2468
+ if child.type == 'catch-block':
2469
+ error_var = child.value.get('error_var') if child.value else None
2470
+ if error_var:
2471
+ self.scope.set(error_var, str(e))
2472
+ for stmt in child.children:
2473
+ self._execute_node(stmt)
2474
+ finally:
2475
+ # v4.2.6: Execute finally block if present
2476
+ for child in node.children:
2477
+ if child.type == 'finally-block':
2478
+ for stmt in child.children:
2479
+ self._execute_node(stmt)
2131
2480
 
2132
2481
  return None
2133
2482
 
@@ -2831,6 +3180,15 @@ class CSSLRuntime:
2831
3180
  final_value = {**current_value, **source}
2832
3181
  elif isinstance(current_value, str) and isinstance(source, str):
2833
3182
  final_value = current_value + source
3183
+ # Handle CSSL container types (DataStruct, Vector, Stack, etc.)
3184
+ elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add'):
3185
+ if hasattr(current_value, 'append'):
3186
+ current_value.append(source)
3187
+ elif hasattr(current_value, 'push'):
3188
+ current_value.push(source)
3189
+ elif hasattr(current_value, 'add'):
3190
+ current_value.add(source)
3191
+ final_value = current_value
2834
3192
  elif current_value is None:
2835
3193
  final_value = [source] if not isinstance(source, list) else source
2836
3194
  else:
@@ -3353,6 +3711,31 @@ class CSSLRuntime:
3353
3711
 
3354
3712
  if node.type == 'identifier':
3355
3713
  name = node.value
3714
+
3715
+ # Handle enum/namespace access: Colors::RED, MyNamespace::func
3716
+ if '::' in name:
3717
+ parts = name.split('::', 1)
3718
+ container_name = parts[0]
3719
+ member_name = parts[1]
3720
+
3721
+ # Look up the container (enum, class, or namespace)
3722
+ container = self.scope.get(container_name)
3723
+ if container is None:
3724
+ container = self.global_scope.get(container_name)
3725
+ if container is None:
3726
+ container = self._promoted_globals.get(container_name)
3727
+
3728
+ if container is not None:
3729
+ # If it's a dict-like object (enum or namespace), get the member
3730
+ if isinstance(container, dict):
3731
+ return container.get(member_name)
3732
+ # If it's an object with the member as an attribute
3733
+ elif hasattr(container, member_name):
3734
+ return getattr(container, member_name)
3735
+
3736
+ # Fall through to normal lookup if container not found
3737
+ return None
3738
+
3356
3739
  value = self.scope.get(name)
3357
3740
  # Check if it's a class member in current instance context
3358
3741
  # This allows accessing members without 'this->' inside methods
@@ -3426,14 +3809,22 @@ class CSSLRuntime:
3426
3809
 
3427
3810
  if node.type == 'captured_ref':
3428
3811
  # %<name> captured reference - use value captured at infusion registration time
3812
+ # Priority: The % prefix means "get the ORIGINAL value before any replacement"
3429
3813
  name = node.value
3430
- # First check captured values from current injection context
3814
+
3815
+ # 1. First check captured values from current injection context
3431
3816
  if name in self._current_captured_values:
3432
3817
  captured_value = self._current_captured_values[name]
3433
- # Only use captured value if it's not None
3434
3818
  if captured_value is not None:
3435
3819
  return captured_value
3436
- # Fall back to normal resolution if not captured or capture was None
3820
+
3821
+ # 2. v4.2.3: Check _original_functions FIRST - this is the pre-replacement value
3822
+ # This ensures %exit() refers to the ORIGINAL exit when using &exit
3823
+ value = self._original_functions.get(name)
3824
+ if value is not None:
3825
+ return value
3826
+
3827
+ # 3. Fall back to scope/builtins if no original was captured
3437
3828
  value = self.scope.get(name)
3438
3829
  if value is None:
3439
3830
  value = self.global_scope.get(name)
@@ -3444,9 +3835,6 @@ class CSSLRuntime:
3444
3835
  value = lambda code=0, rt=runtime: rt.exit(code)
3445
3836
  else:
3446
3837
  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
3838
  if value is not None:
3451
3839
  return value
3452
3840
  # Build helpful error for captured reference
@@ -3782,15 +4170,21 @@ class CSSLRuntime:
3782
4170
 
3783
4171
  if op == '/':
3784
4172
  r = self._to_number(right)
3785
- return self._to_number(left) / r if r != 0 else 0
4173
+ if r == 0:
4174
+ raise CSSLRuntimeError("Division by zero")
4175
+ return self._to_number(left) / r
3786
4176
 
3787
4177
  if op == '//':
3788
4178
  r = self._to_number(right)
3789
- return self._to_number(left) // r if r != 0 else 0
4179
+ if r == 0:
4180
+ raise CSSLRuntimeError("Integer division by zero")
4181
+ return self._to_number(left) // r
3790
4182
 
3791
4183
  if op == '%':
3792
4184
  r = self._to_number(right)
3793
- return self._to_number(left) % r if r != 0 else 0
4185
+ if r == 0:
4186
+ raise CSSLRuntimeError("Modulo by zero")
4187
+ return self._to_number(left) % r
3794
4188
 
3795
4189
  if op == '**':
3796
4190
  return self._to_number(left) ** self._to_number(right)
@@ -4144,30 +4538,44 @@ class CSSLRuntime:
4144
4538
  )
4145
4539
 
4146
4540
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
4147
- """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
4541
+ """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' expression.
4148
4542
 
4149
4543
  Creates a new instance of a CSSL class and calls its constructor.
4150
4544
  Supports multiple constructors (constr keyword), class parameters,
4151
4545
  and automatic parent constructor calling.
4152
4546
 
4153
4547
  With '@' prefix (new @ClassName), looks only in global scope.
4548
+ With Namespace:: prefix, looks in the namespace dict first.
4154
4549
  """
4155
4550
  class_name = node.value.get('class')
4551
+ namespace = node.value.get('namespace') # v4.2.6: Namespace::ClassName support
4156
4552
  is_global_ref = node.value.get('is_global_ref', False)
4157
4553
  args = [self._evaluate(arg) for arg in node.value.get('args', [])]
4158
4554
  kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
4159
4555
 
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)
4556
+ class_def = None
4557
+
4558
+ # v4.2.6: Handle Namespace::ClassName lookup
4559
+ if namespace:
4560
+ # Look up namespace dict first
4561
+ ns_dict = self.scope.get(namespace)
4562
+ if ns_dict is None:
4563
+ ns_dict = self.global_scope.get(namespace)
4564
+ if isinstance(ns_dict, dict) and class_name in ns_dict:
4565
+ class_def = ns_dict[class_name]
4566
+
4567
+ # Get class definition from scope (if not found in namespace)
4568
+ if class_def is None:
4569
+ if is_global_ref:
4570
+ # With @ prefix, only look in global scope
4571
+ class_def = self._promoted_globals.get(class_name)
4572
+ if class_def is None:
4573
+ class_def = self.global_scope.get(class_name)
4574
+ else:
4575
+ # Normal lookup: local scope first, then global
4576
+ class_def = self.scope.get(class_name)
4577
+ if class_def is None:
4578
+ class_def = self.global_scope.get(class_name)
4171
4579
 
4172
4580
  if class_def is None:
4173
4581
  # Build detailed error with suggestions
@@ -4216,6 +4624,18 @@ class CSSLRuntime:
4216
4624
  hint=f"'{class_name}' is of type {type(class_def).__name__}"
4217
4625
  )
4218
4626
 
4627
+ # v4.2.5: Deferred &target replacement for non-embedded classes
4628
+ # Apply on first instantiation if pending
4629
+ if hasattr(class_def, '_pending_target') and class_def._pending_target:
4630
+ pending = class_def._pending_target
4631
+ self._overwrite_class_target(
4632
+ pending['append_ref_class'],
4633
+ pending.get('append_ref_member'),
4634
+ class_def
4635
+ )
4636
+ class_def._target_applied = True
4637
+ class_def._pending_target = None # Clear pending
4638
+
4219
4639
  # Create new instance
4220
4640
  instance = CSSLInstance(class_def)
4221
4641
 
@@ -4939,12 +5359,19 @@ class CSSLRuntime:
4939
5359
  index = self._evaluate(node.value.get('index'))
4940
5360
 
4941
5361
  if obj is None:
4942
- return None
5362
+ raise CSSLRuntimeError(f"Cannot index into None/null value")
4943
5363
 
4944
5364
  try:
4945
5365
  return obj[index]
4946
- except (IndexError, KeyError, TypeError):
4947
- return None
5366
+ except IndexError:
5367
+ # v4.2.6: Throw error instead of returning None
5368
+ length = len(obj) if hasattr(obj, '__len__') else 'unknown'
5369
+ raise CSSLRuntimeError(f"Index {index} out of bounds (length: {length})")
5370
+ except KeyError:
5371
+ # v4.2.6: Throw error for missing dict keys
5372
+ raise CSSLRuntimeError(f"Key '{index}' not found in dictionary")
5373
+ except TypeError as e:
5374
+ raise CSSLRuntimeError(f"Cannot index: {e}")
4948
5375
 
4949
5376
  def _set_member(self, node: ASTNode, value: Any):
4950
5377
  """Set member value"""
@@ -5238,10 +5665,17 @@ class CSSLRuntime:
5238
5665
 
5239
5666
  def _emit_output(self, text: str, level: str = 'normal'):
5240
5667
  """Emit output through callback or print"""
5668
+ import sys
5241
5669
  if self._output_callback:
5242
5670
  self._output_callback(text, level)
5243
5671
  else:
5244
- print(text, end='')
5672
+ # Handle encoding issues on Windows console
5673
+ try:
5674
+ print(text, end='')
5675
+ except UnicodeEncodeError:
5676
+ # Fallback: encode with errors='replace' for unsupported chars
5677
+ encoded = text.encode(sys.stdout.encoding or 'utf-8', errors='replace')
5678
+ print(encoded.decode(sys.stdout.encoding or 'utf-8', errors='replace'), end='')
5245
5679
 
5246
5680
  def output(self, text: str):
5247
5681
  """Output text"""