IncludeCPP 3.4.10__py3-none-any.whl → 3.5.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.
@@ -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,10 +140,13 @@ 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
112
147
  self._output_callback = output_callback # Callback for console output (text, level)
148
+ self._source_lines: List[str] = [] # Store source code lines for error reporting
149
+ self._current_file: str = "<code>" # Current file being executed
113
150
 
114
151
  self._setup_modules()
115
152
  self._setup_builtins()
@@ -195,23 +232,68 @@ class CSSLRuntime:
195
232
 
196
233
  return obj
197
234
 
235
+ def _format_error(self, line: int, message: str, hint: str = None) -> CSSLRuntimeError:
236
+ """Format a detailed error with source context"""
237
+ error_parts = []
238
+
239
+ # Main error header
240
+ if line and line > 0:
241
+ error_parts.append(f"Error at line {line} in {self._current_file}:")
242
+ else:
243
+ error_parts.append(f"Error in {self._current_file}:")
244
+
245
+ # Extract message without existing line info
246
+ clean_msg = message
247
+ if "at line" in clean_msg.lower():
248
+ # Remove redundant line info from message
249
+ clean_msg = clean_msg.split(":", 1)[-1].strip() if ":" in clean_msg else clean_msg
250
+
251
+ error_parts.append(f" {clean_msg}")
252
+
253
+ # Show source context (3 lines before and after)
254
+ if self._source_lines and line and line > 0:
255
+ error_parts.append("")
256
+ start = max(0, line - 3)
257
+ end = min(len(self._source_lines), line + 2)
258
+
259
+ for i in range(start, end):
260
+ line_num = i + 1
261
+ source_line = self._source_lines[i] if i < len(self._source_lines) else ""
262
+ marker = ">>>" if line_num == line else " "
263
+ error_parts.append(f" {marker} {line_num:4d} | {source_line}")
264
+
265
+ # Add hint
266
+ if hint:
267
+ error_parts.append("")
268
+ error_parts.append(f" Hint: {hint}")
269
+
270
+ return CSSLRuntimeError("\n".join(error_parts), line)
271
+
272
+ def _get_source_line(self, line: int) -> str:
273
+ """Get source line by number (1-indexed)"""
274
+ if self._source_lines and 0 < line <= len(self._source_lines):
275
+ return self._source_lines[line - 1]
276
+ return ""
277
+
198
278
  def execute(self, source: str) -> Any:
199
279
  """Execute CSSL service source code"""
280
+ self._source_lines = source.splitlines()
200
281
  try:
201
282
  ast = parse_cssl(source)
202
283
  return self._execute_node(ast)
203
284
  except CSSLSyntaxError as e:
204
- raise CSSLRuntimeError(str(e), e.line)
285
+ raise self._format_error(e.line, str(e))
205
286
  except SyntaxError as e:
206
287
  raise CSSLRuntimeError(f"Syntax error: {e}")
207
288
 
208
289
  def execute_program(self, source: str) -> Any:
209
290
  """Execute standalone CSSL program (no service wrapper)"""
291
+ self._source_lines = source.splitlines()
210
292
  try:
211
293
  ast = parse_cssl_program(source)
212
294
  return self._exec_program(ast)
213
295
  except CSSLSyntaxError as e:
214
- raise CSSLRuntimeError(str(e), e.line)
296
+ raise self._format_error(e.line, str(e))
215
297
  except SyntaxError as e:
216
298
  raise CSSLRuntimeError(f"Syntax error: {e}")
217
299
 
@@ -221,12 +303,16 @@ class CSSLRuntime:
221
303
 
222
304
  def execute_file(self, filepath: str) -> Any:
223
305
  """Execute a CSSL service file"""
306
+ import os
307
+ self._current_file = os.path.basename(filepath)
224
308
  with open(filepath, 'r', encoding='utf-8') as f:
225
309
  source = f.read()
226
310
  return self.execute(source)
227
311
 
228
312
  def execute_program_file(self, filepath: str) -> Any:
229
313
  """Execute a standalone CSSL program file"""
314
+ import os
315
+ self._current_file = os.path.basename(filepath)
230
316
  with open(filepath, 'r', encoding='utf-8') as f:
231
317
  source = f.read()
232
318
  return self.execute_program(source)
@@ -301,7 +387,7 @@ class CSSLRuntime:
301
387
  # Handle typed variable declaration: type<T> varName = value;
302
388
  result = self._exec_typed_declaration(child)
303
389
  elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
304
- 'if', 'while', 'for', 'foreach', 'switch', 'try'):
390
+ 'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
305
391
  result = self._execute_node(child)
306
392
  elif child.type == 'call':
307
393
  result = self._eval_call(child)
@@ -614,7 +700,7 @@ class CSSLRuntime:
614
700
  elif type_name == 'json':
615
701
  instance = {} if value_node is None else self._evaluate(value_node)
616
702
  elif type_name == 'array':
617
- instance = [] if value_node is None else self._evaluate(value_node)
703
+ instance = Array(element_type)
618
704
  else:
619
705
  # Default: evaluate the value or set to None
620
706
  instance = self._evaluate(value_node) if value_node else None
@@ -649,8 +735,14 @@ class CSSLRuntime:
649
735
  value = self._evaluate(inner.value.get('value'))
650
736
 
651
737
  # Get variable name from target
652
- if isinstance(target, ASTNode) and target.type == 'identifier':
653
- var_name = target.value
738
+ if isinstance(target, ASTNode):
739
+ if target.type == 'identifier':
740
+ var_name = target.value
741
+ elif target.type == 'global_ref':
742
+ # r@Name = value
743
+ var_name = target.value
744
+ else:
745
+ var_name = str(target.value) if hasattr(target, 'value') else str(target)
654
746
  elif isinstance(target, str):
655
747
  var_name = target
656
748
  else:
@@ -744,12 +836,16 @@ class CSSLRuntime:
744
836
  return None
745
837
 
746
838
  def _exec_for(self, node: ASTNode) -> Any:
747
- """Execute for loop"""
839
+ """Execute Python-style for loop: for (i in range(start, end, step)) { }"""
748
840
  var_name = node.value.get('var')
749
841
  start = int(self._evaluate(node.value.get('start')))
750
842
  end = int(self._evaluate(node.value.get('end')))
751
843
 
752
- for i in range(start, end):
844
+ # Optional step parameter (default is 1)
845
+ step_node = node.value.get('step')
846
+ step = int(self._evaluate(step_node)) if step_node else 1
847
+
848
+ for i in range(start, end, step):
753
849
  self.scope.set(var_name, i)
754
850
  try:
755
851
  for child in node.children:
@@ -761,6 +857,73 @@ class CSSLRuntime:
761
857
 
762
858
  return None
763
859
 
860
+ def _exec_c_for(self, node: ASTNode) -> Any:
861
+ """Execute C-style for loop: for (init; condition; update) { }
862
+
863
+ Supports:
864
+ - for (int i = 0; i < n; i++) { }
865
+ - for (int i = 0; i < n; i = i + 1) { }
866
+ - for (i = 0; i < n; i += 1) { }
867
+ - for (; condition; ) { } (infinite loop with condition)
868
+ """
869
+ init = node.value.get('init')
870
+ condition = node.value.get('condition')
871
+ update = node.value.get('update')
872
+
873
+ # Execute init statement
874
+ if init:
875
+ var_name = init.value.get('var')
876
+ init_value = self._evaluate(init.value.get('value'))
877
+ self.scope.set(var_name, init_value)
878
+ else:
879
+ var_name = None
880
+
881
+ # Main loop
882
+ while True:
883
+ # Check condition
884
+ if condition:
885
+ cond_result = self._evaluate(condition)
886
+ if not cond_result:
887
+ break
888
+ # If no condition, this would be infinite - we still need a way to break
889
+
890
+ # Execute body
891
+ try:
892
+ for child in node.children:
893
+ self._execute_node(child)
894
+ except CSSLBreak:
895
+ break
896
+ except CSSLContinue:
897
+ pass # Continue to update, then next iteration
898
+
899
+ # Execute update
900
+ if update:
901
+ self._exec_c_for_update(update)
902
+
903
+ return None
904
+
905
+ def _exec_c_for_update(self, update: 'ASTNode') -> None:
906
+ """Execute the update part of a C-style for loop."""
907
+ var_name = update.value.get('var')
908
+ op = update.value.get('op')
909
+ value_node = update.value.get('value')
910
+
911
+ current = self.scope.get(var_name) or 0
912
+
913
+ if op == 'increment':
914
+ self.scope.set(var_name, current + 1)
915
+ elif op == 'decrement':
916
+ self.scope.set(var_name, current - 1)
917
+ elif op == 'add':
918
+ add_value = self._evaluate(value_node)
919
+ self.scope.set(var_name, current + add_value)
920
+ elif op == 'subtract':
921
+ sub_value = self._evaluate(value_node)
922
+ self.scope.set(var_name, current - sub_value)
923
+ elif op == 'assign':
924
+ new_value = self._evaluate(value_node)
925
+ self.scope.set(var_name, new_value)
926
+
764
927
  def _exec_foreach(self, node: ASTNode) -> Any:
765
928
  """Execute foreach loop"""
766
929
  var_name = node.value.get('var')
@@ -885,7 +1048,7 @@ class CSSLRuntime:
885
1048
  def _apply_injection_filter(self, source: Any, filter_info: dict) -> Any:
886
1049
  """Apply injection filter to extract specific data from source.
887
1050
 
888
- Filters:
1051
+ All BruteInjector Helpers:
889
1052
  - string::where=VALUE - Filter strings containing VALUE
890
1053
  - string::length=LENGTH - Filter strings of specific length
891
1054
  - integer::where=VALUE - Filter integers matching VALUE
@@ -894,9 +1057,12 @@ class CSSLRuntime:
894
1057
  - array::index=INDEX - Get specific index from array
895
1058
  - array::length=LENGTH - Filter arrays of specific length
896
1059
  - vector::where=VALUE - Filter vectors containing VALUE
1060
+ - vector::index=INDEX - Get specific index from vector
1061
+ - vector::length=LENGTH - Filter vectors of specific length
897
1062
  - combo::filterdb - Get filter database from combo
898
1063
  - combo::blocked - Get blocked items from combo
899
- - dynamic::VarName=VALUE - Filter by dynamic variable
1064
+ - dynamic::VarName=VALUE - Filter by dynamic variable value
1065
+ - sql::data - Return only SQL-compatible data
900
1066
  """
901
1067
  if not filter_info:
902
1068
  return source
@@ -908,25 +1074,51 @@ class CSSLRuntime:
908
1074
  filter_type, helper = filter_key.split('::', 1)
909
1075
  filter_val = self._evaluate(filter_value) if isinstance(filter_value, ASTNode) else filter_value
910
1076
 
1077
+ # === STRING HELPERS ===
911
1078
  if filter_type == 'string':
912
1079
  if helper == 'where':
913
- if isinstance(result, str) and filter_val in result:
914
- pass # Keep result
1080
+ # Exact match
1081
+ if isinstance(result, str):
1082
+ result = result if result == filter_val else None
1083
+ elif isinstance(result, list):
1084
+ result = [item for item in result if isinstance(item, str) and item == filter_val]
1085
+ elif helper == 'contains':
1086
+ # Contains substring
1087
+ if isinstance(result, str):
1088
+ result = result if filter_val in result else None
915
1089
  elif isinstance(result, list):
916
1090
  result = [item for item in result if isinstance(item, str) and filter_val in item]
917
- elif helper == 'length':
1091
+ elif helper == 'not':
1092
+ # Exclude matching
1093
+ if isinstance(result, str):
1094
+ result = result if result != filter_val else None
1095
+ elif isinstance(result, list):
1096
+ result = [item for item in result if not (isinstance(item, str) and item == filter_val)]
1097
+ elif helper == 'startsWith':
1098
+ if isinstance(result, str):
1099
+ result = result if result.startswith(filter_val) else None
1100
+ elif isinstance(result, list):
1101
+ result = [item for item in result if isinstance(item, str) and item.startswith(filter_val)]
1102
+ elif helper == 'endsWith':
1103
+ if isinstance(result, str):
1104
+ result = result if result.endswith(filter_val) else None
1105
+ elif isinstance(result, list):
1106
+ result = [item for item in result if isinstance(item, str) and item.endswith(filter_val)]
1107
+ elif helper in ('length', 'lenght'): # Support common typo
918
1108
  if isinstance(result, str):
919
1109
  result = result if len(result) == filter_val else None
920
1110
  elif isinstance(result, list):
921
1111
  result = [item for item in result if isinstance(item, str) and len(item) == filter_val]
922
1112
 
1113
+ # === INTEGER HELPERS ===
923
1114
  elif filter_type == 'integer':
924
1115
  if helper == 'where':
925
- if isinstance(result, int) and result == filter_val:
926
- pass # Keep result
1116
+ if isinstance(result, int):
1117
+ result = result if result == filter_val else None
927
1118
  elif isinstance(result, list):
928
1119
  result = [item for item in result if isinstance(item, int) and item == filter_val]
929
1120
 
1121
+ # === JSON HELPERS ===
930
1122
  elif filter_type == 'json':
931
1123
  if helper == 'key':
932
1124
  if isinstance(result, dict):
@@ -935,29 +1127,68 @@ class CSSLRuntime:
935
1127
  result = [item.get(filter_val) for item in result if isinstance(item, dict) and filter_val in item]
936
1128
  elif helper == 'value':
937
1129
  if isinstance(result, dict):
938
- result = {k: v for k, v in result.items() if v == filter_val}
1130
+ # Find key(s) with matching value
1131
+ matches = [k for k, v in result.items() if v == filter_val]
1132
+ result = matches[0] if len(matches) == 1 else matches
939
1133
  elif isinstance(result, list):
940
1134
  result = [item for item in result if (isinstance(item, dict) and filter_val in item.values())]
941
1135
 
942
- elif filter_type == 'array' or filter_type == 'vector':
1136
+ # === ARRAY HELPERS ===
1137
+ elif filter_type == 'array':
943
1138
  if helper == 'index':
944
- if isinstance(result, list) and 0 <= filter_val < len(result):
945
- result = result[filter_val]
946
- elif helper == 'length':
1139
+ if isinstance(result, (list, tuple)):
1140
+ idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
1141
+ if 0 <= idx < len(result):
1142
+ result = result[idx]
1143
+ else:
1144
+ result = None
1145
+ elif helper in ('length', 'lenght'): # Support common typo
1146
+ if isinstance(result, (list, tuple)):
1147
+ result = result if len(result) == filter_val else []
1148
+ elif helper == 'where':
947
1149
  if isinstance(result, list):
1150
+ result = [item for item in result if item == filter_val]
1151
+
1152
+ # === VECTOR HELPERS ===
1153
+ elif filter_type == 'vector':
1154
+ if helper == 'index':
1155
+ if isinstance(result, (list, tuple)):
1156
+ idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
1157
+ if 0 <= idx < len(result):
1158
+ result = result[idx]
1159
+ else:
1160
+ result = None
1161
+ elif helper in ('length', 'lenght'): # Support common typo
1162
+ if isinstance(result, (list, tuple)):
948
1163
  result = result if len(result) == filter_val else []
949
1164
  elif helper == 'where':
950
1165
  if isinstance(result, list):
951
1166
  result = [item for item in result if item == filter_val]
952
1167
 
1168
+ # === COMBO HELPERS ===
953
1169
  elif filter_type == 'combo':
954
1170
  if helper == 'filterdb':
955
1171
  if hasattr(result, '_filterdb'):
956
1172
  result = result._filterdb
1173
+ elif hasattr(result, 'filterdb'):
1174
+ result = result.filterdb
957
1175
  elif helper == 'blocked':
958
1176
  if hasattr(result, '_blocked'):
959
1177
  result = result._blocked
1178
+ elif hasattr(result, 'blocked'):
1179
+ result = result.blocked
1180
+
1181
+ # === DYNAMIC HELPERS ===
1182
+ elif filter_type == 'dynamic':
1183
+ # dynamic::VarName=VALUE - Match if variable equals value
1184
+ var_name = helper
1185
+ var_value = self.scope.get(var_name)
1186
+ if var_value == filter_val:
1187
+ pass # Keep result
1188
+ else:
1189
+ result = None
960
1190
 
1191
+ # === SQL HELPERS ===
961
1192
  elif filter_type == 'sql':
962
1193
  if helper == 'data':
963
1194
  # Return only SQL-compatible data types
@@ -1037,6 +1268,12 @@ class CSSLRuntime:
1037
1268
  self.scope.set(target.value, final_value)
1038
1269
  elif target.type == 'module_ref':
1039
1270
  self._set_module_value(target.value, final_value)
1271
+ elif target.type == 'shared_ref':
1272
+ # $Name <== value - 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))
1040
1277
  elif target.type == 'member_access':
1041
1278
  self._set_member(target, final_value)
1042
1279
  elif target.type == 'call':
@@ -1105,6 +1342,12 @@ class CSSLRuntime:
1105
1342
  self.scope.set(target.value, final_value)
1106
1343
  elif target.type == 'module_ref':
1107
1344
  self._set_module_value(target.value, final_value)
1345
+ elif target.type == 'shared_ref':
1346
+ # value ==> $Name - create/update shared object
1347
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1348
+ name = target.value
1349
+ _live_objects[name] = final_value
1350
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
1108
1351
  elif target.type == 'member_access':
1109
1352
  self._set_member(target, final_value)
1110
1353
 
@@ -1114,13 +1357,13 @@ class CSSLRuntime:
1114
1357
  """Execute code infusion (<<==, +<<==, -<<==)
1115
1358
 
1116
1359
  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)
1360
+ - replace: func <<== { code } - REPLACES function body (original won't execute)
1361
+ - add: func +<<== { code } - ADDS code to function (both execute)
1362
+ - remove: func -<<== { code } - REMOVES matching code from function
1120
1363
  """
1121
1364
  target = node.value.get('target')
1122
1365
  code_block = node.value.get('code')
1123
- mode = node.value.get('mode', 'add')
1366
+ mode = node.value.get('mode', 'replace') # Default is REPLACE for <<==
1124
1367
 
1125
1368
  # Get function name from target
1126
1369
  func_name = None
@@ -1136,17 +1379,21 @@ class CSSLRuntime:
1136
1379
  return None
1137
1380
 
1138
1381
  if mode == 'add':
1139
- # Add code to function (permanent injection)
1382
+ # +<<== : Add code to function (both injection + original execute)
1140
1383
  self.register_function_injection(func_name, code_block)
1384
+ self._function_replaced[func_name] = False # Don't replace, just add
1141
1385
  elif mode == 'replace':
1142
- # Replace - clear existing and add new
1386
+ # <<== : Replace function body (only injection executes, original skipped)
1143
1387
  self._function_injections[func_name] = [code_block]
1388
+ self._function_replaced[func_name] = True # Mark as replaced
1144
1389
  elif mode == 'remove':
1145
- # Remove matching code patterns from function
1390
+ # -<<== : Remove matching code from function body
1391
+ # For now, this removes all injections for the function
1146
1392
  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
1393
  self._function_injections[func_name] = []
1394
+ self._function_replaced[func_name] = False
1395
+ # Note: Removing from actual function body would require AST manipulation
1396
+ # which is complex - for now we just clear injections
1150
1397
 
1151
1398
  return None
1152
1399
 
@@ -1189,6 +1436,14 @@ class CSSLRuntime:
1189
1436
  self.scope.set(target.value, source)
1190
1437
  elif target.type == 'module_ref':
1191
1438
  self._set_module_value(target.value, source)
1439
+ elif target.type == 'shared_ref':
1440
+ # $Name <== value - create/update shared object
1441
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1442
+ name = target.value
1443
+ _live_objects[name] = source
1444
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, source))
1445
+ elif target.type == 'member_access':
1446
+ self._set_member(target, source)
1192
1447
 
1193
1448
  return source
1194
1449
 
@@ -1204,6 +1459,16 @@ class CSSLRuntime:
1204
1459
  # r@Name = value - store in promoted globals
1205
1460
  self._promoted_globals[target.value] = value
1206
1461
  self.global_scope.set(target.value, value)
1462
+ elif target.type == 'shared_ref':
1463
+ # $Name = value - create/update shared object
1464
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1465
+ name = target.value
1466
+ _live_objects[name] = value
1467
+ self.global_scope.set(f'${name}', SharedObjectProxy(name, value))
1468
+ elif target.type == 'module_ref':
1469
+ # @Name = value - store in promoted globals (like global keyword)
1470
+ self._promoted_globals[target.value] = value
1471
+ self.global_scope.set(target.value, value)
1207
1472
  elif target.type == 'member_access':
1208
1473
  self._set_member(target, value)
1209
1474
  elif target.type == 'index_access':
@@ -1307,6 +1572,45 @@ class CSSLRuntime:
1307
1572
  value = self.global_scope.get(node.value)
1308
1573
  return value
1309
1574
 
1575
+ if node.type == 'shared_ref':
1576
+ # $<name> shared object reference
1577
+ # Returns the SharedObjectProxy for live access
1578
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1579
+ name = node.value
1580
+ if name in _live_objects:
1581
+ return SharedObjectProxy(name, _live_objects[name])
1582
+ # Check if stored in runtime's scope as $name
1583
+ scoped_val = self.global_scope.get(f'${name}')
1584
+ if scoped_val is not None:
1585
+ return scoped_val
1586
+ raise CSSLRuntimeError(f"Shared object '${name}' not found. Use share() to share objects.")
1587
+
1588
+ if node.type == 'type_instantiation':
1589
+ # Create new instance of a type: stack<string>, vector<int>, etc.
1590
+ type_name = node.value.get('type')
1591
+ element_type = node.value.get('element_type', 'dynamic')
1592
+
1593
+ if type_name == 'stack':
1594
+ return Stack(element_type)
1595
+ elif type_name == 'vector':
1596
+ return Vector(element_type)
1597
+ elif type_name == 'datastruct':
1598
+ return DataStruct(element_type)
1599
+ elif type_name == 'shuffled':
1600
+ return Shuffled(element_type)
1601
+ elif type_name == 'iterator':
1602
+ return Iterator(element_type)
1603
+ elif type_name == 'combo':
1604
+ return Combo(element_type)
1605
+ elif type_name == 'dataspace':
1606
+ return DataSpace(element_type)
1607
+ elif type_name == 'openquote':
1608
+ return OpenQuote()
1609
+ elif type_name == 'array':
1610
+ return Array(element_type)
1611
+ else:
1612
+ return None
1613
+
1310
1614
  if node.type == 'binary':
1311
1615
  return self._eval_binary(node)
1312
1616
 
@@ -1316,6 +1620,10 @@ class CSSLRuntime:
1316
1620
  if node.type == 'call':
1317
1621
  return self._eval_call(node)
1318
1622
 
1623
+ if node.type == 'typed_call':
1624
+ # Handle OpenFind<type>(args) style calls
1625
+ return self._eval_typed_call(node)
1626
+
1319
1627
  if node.type == 'member_access':
1320
1628
  return self._eval_member_access(node)
1321
1629
 
@@ -1342,32 +1650,161 @@ class CSSLRuntime:
1342
1650
  return None
1343
1651
 
1344
1652
  def _eval_binary(self, node: ASTNode) -> Any:
1345
- """Evaluate binary operation"""
1653
+ """Evaluate binary operation with auto-casting support"""
1346
1654
  op = node.value.get('op')
1347
1655
  left = self._evaluate(node.value.get('left'))
1348
1656
  right = self._evaluate(node.value.get('right'))
1349
1657
 
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
- }
1658
+ # === AUTO-CAST FOR STRING OPERATIONS ===
1659
+ if op == '+':
1660
+ # String concatenation with auto-cast
1661
+ if isinstance(left, str) or isinstance(right, str):
1662
+ return str(left if left is not None else '') + str(right if right is not None else '')
1663
+ # List concatenation
1664
+ if isinstance(left, list) and isinstance(right, list):
1665
+ result = type(left)(left._element_type) if hasattr(left, '_element_type') else []
1666
+ if hasattr(result, 'extend'):
1667
+ result.extend(left)
1668
+ result.extend(right)
1669
+ else:
1670
+ result = list(left) + list(right)
1671
+ return result
1672
+ # Numeric addition
1673
+ return (left or 0) + (right or 0)
1365
1674
 
1366
- if op in operators:
1367
- return operators[op](left, right)
1675
+ if op == '-':
1676
+ return self._to_number(left) - self._to_number(right)
1677
+
1678
+ if op == '*':
1679
+ # String repeat: "abc" * 3 = "abcabcabc"
1680
+ if isinstance(left, str) and isinstance(right, (int, float)):
1681
+ return left * int(right)
1682
+ if isinstance(right, str) and isinstance(left, (int, float)):
1683
+ return right * int(left)
1684
+ return self._to_number(left) * self._to_number(right)
1685
+
1686
+ if op == '/':
1687
+ r = self._to_number(right)
1688
+ return self._to_number(left) / r if r != 0 else 0
1689
+
1690
+ if op == '//':
1691
+ r = self._to_number(right)
1692
+ return self._to_number(left) // r if r != 0 else 0
1693
+
1694
+ if op == '%':
1695
+ r = self._to_number(right)
1696
+ return self._to_number(left) % r if r != 0 else 0
1697
+
1698
+ if op == '**':
1699
+ return self._to_number(left) ** self._to_number(right)
1700
+
1701
+ # === COMPARISON OPERATIONS ===
1702
+ if op == '==':
1703
+ return left == right
1704
+ if op == '!=':
1705
+ return left != right
1706
+ if op == '<':
1707
+ return self._compare(left, right) < 0
1708
+ if op == '>':
1709
+ return self._compare(left, right) > 0
1710
+ if op == '<=':
1711
+ return self._compare(left, right) <= 0
1712
+ if op == '>=':
1713
+ return self._compare(left, right) >= 0
1714
+
1715
+ # === LOGICAL OPERATIONS ===
1716
+ if op == 'and' or op == '&&':
1717
+ return left and right
1718
+ if op == 'or' or op == '||':
1719
+ return left or right
1720
+
1721
+ # === BITWISE OPERATIONS ===
1722
+ if op == '&':
1723
+ return int(left or 0) & int(right or 0)
1724
+ if op == '|':
1725
+ return int(left or 0) | int(right or 0)
1726
+ if op == '^':
1727
+ return int(left or 0) ^ int(right or 0)
1728
+ if op == '<<':
1729
+ return int(left or 0) << int(right or 0)
1730
+ if op == '>>':
1731
+ return int(left or 0) >> int(right or 0)
1732
+
1733
+ # === IN OPERATOR ===
1734
+ if op == 'in':
1735
+ if right is None:
1736
+ return False
1737
+ return left in right
1368
1738
 
1369
1739
  return None
1370
1740
 
1741
+ def _to_number(self, value: Any) -> Union[int, float]:
1742
+ """Convert value to number with auto-casting"""
1743
+ if value is None:
1744
+ return 0
1745
+ if isinstance(value, (int, float)):
1746
+ return value
1747
+ if isinstance(value, str):
1748
+ value = value.strip()
1749
+ if not value:
1750
+ return 0
1751
+ try:
1752
+ if '.' in value:
1753
+ return float(value)
1754
+ return int(value)
1755
+ except ValueError:
1756
+ return 0
1757
+ if isinstance(value, bool):
1758
+ return 1 if value else 0
1759
+ if isinstance(value, (list, tuple)):
1760
+ return len(value)
1761
+ return 0
1762
+
1763
+ def _compare(self, left: Any, right: Any) -> int:
1764
+ """Compare two values with auto-casting, returns -1, 0, or 1"""
1765
+ # Handle None
1766
+ if left is None and right is None:
1767
+ return 0
1768
+ if left is None:
1769
+ return -1
1770
+ if right is None:
1771
+ return 1
1772
+
1773
+ # Both strings - compare as strings
1774
+ if isinstance(left, str) and isinstance(right, str):
1775
+ if left < right:
1776
+ return -1
1777
+ elif left > right:
1778
+ return 1
1779
+ return 0
1780
+
1781
+ # Both numbers - compare as numbers
1782
+ if isinstance(left, (int, float)) and isinstance(right, (int, float)):
1783
+ if left < right:
1784
+ return -1
1785
+ elif left > right:
1786
+ return 1
1787
+ return 0
1788
+
1789
+ # Mixed types - try to convert to numbers
1790
+ try:
1791
+ l = self._to_number(left)
1792
+ r = self._to_number(right)
1793
+ if l < r:
1794
+ return -1
1795
+ elif l > r:
1796
+ return 1
1797
+ return 0
1798
+ except:
1799
+ # Fallback to string comparison
1800
+ l_str = str(left)
1801
+ r_str = str(right)
1802
+ if l_str < r_str:
1803
+ return -1
1804
+ elif l_str > r_str:
1805
+ return 1
1806
+ return 0
1807
+
1371
1808
  def _eval_unary(self, node: ASTNode) -> Any:
1372
1809
  """Evaluate unary operation"""
1373
1810
  op = node.value.get('op')
@@ -1386,7 +1823,7 @@ class CSSLRuntime:
1386
1823
  callee = self._evaluate(callee_node)
1387
1824
  args = [self._evaluate(a) for a in node.value.get('args', [])]
1388
1825
 
1389
- # NEW: Get function name for injection check
1826
+ # Get function name for injection check
1390
1827
  func_name = None
1391
1828
  if isinstance(callee_node, ASTNode):
1392
1829
  if callee_node.type == 'identifier':
@@ -1394,17 +1831,81 @@ class CSSLRuntime:
1394
1831
  elif callee_node.type == 'member_access':
1395
1832
  func_name = callee_node.value.get('member')
1396
1833
 
1397
- # NEW: Execute any permanently injected code first
1398
- if func_name and func_name in self._function_injections:
1834
+ # Check if function has injections
1835
+ has_injections = func_name and func_name in self._function_injections
1836
+ is_replaced = func_name and self._function_replaced.get(func_name, False)
1837
+
1838
+ # Execute injected code first (if any)
1839
+ if has_injections:
1399
1840
  self._execute_function_injections(func_name)
1400
1841
 
1842
+ # If function is REPLACED (<<==), skip original body execution
1843
+ if is_replaced:
1844
+ return None # Injection already ran, don't run original
1845
+
1846
+ # Execute original function
1401
1847
  if callable(callee):
1402
1848
  return callee(*args)
1403
1849
 
1404
1850
  if isinstance(callee, ASTNode) and callee.type == 'function':
1405
1851
  return self._call_function(callee, args)
1406
1852
 
1407
- raise CSSLRuntimeError(f"Cannot call non-function: {type(callee)}", node.line)
1853
+ callee_name = callee_node.value if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value') else str(callee_node)
1854
+ raise CSSLRuntimeError(
1855
+ f"Cannot call '{callee_name}' - it is not a function",
1856
+ node.line,
1857
+ context=f"Type: {type(callee).__name__}",
1858
+ hint=ERROR_HINTS['undefined_function']
1859
+ )
1860
+
1861
+ def _eval_typed_call(self, node: ASTNode) -> Any:
1862
+ """Evaluate typed function call like OpenFind<string>(0)"""
1863
+ name = node.value.get('name')
1864
+ type_param = node.value.get('type_param', 'dynamic')
1865
+ args = [self._evaluate(a) for a in node.value.get('args', [])]
1866
+
1867
+ # Handle OpenFind<type>(index)
1868
+ if name == 'OpenFind':
1869
+ # OpenFind searches for a value of the specified type
1870
+ # from the open parameters in scope
1871
+ open_params = self.scope.get('Params') or []
1872
+ index = args[0] if args else 0
1873
+
1874
+ # Search for value of matching type at or near the index
1875
+ type_map = {
1876
+ 'string': str, 'str': str,
1877
+ 'int': int, 'integer': int,
1878
+ 'float': float, 'double': float,
1879
+ 'bool': bool, 'boolean': bool,
1880
+ 'list': list, 'array': list,
1881
+ 'dict': dict, 'json': dict,
1882
+ }
1883
+
1884
+ target_type = type_map.get(type_param.lower())
1885
+
1886
+ if isinstance(open_params, (list, tuple)):
1887
+ # Find first matching type starting from index
1888
+ for i in range(index, len(open_params)):
1889
+ if target_type is None or isinstance(open_params[i], target_type):
1890
+ return open_params[i]
1891
+ # Also search before index
1892
+ for i in range(0, min(index, len(open_params))):
1893
+ if target_type is None or isinstance(open_params[i], target_type):
1894
+ return open_params[i]
1895
+
1896
+ return None
1897
+
1898
+ # Fallback: call as regular function with type hint
1899
+ func = self.builtins.get_function(name)
1900
+ if func and callable(func):
1901
+ return func(type_param, *args)
1902
+
1903
+ raise CSSLRuntimeError(
1904
+ f"Unknown typed function: {name}<{type_param}>",
1905
+ node.line,
1906
+ context=f"Available typed functions: OpenFind<type>",
1907
+ hint="Typed functions use format: FunctionName<Type>(args)"
1908
+ )
1408
1909
 
1409
1910
  def _eval_member_access(self, node: ASTNode) -> Any:
1410
1911
  """Evaluate member access"""
@@ -1419,6 +1920,18 @@ class CSSLRuntime:
1419
1920
  if isinstance(obj, Parameter) and member == 'return':
1420
1921
  member = 'return_'
1421
1922
 
1923
+ # === STRING METHODS ===
1924
+ if isinstance(obj, str):
1925
+ string_methods = self._get_string_method(obj, member)
1926
+ if string_methods is not None:
1927
+ return string_methods
1928
+
1929
+ # === LIST/ARRAY METHODS for plain lists ===
1930
+ if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array)):
1931
+ list_methods = self._get_list_method(obj, member)
1932
+ if list_methods is not None:
1933
+ return list_methods
1934
+
1422
1935
  if hasattr(obj, member):
1423
1936
  return getattr(obj, member)
1424
1937
 
@@ -1427,6 +1940,198 @@ class CSSLRuntime:
1427
1940
 
1428
1941
  return None
1429
1942
 
1943
+ def _get_string_method(self, s: str, method: str) -> Any:
1944
+ """Get string method implementation for CSSL.
1945
+
1946
+ Provides C++/Java/JS style string methods that Python doesn't have.
1947
+ """
1948
+ # === C++/Java/JS STRING METHODS ===
1949
+ if method == 'contains':
1950
+ return lambda substr: substr in s
1951
+ elif method == 'indexOf':
1952
+ return lambda substr, start=0: s.find(substr, start)
1953
+ elif method == 'lastIndexOf':
1954
+ return lambda substr: s.rfind(substr)
1955
+ elif method == 'charAt':
1956
+ return lambda index: s[index] if 0 <= index < len(s) else ''
1957
+ elif method == 'charCodeAt':
1958
+ return lambda index: ord(s[index]) if 0 <= index < len(s) else -1
1959
+ elif method == 'substring':
1960
+ return lambda start, end=None: s[start:end] if end else s[start:]
1961
+ elif method == 'substr':
1962
+ return lambda start, length=None: s[start:start+length] if length else s[start:]
1963
+ elif method == 'slice':
1964
+ return lambda start, end=None: s[start:end] if end else s[start:]
1965
+
1966
+ # === TRIM METHODS ===
1967
+ elif method == 'trim':
1968
+ return lambda: s.strip()
1969
+ elif method == 'trimStart' or method == 'trimLeft' or method == 'ltrim':
1970
+ return lambda: s.lstrip()
1971
+ elif method == 'trimEnd' or method == 'trimRight' or method == 'rtrim':
1972
+ return lambda: s.rstrip()
1973
+
1974
+ # === CASE METHODS ===
1975
+ elif method in ('toUpperCase', 'toUpper', 'upper'):
1976
+ return lambda: s.upper()
1977
+ elif method in ('toLowerCase', 'toLower', 'lower'):
1978
+ return lambda: s.lower()
1979
+ elif method == 'capitalize':
1980
+ return lambda: s.capitalize()
1981
+ elif method == 'title':
1982
+ return lambda: s.title()
1983
+ elif method == 'swapcase':
1984
+ return lambda: s.swapcase()
1985
+
1986
+ # === REPLACE METHODS ===
1987
+ elif method == 'replaceAll':
1988
+ return lambda old, new: s.replace(old, new)
1989
+ elif method == 'replaceFirst':
1990
+ return lambda old, new: s.replace(old, new, 1)
1991
+
1992
+ # === CHECK METHODS ===
1993
+ elif method == 'isEmpty':
1994
+ return lambda: len(s) == 0
1995
+ elif method == 'isBlank':
1996
+ return lambda: len(s.strip()) == 0
1997
+ elif method == 'isDigit' or method == 'isNumeric':
1998
+ return lambda: s.isdigit()
1999
+ elif method == 'isAlpha':
2000
+ return lambda: s.isalpha()
2001
+ elif method == 'isAlphaNumeric' or method == 'isAlnum':
2002
+ return lambda: s.isalnum()
2003
+ elif method == 'isSpace' or method == 'isWhitespace':
2004
+ return lambda: s.isspace()
2005
+ elif method == 'isUpper':
2006
+ return lambda: s.isupper()
2007
+ elif method == 'isLower':
2008
+ return lambda: s.islower()
2009
+
2010
+ # === STARTS/ENDS WITH ===
2011
+ elif method == 'startsWith' or method == 'startswith':
2012
+ return lambda prefix: s.startswith(prefix)
2013
+ elif method == 'endsWith' or method == 'endswith':
2014
+ return lambda suffix: s.endswith(suffix)
2015
+
2016
+ # === LENGTH/SIZE ===
2017
+ elif method == 'length' or method == 'size':
2018
+ return lambda: len(s)
2019
+
2020
+ # === SPLIT/JOIN ===
2021
+ elif method == 'toArray':
2022
+ return lambda sep=None: list(s.split(sep) if sep else list(s))
2023
+ elif method == 'lines':
2024
+ return lambda: s.splitlines()
2025
+ elif method == 'words':
2026
+ return lambda: s.split()
2027
+
2028
+ # === PADDING ===
2029
+ elif method == 'padStart' or method == 'padLeft' or method == 'lpad':
2030
+ return lambda width, char=' ': s.rjust(width, char[0] if char else ' ')
2031
+ elif method == 'padEnd' or method == 'padRight' or method == 'rpad':
2032
+ return lambda width, char=' ': s.ljust(width, char[0] if char else ' ')
2033
+ elif method == 'center':
2034
+ return lambda width, char=' ': s.center(width, char[0] if char else ' ')
2035
+ elif method == 'zfill':
2036
+ return lambda width: s.zfill(width)
2037
+
2038
+ # === REPEAT ===
2039
+ elif method == 'repeat':
2040
+ return lambda n: s * n
2041
+
2042
+ # === REVERSE ===
2043
+ elif method == 'reverse':
2044
+ return lambda: s[::-1]
2045
+
2046
+ # === FORMAT ===
2047
+ elif method == 'format':
2048
+ return lambda *args, **kwargs: s.format(*args, **kwargs)
2049
+
2050
+ # === ENCODING ===
2051
+ elif method == 'encode':
2052
+ return lambda encoding='utf-8': s.encode(encoding)
2053
+ elif method == 'bytes':
2054
+ return lambda encoding='utf-8': list(s.encode(encoding))
2055
+
2056
+ # === NUMERIC CONVERSION ===
2057
+ elif method == 'toInt' or method == 'toInteger':
2058
+ return lambda base=10: int(s, base) if s.lstrip('-').isdigit() else 0
2059
+ elif method == 'toFloat' or method == 'toDouble':
2060
+ try:
2061
+ return lambda: float(s)
2062
+ except:
2063
+ return lambda: 0.0
2064
+ elif method == 'toBool':
2065
+ return lambda: s.lower() in ('true', '1', 'yes', 'on')
2066
+
2067
+ # === C++ ITERATOR STYLE ===
2068
+ elif method == 'begin':
2069
+ return lambda: 0
2070
+ elif method == 'end':
2071
+ return lambda: len(s)
2072
+
2073
+ # Return None if not a string method
2074
+ return None
2075
+
2076
+ def _get_list_method(self, lst: list, method: str) -> Any:
2077
+ """Get list method implementation for plain Python lists in CSSL."""
2078
+ if method == 'contains':
2079
+ return lambda item: item in lst
2080
+ elif method == 'indexOf':
2081
+ def index_of(item):
2082
+ try:
2083
+ return lst.index(item)
2084
+ except ValueError:
2085
+ return -1
2086
+ return index_of
2087
+ elif method == 'lastIndexOf':
2088
+ def last_index_of(item):
2089
+ for i in range(len(lst) - 1, -1, -1):
2090
+ if lst[i] == item:
2091
+ return i
2092
+ return -1
2093
+ return last_index_of
2094
+ elif method == 'length' or method == 'size':
2095
+ return lambda: len(lst)
2096
+ elif method == 'isEmpty':
2097
+ return lambda: len(lst) == 0
2098
+ elif method == 'first':
2099
+ return lambda: lst[0] if lst else None
2100
+ elif method == 'last':
2101
+ return lambda: lst[-1] if lst else None
2102
+ elif method == 'at':
2103
+ return lambda i: lst[i] if 0 <= i < len(lst) else None
2104
+ elif method == 'slice':
2105
+ return lambda start, end=None: lst[start:end] if end else lst[start:]
2106
+ elif method == 'join':
2107
+ return lambda sep=',': sep.join(str(x) for x in lst)
2108
+ elif method == 'find':
2109
+ def find_item(val):
2110
+ for item in lst:
2111
+ if item == val:
2112
+ return item
2113
+ return None
2114
+ return find_item
2115
+ elif method == 'push':
2116
+ def push_item(item):
2117
+ lst.append(item)
2118
+ return lst
2119
+ return push_item
2120
+ elif method == 'push_back':
2121
+ def push_back_item(item):
2122
+ lst.append(item)
2123
+ return lst
2124
+ return push_back_item
2125
+ elif method == 'toArray':
2126
+ return lambda: list(lst)
2127
+ # === C++ ITERATOR STYLE ===
2128
+ elif method == 'begin':
2129
+ return lambda: 0
2130
+ elif method == 'end':
2131
+ return lambda: len(lst)
2132
+
2133
+ return None
2134
+
1430
2135
  def _eval_index_access(self, node: ASTNode) -> Any:
1431
2136
  """Evaluate index access"""
1432
2137
  obj = self._evaluate(node.value.get('object'))
@@ -1448,10 +2153,30 @@ class CSSLRuntime:
1448
2153
  if obj is None:
1449
2154
  return
1450
2155
 
2156
+ # Check for SharedObjectProxy - directly access underlying object
2157
+ # This is more robust than relying on the proxy's __setattr__
2158
+ if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
2159
+ # This is a SharedObjectProxy - get the real object directly
2160
+ real_obj = object.__getattribute__(obj, '_direct_object')
2161
+ if real_obj is None:
2162
+ # Fallback to _live_objects registry
2163
+ name = object.__getattribute__(obj, '_name')
2164
+ from ..cssl_bridge import _live_objects
2165
+ real_obj = _live_objects.get(name)
2166
+ if real_obj is not None:
2167
+ setattr(real_obj, member, value)
2168
+ return
2169
+
1451
2170
  if hasattr(obj, member):
1452
2171
  setattr(obj, member, value)
1453
2172
  elif isinstance(obj, dict):
1454
2173
  obj[member] = value
2174
+ else:
2175
+ # Try setattr anyway for objects that support dynamic attributes
2176
+ try:
2177
+ setattr(obj, member, value)
2178
+ except (AttributeError, TypeError):
2179
+ pass
1455
2180
 
1456
2181
  def _set_index(self, node: ASTNode, value: Any):
1457
2182
  """Set index value"""