IncludeCPP 3.4.8__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.
@@ -12,14 +12,51 @@ from .cssl_parser import ASTNode, parse_cssl, parse_cssl_program, CSSLSyntaxErro
12
12
  from .cssl_events import CSSLEventManager, EventType, EventData, get_event_manager
13
13
  from .cssl_builtins import CSSLBuiltins
14
14
  from .cssl_modules import get_module_registry, get_standard_module
15
- from .cssl_types import Parameter, DataStruct, Shuffled, Iterator, Combo
15
+ from .cssl_types import (
16
+ Parameter, DataStruct, Shuffled, Iterator, Combo,
17
+ Stack, Vector, Array, DataSpace, OpenQuote
18
+ )
16
19
 
17
20
 
18
21
  class CSSLRuntimeError(Exception):
19
- """Runtime error during CSSL execution"""
20
- 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):
21
24
  self.line = line
22
- 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
+ }
23
60
 
24
61
 
25
62
  class CSSLBreak(Exception):
@@ -103,6 +140,7 @@ class CSSLRuntime:
103
140
  self._modules: Dict[str, Any] = {}
104
141
  self._global_structs: Dict[str, Any] = {} # Global structs for s@<name> references
105
142
  self._function_injections: Dict[str, List[ASTNode]] = {} # NEW: Permanent function injections
143
+ self._function_replaced: Dict[str, bool] = {} # NEW: Track replaced functions (<<==)
106
144
  self._promoted_globals: Dict[str, Any] = {} # NEW: Variables promoted via global()
107
145
  self._running = False
108
146
  self._exit_code = 0
@@ -291,8 +329,14 @@ class CSSLRuntime:
291
329
  self._exec_struct(child)
292
330
  elif child.type == 'function':
293
331
  self._exec_function(child)
332
+ elif child.type == 'global_assignment':
333
+ # Handle global variable declaration: global Name = value
334
+ result = self._exec_global_assignment(child)
335
+ elif child.type == 'typed_declaration':
336
+ # Handle typed variable declaration: type<T> varName = value;
337
+ result = self._exec_typed_declaration(child)
294
338
  elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
295
- 'if', 'while', 'for', 'foreach', 'switch', 'try'):
339
+ 'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
296
340
  result = self._execute_node(child)
297
341
  elif child.type == 'call':
298
342
  result = self._eval_call(child)
@@ -564,6 +608,109 @@ class CSSLRuntime:
564
608
  self.scope.set(func_name, node)
565
609
  return None
566
610
 
611
+ def _exec_typed_declaration(self, node: ASTNode) -> Any:
612
+ """Execute typed variable declaration: type<T> varName = value;
613
+
614
+ Creates appropriate type instances for stack, vector, datastruct, etc.
615
+ """
616
+ decl = node.value
617
+ type_name = decl.get('type')
618
+ element_type = decl.get('element_type', 'dynamic')
619
+ var_name = decl.get('name')
620
+ value_node = decl.get('value')
621
+
622
+ # Create the appropriate type instance
623
+ if type_name == 'stack':
624
+ instance = Stack(element_type)
625
+ elif type_name == 'vector':
626
+ instance = Vector(element_type)
627
+ elif type_name == 'datastruct':
628
+ instance = DataStruct(element_type)
629
+ elif type_name == 'shuffled':
630
+ instance = Shuffled(element_type)
631
+ elif type_name == 'iterator':
632
+ instance = Iterator(element_type)
633
+ elif type_name == 'combo':
634
+ instance = Combo(element_type)
635
+ elif type_name == 'dataspace':
636
+ instance = DataSpace(element_type)
637
+ elif type_name == 'openquote':
638
+ instance = OpenQuote()
639
+ elif type_name in ('int', 'integer'):
640
+ instance = 0 if value_node is None else self._evaluate(value_node)
641
+ elif type_name in ('string', 'str'):
642
+ instance = "" if value_node is None else self._evaluate(value_node)
643
+ elif type_name in ('float', 'double'):
644
+ instance = 0.0 if value_node is None else self._evaluate(value_node)
645
+ elif type_name == 'bool':
646
+ instance = False if value_node is None else self._evaluate(value_node)
647
+ elif type_name == 'dynamic':
648
+ instance = None if value_node is None else self._evaluate(value_node)
649
+ elif type_name == 'json':
650
+ instance = {} if value_node is None else self._evaluate(value_node)
651
+ elif type_name == 'array':
652
+ instance = Array(element_type)
653
+ else:
654
+ # Default: evaluate the value or set to None
655
+ instance = self._evaluate(value_node) if value_node else None
656
+
657
+ # If there's an explicit value, use it instead
658
+ if value_node and type_name not in ('int', 'integer', 'string', 'str', 'float', 'double', 'bool', 'dynamic', 'json', 'array'):
659
+ # For container types, the value might be initialization data
660
+ init_value = self._evaluate(value_node)
661
+ if isinstance(init_value, (list, tuple)):
662
+ instance.extend(init_value)
663
+ elif init_value is not None:
664
+ if hasattr(instance, 'append'):
665
+ instance.append(init_value)
666
+
667
+ # Store in scope
668
+ self.scope.set(var_name, instance)
669
+ return instance
670
+
671
+ def _exec_global_assignment(self, node: ASTNode) -> Any:
672
+ """Execute global variable assignment: global Name = value
673
+
674
+ Stores the value in _promoted_globals so it can be accessed via @Name
675
+ """
676
+ inner = node.value # The wrapped assignment/expression node
677
+
678
+ if inner is None:
679
+ return None
680
+
681
+ # Handle assignment node: global Name = value
682
+ if inner.type == 'assignment':
683
+ target = inner.value.get('target')
684
+ value = self._evaluate(inner.value.get('value'))
685
+
686
+ # Get variable name from target
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)
695
+ elif isinstance(target, str):
696
+ var_name = target
697
+ else:
698
+ var_name = str(target)
699
+
700
+ # Store in promoted globals for @Name access
701
+ self._promoted_globals[var_name] = value
702
+ # Also store in global scope for regular access
703
+ self.global_scope.set(var_name, value)
704
+ return value
705
+
706
+ # Handle expression that results in assignment
707
+ elif inner.type == 'expression':
708
+ result = self._evaluate(inner.value)
709
+ return result
710
+
711
+ # Fallback: execute normally
712
+ return self._execute_node(inner)
713
+
567
714
  def _call_function(self, func_node: ASTNode, args: List[Any]) -> Any:
568
715
  """Call a function node with arguments"""
569
716
  func_info = func_node.value
@@ -638,12 +785,16 @@ class CSSLRuntime:
638
785
  return None
639
786
 
640
787
  def _exec_for(self, node: ASTNode) -> Any:
641
- """Execute for loop"""
788
+ """Execute Python-style for loop: for (i in range(start, end, step)) { }"""
642
789
  var_name = node.value.get('var')
643
790
  start = int(self._evaluate(node.value.get('start')))
644
791
  end = int(self._evaluate(node.value.get('end')))
645
792
 
646
- 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):
647
798
  self.scope.set(var_name, i)
648
799
  try:
649
800
  for child in node.children:
@@ -655,6 +806,73 @@ class CSSLRuntime:
655
806
 
656
807
  return None
657
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
+
658
876
  def _exec_foreach(self, node: ASTNode) -> Any:
659
877
  """Execute foreach loop"""
660
878
  var_name = node.value.get('var')
@@ -779,7 +997,7 @@ class CSSLRuntime:
779
997
  def _apply_injection_filter(self, source: Any, filter_info: dict) -> Any:
780
998
  """Apply injection filter to extract specific data from source.
781
999
 
782
- Filters:
1000
+ All BruteInjector Helpers:
783
1001
  - string::where=VALUE - Filter strings containing VALUE
784
1002
  - string::length=LENGTH - Filter strings of specific length
785
1003
  - integer::where=VALUE - Filter integers matching VALUE
@@ -788,9 +1006,12 @@ class CSSLRuntime:
788
1006
  - array::index=INDEX - Get specific index from array
789
1007
  - array::length=LENGTH - Filter arrays of specific length
790
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
791
1011
  - combo::filterdb - Get filter database from combo
792
1012
  - combo::blocked - Get blocked items from combo
793
- - dynamic::VarName=VALUE - Filter by dynamic variable
1013
+ - dynamic::VarName=VALUE - Filter by dynamic variable value
1014
+ - sql::data - Return only SQL-compatible data
794
1015
  """
795
1016
  if not filter_info:
796
1017
  return source
@@ -802,25 +1023,28 @@ class CSSLRuntime:
802
1023
  filter_type, helper = filter_key.split('::', 1)
803
1024
  filter_val = self._evaluate(filter_value) if isinstance(filter_value, ASTNode) else filter_value
804
1025
 
1026
+ # === STRING HELPERS ===
805
1027
  if filter_type == 'string':
806
1028
  if helper == 'where':
807
- if isinstance(result, str) and filter_val in result:
808
- pass # Keep result
1029
+ if isinstance(result, str):
1030
+ result = result if filter_val in result else None
809
1031
  elif isinstance(result, list):
810
1032
  result = [item for item in result if isinstance(item, str) and filter_val in item]
811
- elif helper == 'length':
1033
+ elif helper in ('length', 'lenght'): # Support common typo
812
1034
  if isinstance(result, str):
813
1035
  result = result if len(result) == filter_val else None
814
1036
  elif isinstance(result, list):
815
1037
  result = [item for item in result if isinstance(item, str) and len(item) == filter_val]
816
1038
 
1039
+ # === INTEGER HELPERS ===
817
1040
  elif filter_type == 'integer':
818
1041
  if helper == 'where':
819
- if isinstance(result, int) and result == filter_val:
820
- pass # Keep result
1042
+ if isinstance(result, int):
1043
+ result = result if result == filter_val else None
821
1044
  elif isinstance(result, list):
822
1045
  result = [item for item in result if isinstance(item, int) and item == filter_val]
823
1046
 
1047
+ # === JSON HELPERS ===
824
1048
  elif filter_type == 'json':
825
1049
  if helper == 'key':
826
1050
  if isinstance(result, dict):
@@ -829,29 +1053,68 @@ class CSSLRuntime:
829
1053
  result = [item.get(filter_val) for item in result if isinstance(item, dict) and filter_val in item]
830
1054
  elif helper == 'value':
831
1055
  if isinstance(result, dict):
832
- 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
833
1059
  elif isinstance(result, list):
834
1060
  result = [item for item in result if (isinstance(item, dict) and filter_val in item.values())]
835
1061
 
836
- elif filter_type == 'array' or filter_type == 'vector':
1062
+ # === ARRAY HELPERS ===
1063
+ elif filter_type == 'array':
837
1064
  if helper == 'index':
838
- if isinstance(result, list) and 0 <= filter_val < len(result):
839
- result = result[filter_val]
840
- 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':
841
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)):
842
1089
  result = result if len(result) == filter_val else []
843
1090
  elif helper == 'where':
844
1091
  if isinstance(result, list):
845
1092
  result = [item for item in result if item == filter_val]
846
1093
 
1094
+ # === COMBO HELPERS ===
847
1095
  elif filter_type == 'combo':
848
1096
  if helper == 'filterdb':
849
1097
  if hasattr(result, '_filterdb'):
850
1098
  result = result._filterdb
1099
+ elif hasattr(result, 'filterdb'):
1100
+ result = result.filterdb
851
1101
  elif helper == 'blocked':
852
1102
  if hasattr(result, '_blocked'):
853
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
854
1116
 
1117
+ # === SQL HELPERS ===
855
1118
  elif filter_type == 'sql':
856
1119
  if helper == 'data':
857
1120
  # Return only SQL-compatible data types
@@ -931,6 +1194,12 @@ class CSSLRuntime:
931
1194
  self.scope.set(target.value, final_value)
932
1195
  elif target.type == 'module_ref':
933
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))
934
1203
  elif target.type == 'member_access':
935
1204
  self._set_member(target, final_value)
936
1205
  elif target.type == 'call':
@@ -999,6 +1268,12 @@ class CSSLRuntime:
999
1268
  self.scope.set(target.value, final_value)
1000
1269
  elif target.type == 'module_ref':
1001
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))
1002
1277
  elif target.type == 'member_access':
1003
1278
  self._set_member(target, final_value)
1004
1279
 
@@ -1008,13 +1283,13 @@ class CSSLRuntime:
1008
1283
  """Execute code infusion (<<==, +<<==, -<<==)
1009
1284
 
1010
1285
  Modes:
1011
- - replace: func <<== { code } (inject code into function, replaces)
1012
- - add: func +<<== { code } (add code to function)
1013
- - 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
1014
1289
  """
1015
1290
  target = node.value.get('target')
1016
1291
  code_block = node.value.get('code')
1017
- mode = node.value.get('mode', 'add')
1292
+ mode = node.value.get('mode', 'replace') # Default is REPLACE for <<==
1018
1293
 
1019
1294
  # Get function name from target
1020
1295
  func_name = None
@@ -1030,17 +1305,21 @@ class CSSLRuntime:
1030
1305
  return None
1031
1306
 
1032
1307
  if mode == 'add':
1033
- # Add code to function (permanent injection)
1308
+ # +<<== : Add code to function (both injection + original execute)
1034
1309
  self.register_function_injection(func_name, code_block)
1310
+ self._function_replaced[func_name] = False # Don't replace, just add
1035
1311
  elif mode == 'replace':
1036
- # Replace - clear existing and add new
1312
+ # <<== : Replace function body (only injection executes, original skipped)
1037
1313
  self._function_injections[func_name] = [code_block]
1314
+ self._function_replaced[func_name] = True # Mark as replaced
1038
1315
  elif mode == 'remove':
1039
- # Remove matching code patterns from function
1316
+ # -<<== : Remove matching code from function body
1317
+ # For now, this removes all injections for the function
1040
1318
  if func_name in self._function_injections:
1041
- # For simplicity, clear all injections for this function
1042
- # A more sophisticated implementation would compare code blocks
1043
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
1044
1323
 
1045
1324
  return None
1046
1325
 
@@ -1083,6 +1362,14 @@ class CSSLRuntime:
1083
1362
  self.scope.set(target.value, source)
1084
1363
  elif target.type == 'module_ref':
1085
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)
1086
1373
 
1087
1374
  return source
1088
1375
 
@@ -1094,6 +1381,20 @@ class CSSLRuntime:
1094
1381
  if isinstance(target, ASTNode):
1095
1382
  if target.type == 'identifier':
1096
1383
  self.scope.set(target.value, value)
1384
+ elif target.type == 'global_ref':
1385
+ # r@Name = value - store in promoted globals
1386
+ self._promoted_globals[target.value] = value
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)
1097
1398
  elif target.type == 'member_access':
1098
1399
  self._set_member(target, value)
1099
1400
  elif target.type == 'index_access':
@@ -1177,12 +1478,65 @@ class CSSLRuntime:
1177
1478
  return value
1178
1479
 
1179
1480
  if node.type == 'module_ref':
1180
- return self.get_module(node.value)
1481
+ # Check modules first, then promoted globals, then scope
1482
+ value = self.get_module(node.value)
1483
+ if value is None:
1484
+ value = self._promoted_globals.get(node.value)
1485
+ if value is None:
1486
+ value = self.global_scope.get(node.value)
1487
+ return value
1181
1488
 
1182
1489
  if node.type == 'self_ref':
1183
1490
  # s@<name> reference to global struct
1184
1491
  return self.get_global_struct(node.value)
1185
1492
 
1493
+ if node.type == 'global_ref':
1494
+ # r@<name> global variable reference
1495
+ # Check promoted globals first, then global scope
1496
+ value = self._promoted_globals.get(node.value)
1497
+ if value is None:
1498
+ value = self.global_scope.get(node.value)
1499
+ return value
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
+
1186
1540
  if node.type == 'binary':
1187
1541
  return self._eval_binary(node)
1188
1542
 
@@ -1192,6 +1546,10 @@ class CSSLRuntime:
1192
1546
  if node.type == 'call':
1193
1547
  return self._eval_call(node)
1194
1548
 
1549
+ if node.type == 'typed_call':
1550
+ # Handle OpenFind<type>(args) style calls
1551
+ return self._eval_typed_call(node)
1552
+
1195
1553
  if node.type == 'member_access':
1196
1554
  return self._eval_member_access(node)
1197
1555
 
@@ -1218,32 +1576,161 @@ class CSSLRuntime:
1218
1576
  return None
1219
1577
 
1220
1578
  def _eval_binary(self, node: ASTNode) -> Any:
1221
- """Evaluate binary operation"""
1579
+ """Evaluate binary operation with auto-casting support"""
1222
1580
  op = node.value.get('op')
1223
1581
  left = self._evaluate(node.value.get('left'))
1224
1582
  right = self._evaluate(node.value.get('right'))
1225
1583
 
1226
- operators = {
1227
- '+': lambda a, b: a + b,
1228
- '-': lambda a, b: a - b,
1229
- '*': lambda a, b: a * b,
1230
- '/': lambda a, b: a / b if b != 0 else 0,
1231
- '%': lambda a, b: a % b if b != 0 else 0,
1232
- '==': lambda a, b: a == b,
1233
- '!=': lambda a, b: a != b,
1234
- '<': lambda a, b: a < b,
1235
- '>': lambda a, b: a > b,
1236
- '<=': lambda a, b: a <= b,
1237
- '>=': lambda a, b: a >= b,
1238
- 'and': lambda a, b: a and b,
1239
- 'or': lambda a, b: a or b,
1240
- }
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)
1241
1600
 
1242
- if op in operators:
1243
- 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
1244
1664
 
1245
1665
  return None
1246
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
+
1247
1734
  def _eval_unary(self, node: ASTNode) -> Any:
1248
1735
  """Evaluate unary operation"""
1249
1736
  op = node.value.get('op')
@@ -1262,7 +1749,7 @@ class CSSLRuntime:
1262
1749
  callee = self._evaluate(callee_node)
1263
1750
  args = [self._evaluate(a) for a in node.value.get('args', [])]
1264
1751
 
1265
- # NEW: Get function name for injection check
1752
+ # Get function name for injection check
1266
1753
  func_name = None
1267
1754
  if isinstance(callee_node, ASTNode):
1268
1755
  if callee_node.type == 'identifier':
@@ -1270,17 +1757,81 @@ class CSSLRuntime:
1270
1757
  elif callee_node.type == 'member_access':
1271
1758
  func_name = callee_node.value.get('member')
1272
1759
 
1273
- # NEW: Execute any permanently injected code first
1274
- 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:
1275
1766
  self._execute_function_injections(func_name)
1276
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
1277
1773
  if callable(callee):
1278
1774
  return callee(*args)
1279
1775
 
1280
1776
  if isinstance(callee, ASTNode) and callee.type == 'function':
1281
1777
  return self._call_function(callee, args)
1282
1778
 
1283
- 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
+ )
1284
1835
 
1285
1836
  def _eval_member_access(self, node: ASTNode) -> Any:
1286
1837
  """Evaluate member access"""
@@ -1290,6 +1841,23 @@ class CSSLRuntime:
1290
1841
  if obj is None:
1291
1842
  return None
1292
1843
 
1844
+ # Special handling for Parameter.return() -> Parameter.return_()
1845
+ # since 'return' is a Python keyword
1846
+ if isinstance(obj, Parameter) and member == 'return':
1847
+ member = 'return_'
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
+
1293
1861
  if hasattr(obj, member):
1294
1862
  return getattr(obj, member)
1295
1863
 
@@ -1298,6 +1866,198 @@ class CSSLRuntime:
1298
1866
 
1299
1867
  return None
1300
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
+
1301
2061
  def _eval_index_access(self, node: ASTNode) -> Any:
1302
2062
  """Evaluate index access"""
1303
2063
  obj = self._evaluate(node.value.get('object'))
@@ -1319,10 +2079,30 @@ class CSSLRuntime:
1319
2079
  if obj is None:
1320
2080
  return
1321
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
+
1322
2096
  if hasattr(obj, member):
1323
2097
  setattr(obj, member, value)
1324
2098
  elif isinstance(obj, dict):
1325
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
1326
2106
 
1327
2107
  def _set_index(self, node: ASTNode, value: Any):
1328
2108
  """Set index value"""