IncludeCPP 3.4.10__py3-none-any.whl → 3.4.21__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.
@@ -14,15 +14,49 @@ from .cssl_builtins import CSSLBuiltins
14
14
  from .cssl_modules import get_module_registry, get_standard_module
15
15
  from .cssl_types import (
16
16
  Parameter, DataStruct, Shuffled, Iterator, Combo,
17
- Stack, Vector, DataSpace, OpenQuote
17
+ Stack, Vector, Array, DataSpace, OpenQuote
18
18
  )
19
19
 
20
20
 
21
21
  class CSSLRuntimeError(Exception):
22
- """Runtime error during CSSL execution"""
23
- def __init__(self, message: str, line: int = 0):
22
+ """Runtime error during CSSL execution with detailed context"""
23
+ def __init__(self, message: str, line: int = 0, context: str = None, hint: str = None):
24
24
  self.line = line
25
- super().__init__(f"{message} (line {line})" if line else message)
25
+ self.context = context
26
+ self.hint = hint
27
+
28
+ # Build detailed error message
29
+ error_parts = []
30
+
31
+ # Main error message
32
+ if line:
33
+ error_parts.append(f"Error at line {line}: {message}")
34
+ else:
35
+ error_parts.append(f"Error: {message}")
36
+
37
+ # Add context if available
38
+ if context:
39
+ error_parts.append(f" Context: {context}")
40
+
41
+ # Add hint if available
42
+ if hint:
43
+ error_parts.append(f" Hint: {hint}")
44
+
45
+ super().__init__("\n".join(error_parts))
46
+
47
+
48
+ # Common error hints for better user experience
49
+ ERROR_HINTS = {
50
+ 'undefined_variable': "Did you forget to declare the variable? Use 'string x = ...' or 'int x = ...'",
51
+ 'undefined_function': "Check function name spelling. Functions are case-sensitive.",
52
+ 'type_mismatch': "Try using explicit type conversion: toInt(), toFloat(), toString()",
53
+ 'null_reference': "Variable is null. Check if it was properly initialized.",
54
+ 'index_out_of_bounds': "Array index must be >= 0 and < array.length()",
55
+ 'division_by_zero': "Cannot divide by zero. Add a check: if (divisor != 0) { ... }",
56
+ 'invalid_operation': "This operation is not supported for this type.",
57
+ 'missing_semicolon': "Statement might be missing a semicolon (;)",
58
+ 'missing_brace': "Check for matching opening and closing braces { }",
59
+ }
26
60
 
27
61
 
28
62
  class CSSLBreak(Exception):
@@ -106,6 +140,7 @@ class CSSLRuntime:
106
140
  self._modules: Dict[str, Any] = {}
107
141
  self._global_structs: Dict[str, Any] = {} # Global structs for s@<name> references
108
142
  self._function_injections: Dict[str, List[ASTNode]] = {} # NEW: Permanent function injections
143
+ self._function_replaced: Dict[str, bool] = {} # NEW: Track replaced functions (<<==)
109
144
  self._promoted_globals: Dict[str, Any] = {} # NEW: Variables promoted via global()
110
145
  self._running = False
111
146
  self._exit_code = 0
@@ -301,7 +336,7 @@ class CSSLRuntime:
301
336
  # Handle typed variable declaration: type<T> varName = value;
302
337
  result = self._exec_typed_declaration(child)
303
338
  elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
304
- 'if', 'while', 'for', 'foreach', 'switch', 'try'):
339
+ 'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
305
340
  result = self._execute_node(child)
306
341
  elif child.type == 'call':
307
342
  result = self._eval_call(child)
@@ -614,7 +649,7 @@ class CSSLRuntime:
614
649
  elif type_name == 'json':
615
650
  instance = {} if value_node is None else self._evaluate(value_node)
616
651
  elif type_name == 'array':
617
- instance = [] if value_node is None else self._evaluate(value_node)
652
+ instance = Array(element_type)
618
653
  else:
619
654
  # Default: evaluate the value or set to None
620
655
  instance = self._evaluate(value_node) if value_node else None
@@ -649,8 +684,14 @@ class CSSLRuntime:
649
684
  value = self._evaluate(inner.value.get('value'))
650
685
 
651
686
  # Get variable name from target
652
- if isinstance(target, ASTNode) and target.type == 'identifier':
653
- var_name = target.value
687
+ if isinstance(target, ASTNode):
688
+ if target.type == 'identifier':
689
+ var_name = target.value
690
+ elif target.type == 'global_ref':
691
+ # r@Name = value
692
+ var_name = target.value
693
+ else:
694
+ var_name = str(target.value) if hasattr(target, 'value') else str(target)
654
695
  elif isinstance(target, str):
655
696
  var_name = target
656
697
  else:
@@ -744,12 +785,16 @@ class CSSLRuntime:
744
785
  return None
745
786
 
746
787
  def _exec_for(self, node: ASTNode) -> Any:
747
- """Execute for loop"""
788
+ """Execute Python-style for loop: for (i in range(start, end, step)) { }"""
748
789
  var_name = node.value.get('var')
749
790
  start = int(self._evaluate(node.value.get('start')))
750
791
  end = int(self._evaluate(node.value.get('end')))
751
792
 
752
- for i in range(start, end):
793
+ # Optional step parameter (default is 1)
794
+ step_node = node.value.get('step')
795
+ step = int(self._evaluate(step_node)) if step_node else 1
796
+
797
+ for i in range(start, end, step):
753
798
  self.scope.set(var_name, i)
754
799
  try:
755
800
  for child in node.children:
@@ -761,6 +806,73 @@ class CSSLRuntime:
761
806
 
762
807
  return None
763
808
 
809
+ def _exec_c_for(self, node: ASTNode) -> Any:
810
+ """Execute C-style for loop: for (init; condition; update) { }
811
+
812
+ Supports:
813
+ - for (int i = 0; i < n; i++) { }
814
+ - for (int i = 0; i < n; i = i + 1) { }
815
+ - for (i = 0; i < n; i += 1) { }
816
+ - for (; condition; ) { } (infinite loop with condition)
817
+ """
818
+ init = node.value.get('init')
819
+ condition = node.value.get('condition')
820
+ update = node.value.get('update')
821
+
822
+ # Execute init statement
823
+ if init:
824
+ var_name = init.value.get('var')
825
+ init_value = self._evaluate(init.value.get('value'))
826
+ self.scope.set(var_name, init_value)
827
+ else:
828
+ var_name = None
829
+
830
+ # Main loop
831
+ while True:
832
+ # Check condition
833
+ if condition:
834
+ cond_result = self._evaluate(condition)
835
+ if not cond_result:
836
+ break
837
+ # If no condition, this would be infinite - we still need a way to break
838
+
839
+ # Execute body
840
+ try:
841
+ for child in node.children:
842
+ self._execute_node(child)
843
+ except CSSLBreak:
844
+ break
845
+ except CSSLContinue:
846
+ pass # Continue to update, then next iteration
847
+
848
+ # Execute update
849
+ if update:
850
+ self._exec_c_for_update(update)
851
+
852
+ return None
853
+
854
+ def _exec_c_for_update(self, update: 'ASTNode') -> None:
855
+ """Execute the update part of a C-style for loop."""
856
+ var_name = update.value.get('var')
857
+ op = update.value.get('op')
858
+ value_node = update.value.get('value')
859
+
860
+ current = self.scope.get(var_name) or 0
861
+
862
+ if op == 'increment':
863
+ self.scope.set(var_name, current + 1)
864
+ elif op == 'decrement':
865
+ self.scope.set(var_name, current - 1)
866
+ elif op == 'add':
867
+ add_value = self._evaluate(value_node)
868
+ self.scope.set(var_name, current + add_value)
869
+ elif op == 'subtract':
870
+ sub_value = self._evaluate(value_node)
871
+ self.scope.set(var_name, current - sub_value)
872
+ elif op == 'assign':
873
+ new_value = self._evaluate(value_node)
874
+ self.scope.set(var_name, new_value)
875
+
764
876
  def _exec_foreach(self, node: ASTNode) -> Any:
765
877
  """Execute foreach loop"""
766
878
  var_name = node.value.get('var')
@@ -885,7 +997,7 @@ class CSSLRuntime:
885
997
  def _apply_injection_filter(self, source: Any, filter_info: dict) -> Any:
886
998
  """Apply injection filter to extract specific data from source.
887
999
 
888
- Filters:
1000
+ All BruteInjector Helpers:
889
1001
  - string::where=VALUE - Filter strings containing VALUE
890
1002
  - string::length=LENGTH - Filter strings of specific length
891
1003
  - integer::where=VALUE - Filter integers matching VALUE
@@ -894,9 +1006,12 @@ class CSSLRuntime:
894
1006
  - array::index=INDEX - Get specific index from array
895
1007
  - array::length=LENGTH - Filter arrays of specific length
896
1008
  - vector::where=VALUE - Filter vectors containing VALUE
1009
+ - vector::index=INDEX - Get specific index from vector
1010
+ - vector::length=LENGTH - Filter vectors of specific length
897
1011
  - combo::filterdb - Get filter database from combo
898
1012
  - combo::blocked - Get blocked items from combo
899
- - dynamic::VarName=VALUE - Filter by dynamic variable
1013
+ - dynamic::VarName=VALUE - Filter by dynamic variable value
1014
+ - sql::data - Return only SQL-compatible data
900
1015
  """
901
1016
  if not filter_info:
902
1017
  return source
@@ -908,25 +1023,28 @@ class CSSLRuntime:
908
1023
  filter_type, helper = filter_key.split('::', 1)
909
1024
  filter_val = self._evaluate(filter_value) if isinstance(filter_value, ASTNode) else filter_value
910
1025
 
1026
+ # === STRING HELPERS ===
911
1027
  if filter_type == 'string':
912
1028
  if helper == 'where':
913
- if isinstance(result, str) and filter_val in result:
914
- pass # Keep result
1029
+ if isinstance(result, str):
1030
+ result = result if filter_val in result else None
915
1031
  elif isinstance(result, list):
916
1032
  result = [item for item in result if isinstance(item, str) and filter_val in item]
917
- elif helper == 'length':
1033
+ elif helper in ('length', 'lenght'): # Support common typo
918
1034
  if isinstance(result, str):
919
1035
  result = result if len(result) == filter_val else None
920
1036
  elif isinstance(result, list):
921
1037
  result = [item for item in result if isinstance(item, str) and len(item) == filter_val]
922
1038
 
1039
+ # === INTEGER HELPERS ===
923
1040
  elif filter_type == 'integer':
924
1041
  if helper == 'where':
925
- if isinstance(result, int) and result == filter_val:
926
- pass # Keep result
1042
+ if isinstance(result, int):
1043
+ result = result if result == filter_val else None
927
1044
  elif isinstance(result, list):
928
1045
  result = [item for item in result if isinstance(item, int) and item == filter_val]
929
1046
 
1047
+ # === JSON HELPERS ===
930
1048
  elif filter_type == 'json':
931
1049
  if helper == 'key':
932
1050
  if isinstance(result, dict):
@@ -935,29 +1053,68 @@ class CSSLRuntime:
935
1053
  result = [item.get(filter_val) for item in result if isinstance(item, dict) and filter_val in item]
936
1054
  elif helper == 'value':
937
1055
  if isinstance(result, dict):
938
- result = {k: v for k, v in result.items() if v == filter_val}
1056
+ # Find key(s) with matching value
1057
+ matches = [k for k, v in result.items() if v == filter_val]
1058
+ result = matches[0] if len(matches) == 1 else matches
939
1059
  elif isinstance(result, list):
940
1060
  result = [item for item in result if (isinstance(item, dict) and filter_val in item.values())]
941
1061
 
942
- elif filter_type == 'array' or filter_type == 'vector':
1062
+ # === ARRAY HELPERS ===
1063
+ elif filter_type == 'array':
943
1064
  if helper == 'index':
944
- if isinstance(result, list) and 0 <= filter_val < len(result):
945
- result = result[filter_val]
946
- elif helper == 'length':
1065
+ if isinstance(result, (list, tuple)):
1066
+ idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
1067
+ if 0 <= idx < len(result):
1068
+ result = result[idx]
1069
+ else:
1070
+ result = None
1071
+ elif helper in ('length', 'lenght'): # Support common typo
1072
+ if isinstance(result, (list, tuple)):
1073
+ result = result if len(result) == filter_val else []
1074
+ elif helper == 'where':
947
1075
  if isinstance(result, list):
1076
+ result = [item for item in result if item == filter_val]
1077
+
1078
+ # === VECTOR HELPERS ===
1079
+ elif filter_type == 'vector':
1080
+ if helper == 'index':
1081
+ if isinstance(result, (list, tuple)):
1082
+ idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
1083
+ if 0 <= idx < len(result):
1084
+ result = result[idx]
1085
+ else:
1086
+ result = None
1087
+ elif helper in ('length', 'lenght'): # Support common typo
1088
+ if isinstance(result, (list, tuple)):
948
1089
  result = result if len(result) == filter_val else []
949
1090
  elif helper == 'where':
950
1091
  if isinstance(result, list):
951
1092
  result = [item for item in result if item == filter_val]
952
1093
 
1094
+ # === COMBO HELPERS ===
953
1095
  elif filter_type == 'combo':
954
1096
  if helper == 'filterdb':
955
1097
  if hasattr(result, '_filterdb'):
956
1098
  result = result._filterdb
1099
+ elif hasattr(result, 'filterdb'):
1100
+ result = result.filterdb
957
1101
  elif helper == 'blocked':
958
1102
  if hasattr(result, '_blocked'):
959
1103
  result = result._blocked
1104
+ elif hasattr(result, 'blocked'):
1105
+ result = result.blocked
1106
+
1107
+ # === DYNAMIC HELPERS ===
1108
+ elif filter_type == 'dynamic':
1109
+ # dynamic::VarName=VALUE - Match if variable equals value
1110
+ var_name = helper
1111
+ var_value = self.scope.get(var_name)
1112
+ if var_value == filter_val:
1113
+ pass # Keep result
1114
+ else:
1115
+ result = None
960
1116
 
1117
+ # === SQL HELPERS ===
961
1118
  elif filter_type == 'sql':
962
1119
  if helper == 'data':
963
1120
  # Return only SQL-compatible data types
@@ -1037,6 +1194,12 @@ class CSSLRuntime:
1037
1194
  self.scope.set(target.value, final_value)
1038
1195
  elif target.type == 'module_ref':
1039
1196
  self._set_module_value(target.value, final_value)
1197
+ elif target.type == 'shared_ref':
1198
+ # $Name <== value - create/update shared object
1199
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1200
+ name = target.value
1201
+ _live_objects[name] = final_value
1202
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
1040
1203
  elif target.type == 'member_access':
1041
1204
  self._set_member(target, final_value)
1042
1205
  elif target.type == 'call':
@@ -1105,6 +1268,12 @@ class CSSLRuntime:
1105
1268
  self.scope.set(target.value, final_value)
1106
1269
  elif target.type == 'module_ref':
1107
1270
  self._set_module_value(target.value, final_value)
1271
+ elif target.type == 'shared_ref':
1272
+ # value ==> $Name - create/update shared object
1273
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1274
+ name = target.value
1275
+ _live_objects[name] = final_value
1276
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
1108
1277
  elif target.type == 'member_access':
1109
1278
  self._set_member(target, final_value)
1110
1279
 
@@ -1114,13 +1283,13 @@ class CSSLRuntime:
1114
1283
  """Execute code infusion (<<==, +<<==, -<<==)
1115
1284
 
1116
1285
  Modes:
1117
- - replace: func <<== { code } (inject code into function, replaces)
1118
- - add: func +<<== { code } (add code to function)
1119
- - remove: func -<<== { code } (remove matching code from function)
1286
+ - replace: func <<== { code } - REPLACES function body (original won't execute)
1287
+ - add: func +<<== { code } - ADDS code to function (both execute)
1288
+ - remove: func -<<== { code } - REMOVES matching code from function
1120
1289
  """
1121
1290
  target = node.value.get('target')
1122
1291
  code_block = node.value.get('code')
1123
- mode = node.value.get('mode', 'add')
1292
+ mode = node.value.get('mode', 'replace') # Default is REPLACE for <<==
1124
1293
 
1125
1294
  # Get function name from target
1126
1295
  func_name = None
@@ -1136,17 +1305,21 @@ class CSSLRuntime:
1136
1305
  return None
1137
1306
 
1138
1307
  if mode == 'add':
1139
- # Add code to function (permanent injection)
1308
+ # +<<== : Add code to function (both injection + original execute)
1140
1309
  self.register_function_injection(func_name, code_block)
1310
+ self._function_replaced[func_name] = False # Don't replace, just add
1141
1311
  elif mode == 'replace':
1142
- # Replace - clear existing and add new
1312
+ # <<== : Replace function body (only injection executes, original skipped)
1143
1313
  self._function_injections[func_name] = [code_block]
1314
+ self._function_replaced[func_name] = True # Mark as replaced
1144
1315
  elif mode == 'remove':
1145
- # Remove matching code patterns from function
1316
+ # -<<== : Remove matching code from function body
1317
+ # For now, this removes all injections for the function
1146
1318
  if func_name in self._function_injections:
1147
- # For simplicity, clear all injections for this function
1148
- # A more sophisticated implementation would compare code blocks
1149
1319
  self._function_injections[func_name] = []
1320
+ self._function_replaced[func_name] = False
1321
+ # Note: Removing from actual function body would require AST manipulation
1322
+ # which is complex - for now we just clear injections
1150
1323
 
1151
1324
  return None
1152
1325
 
@@ -1189,6 +1362,14 @@ class CSSLRuntime:
1189
1362
  self.scope.set(target.value, source)
1190
1363
  elif target.type == 'module_ref':
1191
1364
  self._set_module_value(target.value, source)
1365
+ elif target.type == 'shared_ref':
1366
+ # $Name <== value - create/update shared object
1367
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1368
+ name = target.value
1369
+ _live_objects[name] = source
1370
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, source))
1371
+ elif target.type == 'member_access':
1372
+ self._set_member(target, source)
1192
1373
 
1193
1374
  return source
1194
1375
 
@@ -1204,6 +1385,16 @@ class CSSLRuntime:
1204
1385
  # r@Name = value - store in promoted globals
1205
1386
  self._promoted_globals[target.value] = value
1206
1387
  self.global_scope.set(target.value, value)
1388
+ elif target.type == 'shared_ref':
1389
+ # $Name = value - create/update shared object
1390
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1391
+ name = target.value
1392
+ _live_objects[name] = value
1393
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, value))
1394
+ elif target.type == 'module_ref':
1395
+ # @Name = value - store in promoted globals (like global keyword)
1396
+ self._promoted_globals[target.value] = value
1397
+ self.global_scope.set(target.value, value)
1207
1398
  elif target.type == 'member_access':
1208
1399
  self._set_member(target, value)
1209
1400
  elif target.type == 'index_access':
@@ -1307,6 +1498,45 @@ class CSSLRuntime:
1307
1498
  value = self.global_scope.get(node.value)
1308
1499
  return value
1309
1500
 
1501
+ if node.type == 'shared_ref':
1502
+ # $<name> shared object reference
1503
+ # Returns the SharedObjectProxy for live access
1504
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1505
+ name = node.value
1506
+ if name in _live_objects:
1507
+ return SharedObjectProxy(name, _live_objects[name])
1508
+ # Check if stored in runtime's scope as $name
1509
+ scoped_val = self.global_scope.get(f'${name}')
1510
+ if scoped_val is not None:
1511
+ return scoped_val
1512
+ raise CSSLRuntimeError(f"Shared object '${name}' not found. Use share() to share objects.")
1513
+
1514
+ if node.type == 'type_instantiation':
1515
+ # Create new instance of a type: stack<string>, vector<int>, etc.
1516
+ type_name = node.value.get('type')
1517
+ element_type = node.value.get('element_type', 'dynamic')
1518
+
1519
+ if type_name == 'stack':
1520
+ return Stack(element_type)
1521
+ elif type_name == 'vector':
1522
+ return Vector(element_type)
1523
+ elif type_name == 'datastruct':
1524
+ return DataStruct(element_type)
1525
+ elif type_name == 'shuffled':
1526
+ return Shuffled(element_type)
1527
+ elif type_name == 'iterator':
1528
+ return Iterator(element_type)
1529
+ elif type_name == 'combo':
1530
+ return Combo(element_type)
1531
+ elif type_name == 'dataspace':
1532
+ return DataSpace(element_type)
1533
+ elif type_name == 'openquote':
1534
+ return OpenQuote()
1535
+ elif type_name == 'array':
1536
+ return Array(element_type)
1537
+ else:
1538
+ return None
1539
+
1310
1540
  if node.type == 'binary':
1311
1541
  return self._eval_binary(node)
1312
1542
 
@@ -1316,6 +1546,10 @@ class CSSLRuntime:
1316
1546
  if node.type == 'call':
1317
1547
  return self._eval_call(node)
1318
1548
 
1549
+ if node.type == 'typed_call':
1550
+ # Handle OpenFind<type>(args) style calls
1551
+ return self._eval_typed_call(node)
1552
+
1319
1553
  if node.type == 'member_access':
1320
1554
  return self._eval_member_access(node)
1321
1555
 
@@ -1342,32 +1576,161 @@ class CSSLRuntime:
1342
1576
  return None
1343
1577
 
1344
1578
  def _eval_binary(self, node: ASTNode) -> Any:
1345
- """Evaluate binary operation"""
1579
+ """Evaluate binary operation with auto-casting support"""
1346
1580
  op = node.value.get('op')
1347
1581
  left = self._evaluate(node.value.get('left'))
1348
1582
  right = self._evaluate(node.value.get('right'))
1349
1583
 
1350
- operators = {
1351
- '+': lambda a, b: a + b,
1352
- '-': lambda a, b: a - b,
1353
- '*': lambda a, b: a * b,
1354
- '/': lambda a, b: a / b if b != 0 else 0,
1355
- '%': lambda a, b: a % b if b != 0 else 0,
1356
- '==': lambda a, b: a == b,
1357
- '!=': lambda a, b: a != b,
1358
- '<': lambda a, b: a < b,
1359
- '>': lambda a, b: a > b,
1360
- '<=': lambda a, b: a <= b,
1361
- '>=': lambda a, b: a >= b,
1362
- 'and': lambda a, b: a and b,
1363
- 'or': lambda a, b: a or b,
1364
- }
1584
+ # === AUTO-CAST FOR STRING OPERATIONS ===
1585
+ if op == '+':
1586
+ # String concatenation with auto-cast
1587
+ if isinstance(left, str) or isinstance(right, str):
1588
+ return str(left if left is not None else '') + str(right if right is not None else '')
1589
+ # List concatenation
1590
+ if isinstance(left, list) and isinstance(right, list):
1591
+ result = type(left)(left._element_type) if hasattr(left, '_element_type') else []
1592
+ if hasattr(result, 'extend'):
1593
+ result.extend(left)
1594
+ result.extend(right)
1595
+ else:
1596
+ result = list(left) + list(right)
1597
+ return result
1598
+ # Numeric addition
1599
+ return (left or 0) + (right or 0)
1365
1600
 
1366
- if op in operators:
1367
- return operators[op](left, right)
1601
+ if op == '-':
1602
+ return self._to_number(left) - self._to_number(right)
1603
+
1604
+ if op == '*':
1605
+ # String repeat: "abc" * 3 = "abcabcabc"
1606
+ if isinstance(left, str) and isinstance(right, (int, float)):
1607
+ return left * int(right)
1608
+ if isinstance(right, str) and isinstance(left, (int, float)):
1609
+ return right * int(left)
1610
+ return self._to_number(left) * self._to_number(right)
1611
+
1612
+ if op == '/':
1613
+ r = self._to_number(right)
1614
+ return self._to_number(left) / r if r != 0 else 0
1615
+
1616
+ if op == '//':
1617
+ r = self._to_number(right)
1618
+ return self._to_number(left) // r if r != 0 else 0
1619
+
1620
+ if op == '%':
1621
+ r = self._to_number(right)
1622
+ return self._to_number(left) % r if r != 0 else 0
1623
+
1624
+ if op == '**':
1625
+ return self._to_number(left) ** self._to_number(right)
1626
+
1627
+ # === COMPARISON OPERATIONS ===
1628
+ if op == '==':
1629
+ return left == right
1630
+ if op == '!=':
1631
+ return left != right
1632
+ if op == '<':
1633
+ return self._compare(left, right) < 0
1634
+ if op == '>':
1635
+ return self._compare(left, right) > 0
1636
+ if op == '<=':
1637
+ return self._compare(left, right) <= 0
1638
+ if op == '>=':
1639
+ return self._compare(left, right) >= 0
1640
+
1641
+ # === LOGICAL OPERATIONS ===
1642
+ if op == 'and' or op == '&&':
1643
+ return left and right
1644
+ if op == 'or' or op == '||':
1645
+ return left or right
1646
+
1647
+ # === BITWISE OPERATIONS ===
1648
+ if op == '&':
1649
+ return int(left or 0) & int(right or 0)
1650
+ if op == '|':
1651
+ return int(left or 0) | int(right or 0)
1652
+ if op == '^':
1653
+ return int(left or 0) ^ int(right or 0)
1654
+ if op == '<<':
1655
+ return int(left or 0) << int(right or 0)
1656
+ if op == '>>':
1657
+ return int(left or 0) >> int(right or 0)
1658
+
1659
+ # === IN OPERATOR ===
1660
+ if op == 'in':
1661
+ if right is None:
1662
+ return False
1663
+ return left in right
1368
1664
 
1369
1665
  return None
1370
1666
 
1667
+ def _to_number(self, value: Any) -> Union[int, float]:
1668
+ """Convert value to number with auto-casting"""
1669
+ if value is None:
1670
+ return 0
1671
+ if isinstance(value, (int, float)):
1672
+ return value
1673
+ if isinstance(value, str):
1674
+ value = value.strip()
1675
+ if not value:
1676
+ return 0
1677
+ try:
1678
+ if '.' in value:
1679
+ return float(value)
1680
+ return int(value)
1681
+ except ValueError:
1682
+ return 0
1683
+ if isinstance(value, bool):
1684
+ return 1 if value else 0
1685
+ if isinstance(value, (list, tuple)):
1686
+ return len(value)
1687
+ return 0
1688
+
1689
+ def _compare(self, left: Any, right: Any) -> int:
1690
+ """Compare two values with auto-casting, returns -1, 0, or 1"""
1691
+ # Handle None
1692
+ if left is None and right is None:
1693
+ return 0
1694
+ if left is None:
1695
+ return -1
1696
+ if right is None:
1697
+ return 1
1698
+
1699
+ # Both strings - compare as strings
1700
+ if isinstance(left, str) and isinstance(right, str):
1701
+ if left < right:
1702
+ return -1
1703
+ elif left > right:
1704
+ return 1
1705
+ return 0
1706
+
1707
+ # Both numbers - compare as numbers
1708
+ if isinstance(left, (int, float)) and isinstance(right, (int, float)):
1709
+ if left < right:
1710
+ return -1
1711
+ elif left > right:
1712
+ return 1
1713
+ return 0
1714
+
1715
+ # Mixed types - try to convert to numbers
1716
+ try:
1717
+ l = self._to_number(left)
1718
+ r = self._to_number(right)
1719
+ if l < r:
1720
+ return -1
1721
+ elif l > r:
1722
+ return 1
1723
+ return 0
1724
+ except:
1725
+ # Fallback to string comparison
1726
+ l_str = str(left)
1727
+ r_str = str(right)
1728
+ if l_str < r_str:
1729
+ return -1
1730
+ elif l_str > r_str:
1731
+ return 1
1732
+ return 0
1733
+
1371
1734
  def _eval_unary(self, node: ASTNode) -> Any:
1372
1735
  """Evaluate unary operation"""
1373
1736
  op = node.value.get('op')
@@ -1386,7 +1749,7 @@ class CSSLRuntime:
1386
1749
  callee = self._evaluate(callee_node)
1387
1750
  args = [self._evaluate(a) for a in node.value.get('args', [])]
1388
1751
 
1389
- # NEW: Get function name for injection check
1752
+ # Get function name for injection check
1390
1753
  func_name = None
1391
1754
  if isinstance(callee_node, ASTNode):
1392
1755
  if callee_node.type == 'identifier':
@@ -1394,17 +1757,81 @@ class CSSLRuntime:
1394
1757
  elif callee_node.type == 'member_access':
1395
1758
  func_name = callee_node.value.get('member')
1396
1759
 
1397
- # NEW: Execute any permanently injected code first
1398
- if func_name and func_name in self._function_injections:
1760
+ # Check if function has injections
1761
+ has_injections = func_name and func_name in self._function_injections
1762
+ is_replaced = func_name and self._function_replaced.get(func_name, False)
1763
+
1764
+ # Execute injected code first (if any)
1765
+ if has_injections:
1399
1766
  self._execute_function_injections(func_name)
1400
1767
 
1768
+ # If function is REPLACED (<<==), skip original body execution
1769
+ if is_replaced:
1770
+ return None # Injection already ran, don't run original
1771
+
1772
+ # Execute original function
1401
1773
  if callable(callee):
1402
1774
  return callee(*args)
1403
1775
 
1404
1776
  if isinstance(callee, ASTNode) and callee.type == 'function':
1405
1777
  return self._call_function(callee, args)
1406
1778
 
1407
- raise CSSLRuntimeError(f"Cannot call non-function: {type(callee)}", node.line)
1779
+ callee_name = callee_node.value if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value') else str(callee_node)
1780
+ raise CSSLRuntimeError(
1781
+ f"Cannot call '{callee_name}' - it is not a function",
1782
+ node.line,
1783
+ context=f"Type: {type(callee).__name__}",
1784
+ hint=ERROR_HINTS['undefined_function']
1785
+ )
1786
+
1787
+ def _eval_typed_call(self, node: ASTNode) -> Any:
1788
+ """Evaluate typed function call like OpenFind<string>(0)"""
1789
+ name = node.value.get('name')
1790
+ type_param = node.value.get('type_param', 'dynamic')
1791
+ args = [self._evaluate(a) for a in node.value.get('args', [])]
1792
+
1793
+ # Handle OpenFind<type>(index)
1794
+ if name == 'OpenFind':
1795
+ # OpenFind searches for a value of the specified type
1796
+ # from the open parameters in scope
1797
+ open_params = self.scope.get('Params') or []
1798
+ index = args[0] if args else 0
1799
+
1800
+ # Search for value of matching type at or near the index
1801
+ type_map = {
1802
+ 'string': str, 'str': str,
1803
+ 'int': int, 'integer': int,
1804
+ 'float': float, 'double': float,
1805
+ 'bool': bool, 'boolean': bool,
1806
+ 'list': list, 'array': list,
1807
+ 'dict': dict, 'json': dict,
1808
+ }
1809
+
1810
+ target_type = type_map.get(type_param.lower())
1811
+
1812
+ if isinstance(open_params, (list, tuple)):
1813
+ # Find first matching type starting from index
1814
+ for i in range(index, len(open_params)):
1815
+ if target_type is None or isinstance(open_params[i], target_type):
1816
+ return open_params[i]
1817
+ # Also search before index
1818
+ for i in range(0, min(index, len(open_params))):
1819
+ if target_type is None or isinstance(open_params[i], target_type):
1820
+ return open_params[i]
1821
+
1822
+ return None
1823
+
1824
+ # Fallback: call as regular function with type hint
1825
+ func = self.builtins.get_function(name)
1826
+ if func and callable(func):
1827
+ return func(type_param, *args)
1828
+
1829
+ raise CSSLRuntimeError(
1830
+ f"Unknown typed function: {name}<{type_param}>",
1831
+ node.line,
1832
+ context=f"Available typed functions: OpenFind<type>",
1833
+ hint="Typed functions use format: FunctionName<Type>(args)"
1834
+ )
1408
1835
 
1409
1836
  def _eval_member_access(self, node: ASTNode) -> Any:
1410
1837
  """Evaluate member access"""
@@ -1419,6 +1846,18 @@ class CSSLRuntime:
1419
1846
  if isinstance(obj, Parameter) and member == 'return':
1420
1847
  member = 'return_'
1421
1848
 
1849
+ # === STRING METHODS ===
1850
+ if isinstance(obj, str):
1851
+ string_methods = self._get_string_method(obj, member)
1852
+ if string_methods is not None:
1853
+ return string_methods
1854
+
1855
+ # === LIST/ARRAY METHODS for plain lists ===
1856
+ if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array)):
1857
+ list_methods = self._get_list_method(obj, member)
1858
+ if list_methods is not None:
1859
+ return list_methods
1860
+
1422
1861
  if hasattr(obj, member):
1423
1862
  return getattr(obj, member)
1424
1863
 
@@ -1427,6 +1866,198 @@ class CSSLRuntime:
1427
1866
 
1428
1867
  return None
1429
1868
 
1869
+ def _get_string_method(self, s: str, method: str) -> Any:
1870
+ """Get string method implementation for CSSL.
1871
+
1872
+ Provides C++/Java/JS style string methods that Python doesn't have.
1873
+ """
1874
+ # === C++/Java/JS STRING METHODS ===
1875
+ if method == 'contains':
1876
+ return lambda substr: substr in s
1877
+ elif method == 'indexOf':
1878
+ return lambda substr, start=0: s.find(substr, start)
1879
+ elif method == 'lastIndexOf':
1880
+ return lambda substr: s.rfind(substr)
1881
+ elif method == 'charAt':
1882
+ return lambda index: s[index] if 0 <= index < len(s) else ''
1883
+ elif method == 'charCodeAt':
1884
+ return lambda index: ord(s[index]) if 0 <= index < len(s) else -1
1885
+ elif method == 'substring':
1886
+ return lambda start, end=None: s[start:end] if end else s[start:]
1887
+ elif method == 'substr':
1888
+ return lambda start, length=None: s[start:start+length] if length else s[start:]
1889
+ elif method == 'slice':
1890
+ return lambda start, end=None: s[start:end] if end else s[start:]
1891
+
1892
+ # === TRIM METHODS ===
1893
+ elif method == 'trim':
1894
+ return lambda: s.strip()
1895
+ elif method == 'trimStart' or method == 'trimLeft' or method == 'ltrim':
1896
+ return lambda: s.lstrip()
1897
+ elif method == 'trimEnd' or method == 'trimRight' or method == 'rtrim':
1898
+ return lambda: s.rstrip()
1899
+
1900
+ # === CASE METHODS ===
1901
+ elif method in ('toUpperCase', 'toUpper', 'upper'):
1902
+ return lambda: s.upper()
1903
+ elif method in ('toLowerCase', 'toLower', 'lower'):
1904
+ return lambda: s.lower()
1905
+ elif method == 'capitalize':
1906
+ return lambda: s.capitalize()
1907
+ elif method == 'title':
1908
+ return lambda: s.title()
1909
+ elif method == 'swapcase':
1910
+ return lambda: s.swapcase()
1911
+
1912
+ # === REPLACE METHODS ===
1913
+ elif method == 'replaceAll':
1914
+ return lambda old, new: s.replace(old, new)
1915
+ elif method == 'replaceFirst':
1916
+ return lambda old, new: s.replace(old, new, 1)
1917
+
1918
+ # === CHECK METHODS ===
1919
+ elif method == 'isEmpty':
1920
+ return lambda: len(s) == 0
1921
+ elif method == 'isBlank':
1922
+ return lambda: len(s.strip()) == 0
1923
+ elif method == 'isDigit' or method == 'isNumeric':
1924
+ return lambda: s.isdigit()
1925
+ elif method == 'isAlpha':
1926
+ return lambda: s.isalpha()
1927
+ elif method == 'isAlphaNumeric' or method == 'isAlnum':
1928
+ return lambda: s.isalnum()
1929
+ elif method == 'isSpace' or method == 'isWhitespace':
1930
+ return lambda: s.isspace()
1931
+ elif method == 'isUpper':
1932
+ return lambda: s.isupper()
1933
+ elif method == 'isLower':
1934
+ return lambda: s.islower()
1935
+
1936
+ # === STARTS/ENDS WITH ===
1937
+ elif method == 'startsWith' or method == 'startswith':
1938
+ return lambda prefix: s.startswith(prefix)
1939
+ elif method == 'endsWith' or method == 'endswith':
1940
+ return lambda suffix: s.endswith(suffix)
1941
+
1942
+ # === LENGTH/SIZE ===
1943
+ elif method == 'length' or method == 'size':
1944
+ return lambda: len(s)
1945
+
1946
+ # === SPLIT/JOIN ===
1947
+ elif method == 'toArray':
1948
+ return lambda sep=None: list(s.split(sep) if sep else list(s))
1949
+ elif method == 'lines':
1950
+ return lambda: s.splitlines()
1951
+ elif method == 'words':
1952
+ return lambda: s.split()
1953
+
1954
+ # === PADDING ===
1955
+ elif method == 'padStart' or method == 'padLeft' or method == 'lpad':
1956
+ return lambda width, char=' ': s.rjust(width, char[0] if char else ' ')
1957
+ elif method == 'padEnd' or method == 'padRight' or method == 'rpad':
1958
+ return lambda width, char=' ': s.ljust(width, char[0] if char else ' ')
1959
+ elif method == 'center':
1960
+ return lambda width, char=' ': s.center(width, char[0] if char else ' ')
1961
+ elif method == 'zfill':
1962
+ return lambda width: s.zfill(width)
1963
+
1964
+ # === REPEAT ===
1965
+ elif method == 'repeat':
1966
+ return lambda n: s * n
1967
+
1968
+ # === REVERSE ===
1969
+ elif method == 'reverse':
1970
+ return lambda: s[::-1]
1971
+
1972
+ # === FORMAT ===
1973
+ elif method == 'format':
1974
+ return lambda *args, **kwargs: s.format(*args, **kwargs)
1975
+
1976
+ # === ENCODING ===
1977
+ elif method == 'encode':
1978
+ return lambda encoding='utf-8': s.encode(encoding)
1979
+ elif method == 'bytes':
1980
+ return lambda encoding='utf-8': list(s.encode(encoding))
1981
+
1982
+ # === NUMERIC CONVERSION ===
1983
+ elif method == 'toInt' or method == 'toInteger':
1984
+ return lambda base=10: int(s, base) if s.lstrip('-').isdigit() else 0
1985
+ elif method == 'toFloat' or method == 'toDouble':
1986
+ try:
1987
+ return lambda: float(s)
1988
+ except:
1989
+ return lambda: 0.0
1990
+ elif method == 'toBool':
1991
+ return lambda: s.lower() in ('true', '1', 'yes', 'on')
1992
+
1993
+ # === C++ ITERATOR STYLE ===
1994
+ elif method == 'begin':
1995
+ return lambda: 0
1996
+ elif method == 'end':
1997
+ return lambda: len(s)
1998
+
1999
+ # Return None if not a string method
2000
+ return None
2001
+
2002
+ def _get_list_method(self, lst: list, method: str) -> Any:
2003
+ """Get list method implementation for plain Python lists in CSSL."""
2004
+ if method == 'contains':
2005
+ return lambda item: item in lst
2006
+ elif method == 'indexOf':
2007
+ def index_of(item):
2008
+ try:
2009
+ return lst.index(item)
2010
+ except ValueError:
2011
+ return -1
2012
+ return index_of
2013
+ elif method == 'lastIndexOf':
2014
+ def last_index_of(item):
2015
+ for i in range(len(lst) - 1, -1, -1):
2016
+ if lst[i] == item:
2017
+ return i
2018
+ return -1
2019
+ return last_index_of
2020
+ elif method == 'length' or method == 'size':
2021
+ return lambda: len(lst)
2022
+ elif method == 'isEmpty':
2023
+ return lambda: len(lst) == 0
2024
+ elif method == 'first':
2025
+ return lambda: lst[0] if lst else None
2026
+ elif method == 'last':
2027
+ return lambda: lst[-1] if lst else None
2028
+ elif method == 'at':
2029
+ return lambda i: lst[i] if 0 <= i < len(lst) else None
2030
+ elif method == 'slice':
2031
+ return lambda start, end=None: lst[start:end] if end else lst[start:]
2032
+ elif method == 'join':
2033
+ return lambda sep=',': sep.join(str(x) for x in lst)
2034
+ elif method == 'find':
2035
+ def find_item(val):
2036
+ for item in lst:
2037
+ if item == val:
2038
+ return item
2039
+ return None
2040
+ return find_item
2041
+ elif method == 'push':
2042
+ def push_item(item):
2043
+ lst.append(item)
2044
+ return lst
2045
+ return push_item
2046
+ elif method == 'push_back':
2047
+ def push_back_item(item):
2048
+ lst.append(item)
2049
+ return lst
2050
+ return push_back_item
2051
+ elif method == 'toArray':
2052
+ return lambda: list(lst)
2053
+ # === C++ ITERATOR STYLE ===
2054
+ elif method == 'begin':
2055
+ return lambda: 0
2056
+ elif method == 'end':
2057
+ return lambda: len(lst)
2058
+
2059
+ return None
2060
+
1430
2061
  def _eval_index_access(self, node: ASTNode) -> Any:
1431
2062
  """Evaluate index access"""
1432
2063
  obj = self._evaluate(node.value.get('object'))
@@ -1448,10 +2079,30 @@ class CSSLRuntime:
1448
2079
  if obj is None:
1449
2080
  return
1450
2081
 
2082
+ # Check for SharedObjectProxy - directly access underlying object
2083
+ # This is more robust than relying on the proxy's __setattr__
2084
+ if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
2085
+ # This is a SharedObjectProxy - get the real object directly
2086
+ real_obj = object.__getattribute__(obj, '_direct_object')
2087
+ if real_obj is None:
2088
+ # Fallback to _live_objects registry
2089
+ name = object.__getattribute__(obj, '_name')
2090
+ from ..cssl_bridge import _live_objects
2091
+ real_obj = _live_objects.get(name)
2092
+ if real_obj is not None:
2093
+ setattr(real_obj, member, value)
2094
+ return
2095
+
1451
2096
  if hasattr(obj, member):
1452
2097
  setattr(obj, member, value)
1453
2098
  elif isinstance(obj, dict):
1454
2099
  obj[member] = value
2100
+ else:
2101
+ # Try setattr anyway for objects that support dynamic attributes
2102
+ try:
2103
+ setattr(obj, member, value)
2104
+ except (AttributeError, TypeError):
2105
+ pass
1455
2106
 
1456
2107
  def _set_index(self, node: ASTNode, value: Any):
1457
2108
  """Set index value"""