IncludeCPP 4.6.0__py3-none-any.whl → 4.9.3__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.
Files changed (35) hide show
  1. includecpp/CHANGELOG.md +241 -0
  2. includecpp/__init__.py +89 -3
  3. includecpp/__init__.pyi +2 -1
  4. includecpp/cli/commands.py +1747 -266
  5. includecpp/cli/config_parser.py +1 -1
  6. includecpp/core/build_manager.py +64 -13
  7. includecpp/core/cpp_api_extensions.pyi +43 -270
  8. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1799 -1445
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/api.pyi +274 -0
  11. includecpp/core/cssl/cpp/build/cssl_core.pyi +0 -99
  12. includecpp/core/cssl/cpp/cssl_core.cp +2 -23
  13. includecpp/core/cssl/cssl_builtins.py +2116 -171
  14. includecpp/core/cssl/cssl_builtins.pyi +1324 -104
  15. includecpp/core/cssl/cssl_compiler.py +4 -1
  16. includecpp/core/cssl/cssl_modules.py +605 -6
  17. includecpp/core/cssl/cssl_optimizer.py +12 -1
  18. includecpp/core/cssl/cssl_parser.py +1048 -52
  19. includecpp/core/cssl/cssl_runtime.py +2041 -131
  20. includecpp/core/cssl/cssl_syntax.py +405 -277
  21. includecpp/core/cssl/cssl_types.py +5891 -1655
  22. includecpp/core/cssl_bridge.py +427 -4
  23. includecpp/core/error_catalog.py +54 -10
  24. includecpp/core/homeserver.py +1037 -0
  25. includecpp/generator/parser.cpp +203 -39
  26. includecpp/generator/parser.h +15 -1
  27. includecpp/templates/cpp.proj.template +1 -1
  28. includecpp/vscode/cssl/snippets/cssl.snippets.json +163 -0
  29. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +87 -12
  30. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/METADATA +81 -10
  31. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/RECORD +35 -33
  32. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/WHEEL +1 -1
  33. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/entry_points.txt +0 -0
  34. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/licenses/LICENSE +0 -0
  35. {includecpp-4.6.0.dist-info → includecpp-4.9.3.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,11 @@ from .cssl_modules import get_module_registry, get_standard_module
15
15
  from .cssl_types import (
16
16
  Parameter, DataStruct, Shuffled, Iterator, Combo,
17
17
  Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
18
- CSSLClass, CSSLInstance, ByteArrayed
18
+ Queue, # v4.7: Thread-safe queue
19
+ CSSLClass, CSSLInstance, ByteArrayed,
20
+ CSSLNamespace, # v4.8: Custom namespace support
21
+ Bit, Byte, Address, # v4.9.0: Binary types and address pointer
22
+ CSSLFuture, CSSLGenerator, CSSLAsyncFunction, AsyncModule # v4.9.3: Async support
19
23
  )
20
24
 
21
25
 
@@ -199,13 +203,78 @@ class CSSLReturn(Exception):
199
203
  super().__init__()
200
204
 
201
205
 
206
+ class CSSLYield(Exception):
207
+ """Yield statement - v4.9.3: Generator yield that pauses execution."""
208
+ def __init__(self, value: Any = None):
209
+ self.value = value
210
+ super().__init__()
211
+
212
+
202
213
  class CSSLThrow(Exception):
203
- """Throw statement - v4.5.1: User-thrown exceptions that propagate to catch blocks"""
204
- def __init__(self, message: Any = None):
214
+ """Throw statement - v4.5.1: User-thrown exceptions that propagate to catch blocks
215
+ v4.8: Extended to support Python exception types via raise statement.
216
+ """
217
+ def __init__(self, message: Any = None, underlying_exception: Exception = None):
205
218
  self.message = message
219
+ self.underlying_exception = underlying_exception
220
+ self.exception_type = type(underlying_exception).__name__ if underlying_exception else 'Error'
206
221
  super().__init__(str(message) if message else "")
207
222
 
208
223
 
224
+ class SuperProxy:
225
+ """v4.8.8: Proxy for super->method() calls in child classes.
226
+
227
+ Provides access to parent class methods from within a child class method.
228
+
229
+ Usage in CSSL:
230
+ class Parent {
231
+ define greet() { println("Hello from Parent"); }
232
+ }
233
+ class Child extends Parent {
234
+ define greet() {
235
+ super->greet(); // Calls Parent.greet()
236
+ println("Hello from Child");
237
+ }
238
+ }
239
+ """
240
+ def __init__(self, instance: 'CSSLInstance', parent_class: 'CSSLClass', runtime: 'CSSLRuntime'):
241
+ self._instance = instance
242
+ self._parent_class = parent_class
243
+ self._runtime = runtime
244
+
245
+ def get_method(self, name: str):
246
+ """Get a method from the parent class."""
247
+ if self._parent_class is None:
248
+ return None
249
+
250
+ # Check parent class methods (methods is a dict with name -> AST node)
251
+ if hasattr(self._parent_class, 'methods') and isinstance(self._parent_class.methods, dict):
252
+ if name in self._parent_class.methods:
253
+ return self._parent_class.methods[name]
254
+
255
+ # Check if parent has its own parent (grandparent) - recursive lookup
256
+ if hasattr(self._parent_class, 'parent') and self._parent_class.parent:
257
+ grandparent_proxy = SuperProxy(self._instance, self._parent_class.parent, self._runtime)
258
+ return grandparent_proxy.get_method(name)
259
+
260
+ return None
261
+
262
+ def get_member(self, name: str):
263
+ """Get a member variable from parent class defaults."""
264
+ if self._parent_class is None:
265
+ return None
266
+
267
+ # Check parent class member defaults (members is a dict with name -> type/default)
268
+ if hasattr(self._parent_class, 'members') and isinstance(self._parent_class.members, dict):
269
+ if name in self._parent_class.members:
270
+ member_info = self._parent_class.members[name]
271
+ if isinstance(member_info, dict) and 'default' in member_info:
272
+ return self._runtime._evaluate(member_info['default'])
273
+ return member_info
274
+
275
+ return None
276
+
277
+
209
278
  @dataclass
210
279
  class Scope:
211
280
  """Variable scope"""
@@ -240,7 +309,11 @@ class Scope:
240
309
 
241
310
  @dataclass
242
311
  class ServiceDefinition:
243
- """Parsed service definition"""
312
+ """Parsed service definition
313
+
314
+ v4.8.6: Added __getattr__ and get() for accessing exported functions/structs/classes.
315
+ This allows include() modules to be used like: mod.myFunc() or mod.get('myFunc')
316
+ """
244
317
  name: str = ""
245
318
  version: str = "1.0"
246
319
  author: str = ""
@@ -250,7 +323,56 @@ class ServiceDefinition:
250
323
  priority: int = 0
251
324
  structs: Dict[str, ASTNode] = field(default_factory=dict)
252
325
  functions: Dict[str, ASTNode] = field(default_factory=dict)
326
+ classes: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Class definitions
327
+ enums: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Enum definitions
328
+ namespaces: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Namespace definitions
253
329
  event_handlers: Dict[str, List[ASTNode]] = field(default_factory=dict)
330
+ _runtime: Any = field(default=None, repr=False) # Reference to runtime for calling functions
331
+
332
+ def get(self, name: str, default: Any = None) -> Any:
333
+ """Get an exported function, struct, class, enum, or namespace by name."""
334
+ if name in self.functions:
335
+ return self.functions[name]
336
+ if name in self.classes:
337
+ return self.classes[name]
338
+ if name in self.structs:
339
+ return self.structs[name]
340
+ if name in self.enums:
341
+ return self.enums[name]
342
+ if name in self.namespaces:
343
+ return self.namespaces[name]
344
+ return default
345
+
346
+ def __getattr__(self, name: str) -> Any:
347
+ """Allow direct attribute access to functions, classes, structs, etc.
348
+
349
+ Usage: mod.myFunc (returns the function AST node)
350
+ mod.MyClass (returns the class definition)
351
+ """
352
+ # Avoid infinite recursion with dataclass fields
353
+ if name.startswith('_') or name in ('name', 'version', 'author', 'description',
354
+ 'dependencies', 'autostart', 'priority',
355
+ 'structs', 'functions', 'classes', 'enums',
356
+ 'namespaces', 'event_handlers'):
357
+ raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
358
+
359
+ # Check classes first (most common case after functions)
360
+ if hasattr(self, 'classes') and name in self.classes:
361
+ return self.classes[name]
362
+ # Check functions
363
+ if hasattr(self, 'functions') and name in self.functions:
364
+ return self.functions[name]
365
+ # Check structs
366
+ if hasattr(self, 'structs') and name in self.structs:
367
+ return self.structs[name]
368
+ # Check enums
369
+ if hasattr(self, 'enums') and name in self.enums:
370
+ return self.enums[name]
371
+ # Check namespaces
372
+ if hasattr(self, 'namespaces') and name in self.namespaces:
373
+ return self.namespaces[name]
374
+
375
+ raise AttributeError(f"Module '{self.name}' has no export '{name}'")
254
376
 
255
377
 
256
378
  class CSSLRuntime:
@@ -272,6 +394,8 @@ class CSSLRuntime:
272
394
  self._function_injections: Dict[str, List[tuple]] = {} # List of (code_block, captured_values_dict)
273
395
  self._function_replaced: Dict[str, bool] = {} # NEW: Track replaced functions (<<==)
274
396
  self._original_functions: Dict[str, Any] = {} # Store originals before replacement
397
+ self._hook_executing: set = set() # v4.9.2: Track currently executing hooks to prevent recursion
398
+ self._hook_locals: dict = {} # v4.9.2: Local variables from hooked function for local:: access
275
399
  self._injection_captures: Dict[str, Dict[str, Any]] = {} # Captured %vars per injection
276
400
  self._current_captured_values: Dict[str, Any] = {} # Current captured values during injection execution
277
401
  self._promoted_globals: Dict[str, Any] = {} # NEW: Variables promoted via global()
@@ -346,6 +470,15 @@ class CSSLRuntime:
346
470
  for name in self.builtins.list_functions():
347
471
  self.global_scope.set(name, self.builtins.get_function(name))
348
472
 
473
+ # v4.8.5: Setup C++ style stream variables (not functions)
474
+ # This enables: cout << "Hello" << endl; (without parentheses)
475
+ from .cssl_types import OutputStream, InputStream
476
+ self.global_scope.set('cout', OutputStream('stdout'))
477
+ self.global_scope.set('cerr', OutputStream('stderr'))
478
+ self.global_scope.set('clog', OutputStream('clog'))
479
+ self.global_scope.set('cin', InputStream('stdin'))
480
+ self.global_scope.set('endl', '\n') # endl as newline marker
481
+
349
482
  def get_module(self, path: str) -> Any:
350
483
  """Get a module by path like 'KernelClient.VSRam'"""
351
484
  parts = path.split('.')
@@ -429,6 +562,40 @@ class CSSLRuntime:
429
562
  return self._source_lines[line - 1]
430
563
  return ""
431
564
 
565
+ def _get_empty_value_for_type(self, value: Any) -> Any:
566
+ """v4.8.6: Get an empty value of the same type for move operations.
567
+
568
+ Used by -<== operator to clear source after move without destroying it.
569
+ Returns empty container for containers, None for primitives.
570
+ """
571
+ from .cssl_types import Stack, Vector, Array, DataStruct, List, Dictionary, Map, Queue
572
+
573
+ if isinstance(value, Stack):
574
+ return Stack(getattr(value, '_element_type', 'dynamic'))
575
+ elif isinstance(value, Vector):
576
+ return Vector(getattr(value, '_element_type', 'dynamic'))
577
+ elif isinstance(value, Array):
578
+ return Array(getattr(value, '_element_type', 'dynamic'))
579
+ elif isinstance(value, DataStruct):
580
+ return DataStruct(getattr(value, '_element_type', 'dynamic'))
581
+ elif isinstance(value, List):
582
+ return List(getattr(value, '_element_type', 'dynamic'))
583
+ elif isinstance(value, Dictionary):
584
+ return Dictionary(getattr(value, '_element_type', 'dynamic'))
585
+ elif isinstance(value, Map):
586
+ return Map(getattr(value, '_element_type', 'dynamic'))
587
+ elif isinstance(value, Queue):
588
+ return Queue(getattr(value, '_element_type', 'dynamic'))
589
+ elif isinstance(value, list):
590
+ return []
591
+ elif isinstance(value, dict):
592
+ return {}
593
+ elif isinstance(value, str):
594
+ return ""
595
+ else:
596
+ # For other types (int, float, objects), return None
597
+ return None
598
+
432
599
  def execute(self, source: str) -> Any:
433
600
  """Execute CSSL service source code"""
434
601
  self._source_lines = source.splitlines()
@@ -456,17 +623,28 @@ class CSSLRuntime:
456
623
  return self._execute_node(ast)
457
624
 
458
625
  def execute_file(self, filepath: str) -> Any:
459
- """Execute a CSSL service file"""
626
+ """Execute a CSSL file (auto-detects service vs program format)"""
460
627
  import os
461
628
  self._current_file = os.path.basename(filepath)
629
+ # v4.8.8: Track full path for relative payload resolution
630
+ self._current_file_path = os.path.abspath(filepath)
462
631
  with open(filepath, 'r', encoding='utf-8') as f:
463
632
  source = f.read()
464
- return self.execute(source)
633
+
634
+ # Auto-detect: if file starts with service-init, use service parser
635
+ # Otherwise use program parser (supports class, struct, define at top level)
636
+ stripped = source.lstrip()
637
+ if stripped.startswith('service-init') or stripped.startswith('service-run') or stripped.startswith('service-include'):
638
+ return self.execute(source)
639
+ else:
640
+ return self.execute_program(source)
465
641
 
466
642
  def execute_program_file(self, filepath: str) -> Any:
467
643
  """Execute a standalone CSSL program file"""
468
644
  import os
469
645
  self._current_file = os.path.basename(filepath)
646
+ # v4.8.8: Track full path for relative payload resolution
647
+ self._current_file_path = os.path.abspath(filepath)
470
648
  with open(filepath, 'r', encoding='utf-8') as f:
471
649
  source = f.read()
472
650
  return self.execute_program(source)
@@ -516,6 +694,28 @@ class CSSLRuntime:
516
694
  func_name = func_info.get('name')
517
695
  service.functions[func_name] = child
518
696
  self.scope.set(func_name, child)
697
+ # v4.8.6: class at top level
698
+ elif child.type == 'class':
699
+ class_info = child.value
700
+ class_name = class_info.get('name') if isinstance(class_info, dict) else class_info
701
+ # Execute class definition to register it
702
+ class_def = self._exec_class(child)
703
+ if class_def:
704
+ service.classes[class_name] = class_def
705
+ # v4.8.6: enum at top level
706
+ elif child.type == 'enum':
707
+ enum_info = child.value
708
+ enum_name = enum_info.get('name') if isinstance(enum_info, dict) else enum_info
709
+ enum_def = self._exec_enum(child)
710
+ if enum_def:
711
+ service.enums[enum_name] = enum_def
712
+ # v4.8.6: namespace at top level
713
+ elif child.type == 'namespace':
714
+ ns_info = child.value
715
+ ns_name = ns_info.get('name') if isinstance(ns_info, dict) else ns_info
716
+ ns_def = self._exec_namespace(child)
717
+ if ns_def:
718
+ service.namespaces[ns_name] = ns_def
519
719
 
520
720
  return service
521
721
 
@@ -541,6 +741,8 @@ class CSSLRuntime:
541
741
  self._exec_class(child)
542
742
  elif child.type == 'enum':
543
743
  self._exec_enum(child)
744
+ elif child.type == 'namespace':
745
+ self._exec_namespace(child)
544
746
  elif child.type == 'bytearrayed':
545
747
  self._exec_bytearrayed(child)
546
748
  elif child.type == 'function':
@@ -941,6 +1143,196 @@ class CSSLRuntime:
941
1143
 
942
1144
  return bytearrayed_obj
943
1145
 
1146
+ def _execute_bytearrayed_function(self, func_node: ASTNode) -> Any:
1147
+ """Execute a function with bytearrayed modifier (v4.7).
1148
+
1149
+ Bytearrayed functions call &FuncRef() to get a value, then match
1150
+ against case patterns to determine which body to execute.
1151
+
1152
+ Syntax:
1153
+ bytearrayed define Localize() {
1154
+ case "en": &GetLang() {
1155
+ return "Hello";
1156
+ }
1157
+ case "de": &GetLang() {
1158
+ return "Hallo";
1159
+ }
1160
+ default: {
1161
+ return "Unknown";
1162
+ }
1163
+ }
1164
+ """
1165
+ case_blocks = []
1166
+ default_node = None
1167
+ func_ref_stmts = [] # v4.8.8: Collect &func; statements before case/default
1168
+
1169
+ # Collect case/default blocks and func_ref statements
1170
+ for child in func_node.children:
1171
+ if not self._running:
1172
+ break
1173
+ if child.type == 'case':
1174
+ case_blocks.append(child)
1175
+ elif child.type == 'default':
1176
+ default_node = child
1177
+ elif child.type == 'func_ref':
1178
+ # v4.8.8: Collect func_ref statements (&n; &b(100);)
1179
+ func_ref_stmts.append(child)
1180
+ elif child.type == 'expression' and hasattr(child, 'value'):
1181
+ # Check if expression is a func_ref (ampersand expression)
1182
+ expr_val = child.value
1183
+ if hasattr(expr_val, 'type') and expr_val.type == 'func_ref':
1184
+ func_ref_stmts.append(expr_val)
1185
+
1186
+ # v4.8.8: Execute all func_ref statements and collect results
1187
+ collected_results = []
1188
+ for ref_stmt in func_ref_stmts:
1189
+ ref_info = ref_stmt.value if hasattr(ref_stmt, 'value') else ref_stmt
1190
+ if isinstance(ref_info, dict):
1191
+ func_name = ref_info.get('name')
1192
+ func_args = ref_info.get('args', [])
1193
+ else:
1194
+ func_name = str(ref_info)
1195
+ func_args = []
1196
+
1197
+ eval_args = [self._evaluate(a) for a in func_args] if func_args else []
1198
+
1199
+ # Call the function
1200
+ try:
1201
+ result = None
1202
+ func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
1203
+ if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
1204
+ result = self._call_function(func_def, eval_args)
1205
+ elif callable(func_def):
1206
+ result = func_def(*eval_args)
1207
+ except Exception as e:
1208
+ result = None
1209
+
1210
+ collected_results.append(result)
1211
+
1212
+ # Process each case block
1213
+ for case_node in case_blocks:
1214
+ case_value = case_node.value
1215
+ if not isinstance(case_value, dict):
1216
+ continue
1217
+
1218
+ patterns = case_value.get('patterns', [])
1219
+ func_refs = case_value.get('func_refs', [])
1220
+ body = case_value.get('body', [])
1221
+
1222
+ # v4.8.8: If we have collected results from &func; statements,
1223
+ # match patterns against those results (tuple pattern matching)
1224
+ if collected_results and not func_refs:
1225
+ # Match collected_results against patterns
1226
+ all_match = True
1227
+ if isinstance(patterns, list) and len(patterns) == 1 and isinstance(patterns[0], tuple):
1228
+ # Tuple pattern: case {0, 0}: -> patterns = [(0, 0)]
1229
+ expected = list(patterns[0])
1230
+ if len(expected) == len(collected_results):
1231
+ for i, (exp, got) in enumerate(zip(expected, collected_results)):
1232
+ if exp != got:
1233
+ all_match = False
1234
+ break
1235
+ else:
1236
+ all_match = False
1237
+ elif len(patterns) == len(collected_results):
1238
+ # Comma-separated patterns: case 0, 0: -> patterns = [0, 0]
1239
+ for i, (exp, got) in enumerate(zip(patterns, collected_results)):
1240
+ if exp != got:
1241
+ all_match = False
1242
+ break
1243
+ else:
1244
+ all_match = False
1245
+ else:
1246
+ # Original behavior: execute func_refs from case and match
1247
+ all_match = True
1248
+ for i, func_ref in enumerate(func_refs):
1249
+ func_name = func_ref.get('name')
1250
+ func_args = func_ref.get('args', [])
1251
+ eval_args = [self._evaluate(a) for a in func_args]
1252
+
1253
+ try:
1254
+ result = None
1255
+ if self._current_instance and hasattr(self._current_instance, 'get_method'):
1256
+ method = self._current_instance.get_method(func_name)
1257
+ if method:
1258
+ result = self._call_method(self._current_instance, method, eval_args)
1259
+ else:
1260
+ func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
1261
+ if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
1262
+ result = self._call_function(func_def, eval_args)
1263
+ elif callable(func_def):
1264
+ result = func_def(*eval_args)
1265
+ else:
1266
+ func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
1267
+ if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
1268
+ result = self._call_function(func_def, eval_args)
1269
+ elif callable(func_def):
1270
+ result = func_def(*eval_args)
1271
+ except Exception as e:
1272
+ result = None
1273
+
1274
+ if i < len(patterns):
1275
+ pattern = patterns[i]
1276
+ if result != pattern:
1277
+ all_match = False
1278
+ break
1279
+
1280
+ if all_match:
1281
+ # Execute the case body
1282
+ for stmt in body:
1283
+ try:
1284
+ self._execute_node(stmt)
1285
+ except CSSLReturn as ret:
1286
+ return ret.value
1287
+ return None
1288
+
1289
+ # No case matched - execute default
1290
+ if default_node and isinstance(default_node.value, dict):
1291
+ body = default_node.value.get('body', [])
1292
+ for stmt in body:
1293
+ try:
1294
+ self._execute_node(stmt)
1295
+ except CSSLReturn as ret:
1296
+ return ret.value
1297
+
1298
+ return None
1299
+
1300
+ def _match_bytearrayed_pattern(self, pattern: Any, values: List[Any]) -> bool:
1301
+ """Match a pattern against collected bytearrayed values.
1302
+
1303
+ Patterns can be:
1304
+ - List/tuple: [10, 7] matches if values[0]==10 and values[1]==7
1305
+ - Dict with values: evaluates expressions
1306
+ - Single value: matches first collected value
1307
+ - Wildcard '_': matches any value
1308
+ """
1309
+ if pattern is None:
1310
+ return False
1311
+
1312
+ # Convert pattern to list if needed
1313
+ if isinstance(pattern, (list, tuple)):
1314
+ pattern_list = list(pattern)
1315
+ elif isinstance(pattern, dict):
1316
+ # Dict patterns - extract values
1317
+ pattern_list = list(pattern.values())
1318
+ else:
1319
+ pattern_list = [pattern]
1320
+
1321
+ # Match each pattern element against collected values
1322
+ for i, pat_val in enumerate(pattern_list):
1323
+ if i >= len(values):
1324
+ return False
1325
+
1326
+ # Wildcard matches anything
1327
+ if pat_val == '_' or pat_val is None:
1328
+ continue
1329
+
1330
+ # Exact match
1331
+ if values[i] != pat_val:
1332
+ return False
1333
+
1334
+ return True
1335
+
944
1336
  def _exec_class(self, node: ASTNode) -> CSSLClass:
945
1337
  """Execute class definition - registers class in scope.
946
1338
 
@@ -957,6 +1349,26 @@ class CSSLRuntime:
957
1349
  extends_is_python = class_info.get('extends_is_python', False)
958
1350
  overwrites_class_name = class_info.get('overwrites')
959
1351
  overwrites_is_python = class_info.get('overwrites_is_python', False)
1352
+ uses_memory = class_info.get('uses_memory') # v4.9.0
1353
+
1354
+ # v4.9.0: Handle ': uses memory(address)' - deferred execution binding
1355
+ # Class constructor is hooked to the host's execution
1356
+ if uses_memory:
1357
+ host_obj = self._evaluate(uses_memory)
1358
+ # Convert to hashable key - use name for functions, id() for others
1359
+ if isinstance(host_obj, ASTNode):
1360
+ host_key = host_obj.value.get('name') if isinstance(host_obj.value, dict) else str(id(host_obj))
1361
+ elif isinstance(host_obj, str):
1362
+ host_key = host_obj
1363
+ else:
1364
+ host_key = str(id(host_obj))
1365
+ if not hasattr(self, '_memory_hooks'):
1366
+ self._memory_hooks = {}
1367
+ if host_key not in self._memory_hooks:
1368
+ self._memory_hooks[host_key] = []
1369
+ self._memory_hooks[host_key].append(('class', node))
1370
+ # Continue to register the class normally so it can be referenced
1371
+ # Hooks execute when the host is called
960
1372
 
961
1373
  # Resolve parent class if extends is specified
962
1374
  parent_class = None
@@ -1023,10 +1435,16 @@ class CSSLRuntime:
1023
1435
  # Add transformed children to node's children
1024
1436
  node.children = transformed_children
1025
1437
 
1438
+ destructors = [] # v4.8.8: Store destructors (constr ~Name())
1439
+
1026
1440
  for child in node.children:
1027
1441
  if child.type == 'constructor':
1028
- # New-style constructor from 'constr' keyword
1029
- constructors.append(child)
1442
+ # v4.8.8: Check if this is a destructor (~Name)
1443
+ if child.value.get('is_destructor'):
1444
+ destructors.append(child)
1445
+ else:
1446
+ # New-style constructor from 'constr' keyword
1447
+ constructors.append(child)
1030
1448
 
1031
1449
  elif child.type == 'function':
1032
1450
  # This is a method or old-style constructor
@@ -1061,6 +1479,7 @@ class CSSLRuntime:
1061
1479
  )
1062
1480
  # Store additional constructor info
1063
1481
  class_def.constructors = constructors # Multiple constructors from 'constr' keyword
1482
+ class_def.destructors = destructors # v4.8.8: Destructors (constr ~Name())
1064
1483
  class_def.class_params = class_params # Class-level constructor parameters
1065
1484
  class_def.extends_args = extends_args # Arguments to pass to parent constructor
1066
1485
 
@@ -1131,8 +1550,8 @@ class CSSLRuntime:
1131
1550
  # Try setting on class instead
1132
1551
  try:
1133
1552
  setattr(target.__class__, method_name, wrapper)
1134
- except:
1135
- pass
1553
+ except (AttributeError, TypeError):
1554
+ pass # Can't set attribute on immutable type
1136
1555
  elif isinstance(target, CSSLClass):
1137
1556
  # CSSL class - directly replace methods
1138
1557
  for method_name, method_node in methods_to_overwrite.items():
@@ -1147,8 +1566,8 @@ class CSSLRuntime:
1147
1566
  except AttributeError:
1148
1567
  try:
1149
1568
  setattr(py_obj.__class__, method_name, wrapper)
1150
- except:
1151
- pass
1569
+ except (AttributeError, TypeError):
1570
+ pass # Can't set attribute on immutable type
1152
1571
 
1153
1572
  def _create_method_wrapper(self, method_node: ASTNode, instance: Any):
1154
1573
  """Create a Python-callable wrapper for a CSSL method that works with an instance."""
@@ -1163,6 +1582,64 @@ class CSSLRuntime:
1163
1582
  self._current_instance = old_instance
1164
1583
  return wrapper
1165
1584
 
1585
+ def _exec_namespace(self, node: ASTNode) -> 'CSSLNamespace':
1586
+ """Execute namespace definition - registers namespace in scope.
1587
+
1588
+ Namespaces group functions, classes, and nested namespaces together,
1589
+ accessible via the :: operator (e.g., mylib::myFunc()).
1590
+
1591
+ Syntax:
1592
+ namespace mylib {
1593
+ void myFunc() { ... }
1594
+ class MyClass { ... }
1595
+ namespace nested { ... }
1596
+ }
1597
+
1598
+ Access: mylib::myFunc(), mylib::MyClass, mylib::nested::innerFunc()
1599
+ """
1600
+ ns_name = node.value.get('name') if isinstance(node.value, dict) else node.value
1601
+
1602
+ # Create namespace object
1603
+ namespace = CSSLNamespace(ns_name)
1604
+
1605
+ # Process namespace members
1606
+ for child in node.children:
1607
+ if child.type == 'function':
1608
+ # Register function in namespace
1609
+ func_info = child.value
1610
+ func_name = func_info.get('name')
1611
+ namespace.functions[func_name] = child
1612
+ elif child.type == 'class':
1613
+ # Register class in namespace
1614
+ class_info = child.value
1615
+ class_name = class_info.get('name')
1616
+ cssl_class = self._exec_class(child)
1617
+ namespace.classes[class_name] = cssl_class
1618
+ elif child.type == 'enum':
1619
+ # Register enum in namespace
1620
+ enum_info = child.value
1621
+ enum_name = enum_info.get('name')
1622
+ self._exec_enum(child)
1623
+ namespace.enums[enum_name] = self.scope.get(enum_name)
1624
+ elif child.type == 'struct':
1625
+ # Register struct in namespace
1626
+ struct_info = child.value
1627
+ if isinstance(struct_info, dict):
1628
+ struct_name = struct_info.get('name', '')
1629
+ else:
1630
+ struct_name = struct_info
1631
+ namespace.structs[struct_name] = child
1632
+ elif child.type == 'namespace':
1633
+ # Nested namespace
1634
+ nested_ns = self._exec_namespace(child)
1635
+ namespace.namespaces[nested_ns.name] = nested_ns
1636
+
1637
+ # Register namespace in both local and global scope for :: access
1638
+ self.scope.set(ns_name, namespace)
1639
+ self.global_scope.set(ns_name, namespace)
1640
+
1641
+ return namespace
1642
+
1166
1643
  def _transform_and_parse_class_body(self, raw_body: str, language: str, class_name: str) -> list:
1167
1644
  """Transform source code from another language to CSSL and parse as class body.
1168
1645
 
@@ -1293,6 +1770,48 @@ class CSSLRuntime:
1293
1770
  extends_is_python = func_info.get('extends_is_python', False)
1294
1771
  overwrites_func = func_info.get('overwrites')
1295
1772
  overwrites_is_python = func_info.get('overwrites_is_python', False)
1773
+ uses_memory = func_info.get('uses_memory') # v4.9.0
1774
+
1775
+ # v4.9.0: Handle ': uses memory(address)' - deferred execution binding
1776
+ # Function is not executed immediately but deferred until the host is called
1777
+ # Calling this function directly is a no-op - it only triggers when the host is called
1778
+ if uses_memory:
1779
+ # Evaluate the address expression (can be function ref, address string, etc.)
1780
+ host_obj = self._evaluate(uses_memory)
1781
+
1782
+ # Resolve the actual target function to get its name for hook lookup
1783
+ host_key = None
1784
+ from .cssl_types import Address
1785
+ if isinstance(host_obj, Address):
1786
+ # Address type - reflect to get the original object
1787
+ reflected = host_obj.reflect()
1788
+ if isinstance(reflected, ASTNode) and reflected.type == 'function':
1789
+ host_key = reflected.value.get('name')
1790
+ elif reflected is not None:
1791
+ host_key = str(id(reflected))
1792
+ elif isinstance(host_obj, ASTNode):
1793
+ # Direct function reference
1794
+ host_key = host_obj.value.get('name') if isinstance(host_obj.value, dict) else str(id(host_obj))
1795
+ elif isinstance(host_obj, str):
1796
+ host_key = host_obj # Address string like "0x..."
1797
+ else:
1798
+ host_key = str(id(host_obj))
1799
+
1800
+ if host_key:
1801
+ # Store the function as a memory hook
1802
+ if not hasattr(self, '_memory_hooks'):
1803
+ self._memory_hooks = {} # key -> list of hooked functions
1804
+ if host_key not in self._memory_hooks:
1805
+ self._memory_hooks[host_key] = []
1806
+ self._memory_hooks[host_key].append(node)
1807
+
1808
+ # Mark function as a hook (calling it directly is a no-op)
1809
+ node.value['_is_memory_hook'] = True
1810
+ # Register the function by name for reference
1811
+ self.scope.set(func_name, node)
1812
+ if is_global:
1813
+ self.global_scope.set(func_name, node)
1814
+ return None
1296
1815
 
1297
1816
  # Get append/overwrite reference info (&Class::method syntax)
1298
1817
  append_mode = func_info.get('append_mode', False)
@@ -1338,6 +1857,24 @@ class CSSLRuntime:
1338
1857
  self.scope.set(overwrites_func, node)
1339
1858
  self.global_scope.set(overwrites_func, node)
1340
1859
 
1860
+ # v4.9.3: Check for async modifier - wrap function in CSSLAsyncFunction
1861
+ modifiers = func_info.get('modifiers', [])
1862
+ if 'async' in modifiers:
1863
+ # Create async wrapper that returns a Future when called
1864
+ async_func = CSSLAsyncFunction(func_name, node, self)
1865
+ self.scope.set(func_name, async_func)
1866
+ if is_global:
1867
+ self.global_scope.set(func_name, async_func)
1868
+ self._promoted_globals[func_name] = async_func
1869
+ return None
1870
+
1871
+ # v4.9.3: Check if function is a generator (contains yield statements)
1872
+ # Return type 'generator' or body contains yield
1873
+ return_type = func_info.get('return_type', '')
1874
+ if return_type == 'generator' or self._contains_yield(node):
1875
+ # Mark as generator function - will create CSSLGenerator on call
1876
+ node.value['_is_generator'] = True
1877
+
1341
1878
  # Register the function (local by default, global if marked)
1342
1879
  self.scope.set(func_name, node)
1343
1880
  if is_global:
@@ -1345,6 +1882,38 @@ class CSSLRuntime:
1345
1882
  self._promoted_globals[func_name] = node
1346
1883
  return None
1347
1884
 
1885
+ def _contains_yield(self, node: ASTNode) -> bool:
1886
+ """Check if a function node contains yield statements."""
1887
+ if node is None:
1888
+ return False
1889
+
1890
+ def _search_yield(n):
1891
+ if n is None:
1892
+ return False
1893
+ if hasattr(n, 'type') and n.type == 'yield':
1894
+ return True
1895
+ if hasattr(n, 'children'):
1896
+ for child in n.children:
1897
+ if _search_yield(child):
1898
+ return True
1899
+ if hasattr(n, 'value'):
1900
+ # Check if value is an ASTNode (nested structure)
1901
+ if isinstance(n.value, dict):
1902
+ for v in n.value.values():
1903
+ if isinstance(v, ASTNode) and _search_yield(v):
1904
+ return True
1905
+ if isinstance(v, list):
1906
+ for item in v:
1907
+ if isinstance(item, ASTNode) and _search_yield(item):
1908
+ return True
1909
+ return False
1910
+
1911
+ # Search in function body (children)
1912
+ for child in node.children:
1913
+ if _search_yield(child):
1914
+ return True
1915
+ return False
1916
+
1348
1917
  def _resolve_function_target(self, name: str, is_python: bool) -> Any:
1349
1918
  """Resolve a function target for extends/overwrites."""
1350
1919
  if is_python:
@@ -1392,6 +1961,265 @@ class CSSLRuntime:
1392
1961
 
1393
1962
  return wrapper
1394
1963
 
1964
+ def _create_generator(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None) -> CSSLGenerator:
1965
+ """Create a CSSLGenerator for a generator function - v4.9.3
1966
+
1967
+ Generator functions are functions that contain yield statements.
1968
+ When called, they return a CSSLGenerator object that can be iterated.
1969
+
1970
+ Example CSSL:
1971
+ generator<int> define Range(int n) {
1972
+ int i = 0;
1973
+ while (i < n) {
1974
+ yield i;
1975
+ i = i + 1;
1976
+ }
1977
+ }
1978
+
1979
+ gen = Range(5);
1980
+ while (gen.has_next()) {
1981
+ printl(gen.next());
1982
+ }
1983
+ """
1984
+ func_info = func_node.value
1985
+ params = func_info.get('params', [])
1986
+ kwargs = kwargs or {}
1987
+ func_name = func_info.get('name', 'anonymous')
1988
+
1989
+ # Set up scope with parameters first (before creating generator)
1990
+ old_scope = self.scope
1991
+ gen_scope = Scope(parent=self.scope)
1992
+
1993
+ # Bind parameters
1994
+ for i, param in enumerate(params):
1995
+ if isinstance(param, dict):
1996
+ param_name = param['name']
1997
+ param_default = param.get('default')
1998
+ else:
1999
+ param_name = param
2000
+ param_default = None
2001
+
2002
+ # Get value from kwargs, args, or default
2003
+ if param_name in kwargs:
2004
+ value = kwargs[param_name]
2005
+ elif i < len(args):
2006
+ value = args[i]
2007
+ elif param_default is not None:
2008
+ self.scope = gen_scope
2009
+ value = self._evaluate(param_default)
2010
+ self.scope = old_scope
2011
+ else:
2012
+ value = None
2013
+
2014
+ gen_scope.set(param_name, value)
2015
+
2016
+ # Generator execution using a Python generator with proper state management
2017
+ runtime = self
2018
+
2019
+ def cssl_generator_func():
2020
+ # Execute generator body, properly managing scope around each yield
2021
+ for child in func_node.children:
2022
+ if not runtime._running:
2023
+ return
2024
+
2025
+ # Save caller's scope before entering generator scope
2026
+ caller_scope = runtime.scope
2027
+ runtime.scope = gen_scope
2028
+
2029
+ try:
2030
+ if child.type == 'yield':
2031
+ # Direct yield node
2032
+ value = runtime._evaluate(child.value) if child.value else None
2033
+ runtime.scope = caller_scope # Restore before yield
2034
+ yield value
2035
+ elif child.type == 'while':
2036
+ # While loop - handle yield inside
2037
+ for val in _execute_generator_while(child):
2038
+ runtime.scope = caller_scope
2039
+ yield val
2040
+ elif child.type in ('for', 'c_for'):
2041
+ for val in _execute_generator_for(child):
2042
+ runtime.scope = caller_scope
2043
+ yield val
2044
+ elif child.type == 'foreach':
2045
+ for val in _execute_generator_foreach(child):
2046
+ runtime.scope = caller_scope
2047
+ yield val
2048
+ elif child.type == 'if':
2049
+ for val in _execute_generator_if(child):
2050
+ runtime.scope = caller_scope
2051
+ yield val
2052
+ elif child.type == 'return':
2053
+ runtime.scope = caller_scope
2054
+ return
2055
+ else:
2056
+ # Regular statement
2057
+ try:
2058
+ runtime._execute_node(child)
2059
+ except CSSLYield as y:
2060
+ runtime.scope = caller_scope
2061
+ yield y.value
2062
+ except CSSLReturn:
2063
+ runtime.scope = caller_scope
2064
+ return
2065
+ finally:
2066
+ runtime.scope = caller_scope
2067
+
2068
+ def _execute_generator_while(node):
2069
+ while runtime._running:
2070
+ # Always restore gen_scope at start of each iteration (may have been changed by yield)
2071
+ runtime.scope = gen_scope
2072
+ if not runtime._evaluate(node.value.get('condition')):
2073
+ break
2074
+ try:
2075
+ for child in node.children:
2076
+ runtime.scope = gen_scope # Ensure gen_scope for all operations
2077
+ if child.type == 'yield':
2078
+ value = runtime._evaluate(child.value) if child.value else None
2079
+ yield value
2080
+ else:
2081
+ try:
2082
+ runtime._execute_node(child)
2083
+ except CSSLYield as y:
2084
+ yield y.value
2085
+ except CSSLReturn:
2086
+ return
2087
+ except CSSLBreak:
2088
+ break
2089
+ except CSSLContinue:
2090
+ continue
2091
+
2092
+ def _execute_generator_for(node):
2093
+ if node.type == 'c_for':
2094
+ init = node.value.get('init')
2095
+ condition = node.value.get('condition')
2096
+ update = node.value.get('update')
2097
+
2098
+ runtime.scope = gen_scope
2099
+ if init:
2100
+ runtime._execute_node(init)
2101
+
2102
+ while runtime._running:
2103
+ # Restore gen_scope at start of each iteration
2104
+ runtime.scope = gen_scope
2105
+ if condition is not None and not runtime._evaluate(condition):
2106
+ break
2107
+ try:
2108
+ for child in node.children:
2109
+ runtime.scope = gen_scope
2110
+ if child.type == 'yield':
2111
+ value = runtime._evaluate(child.value) if child.value else None
2112
+ yield value
2113
+ else:
2114
+ try:
2115
+ runtime._execute_node(child)
2116
+ except CSSLYield as y:
2117
+ yield y.value
2118
+ except CSSLReturn:
2119
+ return
2120
+ except CSSLBreak:
2121
+ break
2122
+ except CSSLContinue:
2123
+ pass
2124
+
2125
+ runtime.scope = gen_scope
2126
+ if update:
2127
+ runtime._evaluate(update)
2128
+ else:
2129
+ runtime.scope = gen_scope
2130
+ var_name = node.value.get('var')
2131
+ start = int(runtime._evaluate(node.value.get('start')))
2132
+ end = int(runtime._evaluate(node.value.get('end')))
2133
+ step_node = node.value.get('step')
2134
+ step = int(runtime._evaluate(step_node)) if step_node else 1
2135
+
2136
+ for i in range(start, end, step):
2137
+ if not runtime._running:
2138
+ break
2139
+ runtime.scope = gen_scope # Restore at start of each iteration
2140
+ gen_scope.set(var_name, i)
2141
+ try:
2142
+ for child in node.children:
2143
+ runtime.scope = gen_scope
2144
+ if child.type == 'yield':
2145
+ value = runtime._evaluate(child.value) if child.value else None
2146
+ yield value
2147
+ else:
2148
+ try:
2149
+ runtime._execute_node(child)
2150
+ except CSSLYield as y:
2151
+ yield y.value
2152
+ except CSSLReturn:
2153
+ return
2154
+ except CSSLBreak:
2155
+ break
2156
+ except CSSLContinue:
2157
+ continue
2158
+
2159
+ def _execute_generator_foreach(node):
2160
+ runtime.scope = gen_scope
2161
+ var_name = node.value.get('var')
2162
+ iterable = runtime._evaluate(node.value.get('iterable'))
2163
+
2164
+ if hasattr(iterable, '__iter__'):
2165
+ for item in iterable:
2166
+ if not runtime._running:
2167
+ break
2168
+ runtime.scope = gen_scope # Restore at start of each iteration
2169
+ gen_scope.set(var_name, item)
2170
+ try:
2171
+ for child in node.children:
2172
+ runtime.scope = gen_scope
2173
+ if child.type == 'yield':
2174
+ value = runtime._evaluate(child.value) if child.value else None
2175
+ yield value
2176
+ else:
2177
+ try:
2178
+ runtime._execute_node(child)
2179
+ except CSSLYield as y:
2180
+ yield y.value
2181
+ except CSSLReturn:
2182
+ return
2183
+ except CSSLBreak:
2184
+ break
2185
+ except CSSLContinue:
2186
+ continue
2187
+
2188
+ def _execute_generator_if(node):
2189
+ runtime.scope = gen_scope
2190
+ condition = runtime._evaluate(node.value.get('condition'))
2191
+ if condition:
2192
+ for child in node.children:
2193
+ runtime.scope = gen_scope
2194
+ if child.type == 'yield':
2195
+ value = runtime._evaluate(child.value) if child.value else None
2196
+ yield value
2197
+ else:
2198
+ try:
2199
+ runtime._execute_node(child)
2200
+ except CSSLYield as y:
2201
+ yield y.value
2202
+ except CSSLReturn:
2203
+ return
2204
+ else:
2205
+ else_branch = node.value.get('else_branch')
2206
+ if else_branch:
2207
+ branches = else_branch if isinstance(else_branch, list) else [else_branch]
2208
+ for child in branches:
2209
+ runtime.scope = gen_scope
2210
+ if hasattr(child, 'type') and child.type == 'yield':
2211
+ value = runtime._evaluate(child.value) if child.value else None
2212
+ yield value
2213
+ elif hasattr(child, 'type'):
2214
+ try:
2215
+ runtime._execute_node(child)
2216
+ except CSSLYield as y:
2217
+ yield y.value
2218
+ except CSSLReturn:
2219
+ return
2220
+
2221
+ return CSSLGenerator(func_name, cssl_generator_func())
2222
+
1395
2223
  def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
1396
2224
  """Overwrite a class method or function with the replacement.
1397
2225
 
@@ -1470,6 +2298,13 @@ class CSSLRuntime:
1470
2298
  # Handle CSSL class method overwrite
1471
2299
  target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1472
2300
 
2301
+ # v4.9.2: Handle &builtinName syntax (stored as __builtins__::builtinName)
2302
+ if ref_class == '__builtins__' and ref_member:
2303
+ # Treat as standalone builtin function overwrite
2304
+ ref_class = ref_member
2305
+ ref_member = None
2306
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
2307
+
1473
2308
  # v4.2.3: Handle standalone function reference (&functionName) including builtins
1474
2309
  if ref_member is None:
1475
2310
  # Check if this is a builtin function
@@ -1586,8 +2421,8 @@ class CSSLRuntime:
1586
2421
  if callable(_saved_original):
1587
2422
  try:
1588
2423
  result = _saved_original(*args, **kwargs)
1589
- except:
1590
- pass
2424
+ except Exception:
2425
+ pass # Continue to appended code even if original fails
1591
2426
  # Then run appended code - disable append_mode to prevent recursion
1592
2427
  append_node.value['append_mode'] = False
1593
2428
  try:
@@ -1602,15 +2437,106 @@ class CSSLRuntime:
1602
2437
 
1603
2438
  # Handle CSSL class method append
1604
2439
  target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1605
- if target_class is None:
1606
- # Standalone function append
2440
+
2441
+ # v4.9.2: Handle &builtinName ++ syntax (stored as __builtins__::builtinName)
2442
+ is_builtin_hook = False
2443
+ if ref_class == '__builtins__' and ref_member:
2444
+ # Treat as standalone builtin function append
2445
+ ref_class = ref_member
2446
+ ref_member = None
2447
+ is_builtin_hook = True
2448
+ # For builtin hooks, force standalone path (don't use target_class lookup)
2449
+ target_class = None
2450
+ # v4.9.2: Also check if ref_class is a direct builtin name (not via __builtins__)
2451
+ # This handles cases like &address ++ where address is a builtin
2452
+ elif ref_member is None and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
2453
+ is_builtin_hook = True
2454
+ # Force standalone path for builtin hooks
2455
+ target_class = None
2456
+
2457
+ if target_class is None or is_builtin_hook:
2458
+ # Standalone function append (or builtin hook)
1607
2459
  if ref_member is None:
1608
- original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
2460
+ # v4.9.2: For builtin hooks, get from builtins._functions directly
2461
+ if is_builtin_hook and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
2462
+ original_func = self.builtins._functions.get(ref_class)
2463
+ # Store original before we overwrite
2464
+ if original_func and ref_class not in self._original_functions:
2465
+ self._original_functions[ref_class] = original_func
2466
+ else:
2467
+ original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
2468
+ # For non-builtin, check _original_functions as fallback
2469
+ if original_func is None:
2470
+ if hasattr(self, '_original_functions'):
2471
+ original_func = self._original_functions.get(ref_class)
2472
+ if original_func is None and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
2473
+ original_func = self.builtins._functions.get(ref_class)
2474
+ if original_func and ref_class not in self._original_functions:
2475
+ self._original_functions[ref_class] = original_func
2476
+
1609
2477
  if original_func:
1610
2478
  # Store original in append_node so it can run first
1611
2479
  append_node.value['_original_func'] = original_func
1612
2480
  self.scope.set(ref_class, append_node)
1613
2481
  self.global_scope.set(ref_class, append_node)
2482
+
2483
+ # v4.9.2: Also update builtins._functions for builtin hooks
2484
+ # For builtin hooks, DON'T store in scope - only use the wrapper in builtins._functions
2485
+ # This ensures all calls go through the wrapper which has proper re-entry protection
2486
+ if hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
2487
+ # Remove from scope so all lookups go through builtins
2488
+ self.scope.set(ref_class, None)
2489
+ self.global_scope.set(ref_class, None)
2490
+ # v4.9.2: Also clear from _promoted_globals since @var=builtin stores there too
2491
+ if ref_class in self._promoted_globals:
2492
+ del self._promoted_globals[ref_class]
2493
+
2494
+ def make_wrapper(node, runtime, builtin_name, orig_func):
2495
+ def wrapper(*args, **kwargs):
2496
+ # v4.9.2: Prevent infinite recursion - if hook is already executing,
2497
+ # call the original builtin directly instead of the hook
2498
+ if builtin_name in runtime._hook_executing:
2499
+ # Already in hook, call original directly
2500
+ if callable(orig_func):
2501
+ return orig_func(*args, **kwargs)
2502
+ return None
2503
+ # Mark hook as executing
2504
+ runtime._hook_executing.add(builtin_name)
2505
+ try:
2506
+ # v4.9.2: Append mode - run ORIGINAL first, capture result
2507
+ original_result = None
2508
+ if callable(orig_func):
2509
+ try:
2510
+ original_result = orig_func(*args, **kwargs)
2511
+ except Exception:
2512
+ pass # Continue to hook even if original fails
2513
+ # Store original result so hook can access it via %<name>_result or _result
2514
+ runtime._original_functions[f'{builtin_name}_result'] = original_result
2515
+ # Also set _result in scope for easy access inside hook
2516
+ runtime.scope.set('_result', original_result)
2517
+ # v4.9.2: Populate _hook_locals with args for local:: access
2518
+ old_hook_locals = runtime._hook_locals
2519
+ runtime._hook_locals = {'_result': original_result, '_args': args, '_kwargs': kwargs}
2520
+ # Add numbered args (local::0, local::1, etc.)
2521
+ for i, arg in enumerate(args):
2522
+ runtime._hook_locals[str(i)] = arg
2523
+ # Run hook body with same args
2524
+ hook_result = runtime._call_function(node, list(args), kwargs)
2525
+ # Restore previous hook locals
2526
+ runtime._hook_locals = old_hook_locals
2527
+ # Clean up _result
2528
+ runtime.scope.set('_result', None)
2529
+ # Return original result unless hook explicitly returned something
2530
+ if hook_result is not None:
2531
+ return hook_result
2532
+ return original_result
2533
+ finally:
2534
+ runtime._hook_executing.discard(builtin_name)
2535
+ # Clean up result
2536
+ runtime._original_functions.pop(f'{builtin_name}_result', None)
2537
+ return wrapper
2538
+ self.builtins._functions[ref_class] = make_wrapper(append_node, self, ref_class, original_func)
2539
+ return # Don't store in scope for builtin hooks
1614
2540
  return
1615
2541
 
1616
2542
  if isinstance(target_class, CSSLClass) and ref_member:
@@ -1742,12 +2668,68 @@ class CSSLRuntime:
1742
2668
  instance = Dictionary(element_type)
1743
2669
  elif type_name == 'map':
1744
2670
  instance = Map(element_type)
2671
+ elif type_name == 'queue':
2672
+ # v4.7: Queue with optional size from element_type (queue<type, size>)
2673
+ queue_size = decl.get('size', 'dynamic')
2674
+ instance = Queue(element_type, queue_size)
2675
+ elif type_name == 'bit':
2676
+ # v4.9.0: Single bit value (0 or 1)
2677
+ from .cssl_types import Bit
2678
+ if value_node is None:
2679
+ instance = Bit(0)
2680
+ else:
2681
+ val = self._evaluate(value_node)
2682
+ if isinstance(val, Bit):
2683
+ # Already a Bit object (e.g., from .copy())
2684
+ instance = val
2685
+ elif isinstance(val, int) and val in (0, 1):
2686
+ instance = Bit(val)
2687
+ else:
2688
+ raise CSSLRuntimeError(f"Bit must be 0 or 1, got {val}", node.line)
2689
+ elif type_name == 'byte':
2690
+ # v4.9.0: Byte value (x^y notation where x=0/1, y=0-255)
2691
+ from .cssl_types import Byte
2692
+ if value_node is None:
2693
+ instance = Byte(0, 0)
2694
+ else:
2695
+ val = self._evaluate(value_node)
2696
+ if isinstance(val, Byte):
2697
+ instance = val
2698
+ elif isinstance(val, tuple) and len(val) == 2:
2699
+ # Tuple from byte literal parsing (base, weight)
2700
+ instance = Byte(val[0], val[1])
2701
+ elif isinstance(val, int):
2702
+ # Plain int - store as 1^val
2703
+ instance = Byte(1, val % 256)
2704
+ else:
2705
+ raise CSSLRuntimeError(f"Invalid byte value: {val}", node.line)
2706
+ elif type_name in ('address', 'ptr', 'pointer'):
2707
+ # v4.9.0: Memory address/pointer type
2708
+ # v4.9.3: Added 'ptr' and 'pointer' as aliases for 'address'
2709
+ from .cssl_types import Address
2710
+ if value_node is None:
2711
+ instance = Address() # Null address
2712
+ else:
2713
+ val = self._evaluate(value_node)
2714
+ if isinstance(val, Address):
2715
+ # Already an Address object
2716
+ instance = val
2717
+ elif isinstance(val, str):
2718
+ # Address string from memory().get("address")
2719
+ instance = Address(val)
2720
+ else:
2721
+ # Create address from object
2722
+ instance = Address(obj=val)
1745
2723
  else:
1746
2724
  # Default: evaluate the value or set to None
1747
2725
  instance = self._evaluate(value_node) if value_node else None
1748
2726
 
1749
2727
  # If there's an explicit value, use it instead
1750
- if value_node and type_name not in ('int', 'integer', 'string', 'str', 'float', 'double', 'bool', 'dynamic', 'json', 'array'):
2728
+ # v4.8.7: Removed 'array' from exclusion - arrays should also get init values
2729
+ # v4.8.8: Added 'instance' to exclusion - instance type already evaluates value above
2730
+ # v4.9.0: Added 'bit', 'byte', 'address' to exclusion - they already handle values above
2731
+ # v4.9.2: Added 'ptr', 'pointer' to exclusion - they already evaluate value above
2732
+ if value_node and type_name not in ('int', 'integer', 'string', 'str', 'float', 'double', 'bool', 'dynamic', 'json', 'instance', 'bit', 'byte', 'address', 'ptr', 'pointer'):
1751
2733
  # For container types, the value might be initialization data
1752
2734
  init_value = self._evaluate(value_node)
1753
2735
  if isinstance(init_value, (list, tuple)):
@@ -1755,6 +2737,13 @@ class CSSLRuntime:
1755
2737
  if non_null:
1756
2738
  init_value = [v for v in init_value if v is not None]
1757
2739
  instance.extend(init_value)
2740
+ elif isinstance(init_value, dict):
2741
+ # v4.8.7: Handle dict initialization: dict d = {"key": "value"}
2742
+ if hasattr(instance, 'update'):
2743
+ instance.update(init_value)
2744
+ elif hasattr(instance, '__setitem__'):
2745
+ for k, v in init_value.items():
2746
+ instance[k] = v
1758
2747
  elif init_value is not None:
1759
2748
  if hasattr(instance, 'append'):
1760
2749
  instance.append(init_value)
@@ -1781,8 +2770,21 @@ class CSSLRuntime:
1781
2770
  modifiers = decl.get('modifiers', [])
1782
2771
  is_global = 'global' in modifiers
1783
2772
 
1784
- # Store in scope
1785
- self.scope.set(var_name, instance)
2773
+ # v4.9.2: Check for this->member declaration
2774
+ is_this_member = decl.get('is_this_member', False)
2775
+
2776
+ # Store in appropriate location
2777
+ if is_this_member:
2778
+ # this->member declaration - store on current instance
2779
+ if self._current_instance is None:
2780
+ raise CSSLRuntimeError("'this' used outside of class method context", node.line)
2781
+ if hasattr(self._current_instance, 'set_member'):
2782
+ self._current_instance.set_member(var_name, instance)
2783
+ else:
2784
+ setattr(self._current_instance, var_name, instance)
2785
+ else:
2786
+ # Normal variable - store in scope
2787
+ self.scope.set(var_name, instance)
1786
2788
 
1787
2789
  # If global, also store in promoted_globals and global_scope
1788
2790
  if is_global:
@@ -1955,13 +2957,14 @@ class CSSLRuntime:
1955
2957
  # Fallback: execute normally
1956
2958
  return self._execute_node(inner)
1957
2959
 
1958
- def _call_function(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None) -> Any:
2960
+ def _call_function(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None, _is_hook_trigger: bool = False) -> Any:
1959
2961
  """Call a function node with arguments (positional and named)
1960
2962
 
1961
2963
  Args:
1962
2964
  func_node: The function AST node
1963
2965
  args: List of positional arguments
1964
2966
  kwargs: Dict of named arguments (param_name -> value)
2967
+ _is_hook_trigger: Internal flag - True when called as a hook trigger
1965
2968
 
1966
2969
  Supports:
1967
2970
  define func : extends otherFunc() { ... } - Inherit local vars from otherFunc
@@ -1971,6 +2974,33 @@ class CSSLRuntime:
1971
2974
  modifiers = func_info.get('modifiers', [])
1972
2975
  kwargs = kwargs or {}
1973
2976
 
2977
+ # v4.9.3: If this is a generator function, return a CSSLGenerator instead of executing
2978
+ if func_info.get('_is_generator'):
2979
+ return self._create_generator(func_node, args, kwargs)
2980
+
2981
+ # v4.9.0: If this is a memory hook function and it's being called directly
2982
+ # (not as a hook trigger), skip execution - it's just a hook registration
2983
+ if func_info.get('_is_memory_hook') and not _is_hook_trigger:
2984
+ return None # Silent no-op for direct calls to hook functions
2985
+
2986
+ # v4.9.0: Execute memory-hooked functions first
2987
+ # Functions with ': uses memory(address)' are executed when their host is called
2988
+ if hasattr(self, '_memory_hooks'):
2989
+ # Look up by function name (how hooks are stored)
2990
+ func_name = func_info.get('name', '')
2991
+ if func_name in self._memory_hooks:
2992
+ for hooked_item in self._memory_hooks[func_name]:
2993
+ try:
2994
+ # hooked_item can be ASTNode or ('class', ASTNode) tuple
2995
+ if isinstance(hooked_item, tuple) and hooked_item[0] == 'class':
2996
+ # Class hook - instantiate the class
2997
+ pass # Class instantiation happens separately
2998
+ elif isinstance(hooked_item, ASTNode) and hooked_item.type == 'function':
2999
+ # Call with _is_hook_trigger=True so hook body executes
3000
+ self._call_function(hooked_item, args, kwargs, _is_hook_trigger=True)
3001
+ except Exception:
3002
+ pass # Continue with host even if hook fails
3003
+
1974
3004
  # v4.2.5: Deferred &target replacement for non-embedded functions
1975
3005
  # If function has &target and hasn't been applied yet, apply now on first call
1976
3006
  append_ref_class = func_info.get('append_ref_class')
@@ -1996,8 +3026,8 @@ class CSSLRuntime:
1996
3026
  # Python function - call it first to populate any state
1997
3027
  try:
1998
3028
  extends_resolved(*args, **kwargs)
1999
- except:
2000
- pass
3029
+ except Exception:
3030
+ pass # Extended function failed - continue anyway
2001
3031
  elif hasattr(extends_resolved, 'value'):
2002
3032
  # CSSL function - execute it in a temporary scope to get local vars
2003
3033
  old_scope = self.scope
@@ -2012,8 +3042,8 @@ class CSSLRuntime:
2012
3042
  except CSSLReturn:
2013
3043
  # Parent returned - that's fine, we just want the local vars
2014
3044
  pass
2015
- except:
2016
- pass
3045
+ except Exception:
3046
+ pass # Extended function failed - still copy local vars
2017
3047
  finally:
2018
3048
  # ALWAYS copy local vars (even if parent returned)
2019
3049
  for name, value in temp_scope.variables.items():
@@ -2066,16 +3096,23 @@ class CSSLRuntime:
2066
3096
 
2067
3097
  try:
2068
3098
  # Handle append mode (++) - execute referenced function first
3099
+ # v4.9.2: Skip for builtin hooks - the wrapper handles original execution
2069
3100
  append_mode = func_info.get('append_mode', False)
2070
3101
  append_ref_class = func_info.get('append_ref_class')
2071
3102
  append_ref_member = func_info.get('append_ref_member')
2072
3103
 
2073
- if append_mode and append_ref_class:
3104
+ # v4.9.2: Don't execute original for builtin hooks - wrapper already does that
3105
+ is_builtin_hook = append_ref_class == '__builtins__'
3106
+ if append_mode and append_ref_class and not is_builtin_hook:
2074
3107
  self._execute_append_reference(
2075
3108
  None, append_ref_class, append_ref_member,
2076
3109
  args, kwargs, {}, is_constructor=False
2077
3110
  )
2078
3111
 
3112
+ # v4.7: Handle bytearrayed function modifier
3113
+ if 'bytearrayed' in modifiers:
3114
+ return self._execute_bytearrayed_function(func_node)
3115
+
2079
3116
  # v4.2.0: Handle raw_body with supports_language (multi-language support)
2080
3117
  raw_body = func_info.get('raw_body')
2081
3118
  supports_language = func_info.get('supports_language')
@@ -2498,16 +3535,92 @@ class CSSLRuntime:
2498
3535
  Supports multiple return values for shuffled functions:
2499
3536
  return a, b, c; // Returns tuple (a, b, c)
2500
3537
  """
2501
- if node.value is None:
2502
- raise CSSLReturn(None)
3538
+ if node.value is None:
3539
+ raise CSSLReturn(None)
3540
+
3541
+ # Check if this is a multiple return value
3542
+ if isinstance(node.value, dict) and node.value.get('multiple'):
3543
+ values = [self._evaluate(v) for v in node.value.get('values', [])]
3544
+ raise CSSLReturn(tuple(values))
3545
+
3546
+ value = self._evaluate(node.value)
3547
+ raise CSSLReturn(value)
3548
+
3549
+ def _exec_yield(self, node: ASTNode) -> Any:
3550
+ """Execute yield statement - v4.9.3
3551
+
3552
+ Yields a value from a generator function, pausing execution.
3553
+ The function resumes from here when next() is called on the generator.
3554
+
3555
+ Syntax:
3556
+ yield value; // Yield a value and pause
3557
+ yield; // Yield None and pause
3558
+
3559
+ Example:
3560
+ generator<int> define Range(int n) {
3561
+ int i = 0;
3562
+ while (i < n) {
3563
+ yield i;
3564
+ i = i + 1;
3565
+ }
3566
+ }
3567
+ """
3568
+ if node.value is None:
3569
+ raise CSSLYield(None)
3570
+
3571
+ value = self._evaluate(node.value)
3572
+ raise CSSLYield(value)
3573
+
3574
+ def _exec_await(self, node: ASTNode) -> Any:
3575
+ """Execute await expression - v4.9.3
3576
+
3577
+ Awaits a Future or async function result, blocking until complete.
3578
+
3579
+ Syntax:
3580
+ await future; // Wait for future to complete
3581
+ await asyncFunc(); // Wait for async function
3582
+ result = await future; // Capture result
3583
+
3584
+ Example:
3585
+ async define FetchData() {
3586
+ return http::get("url");
3587
+ }
3588
+ data = await FetchData();
3589
+ """
3590
+ value = self._evaluate(node.value)
2503
3591
 
2504
- # Check if this is a multiple return value
2505
- if isinstance(node.value, dict) and node.value.get('multiple'):
2506
- values = [self._evaluate(v) for v in node.value.get('values', [])]
2507
- raise CSSLReturn(tuple(values))
3592
+ # If it's a CSSLFuture, wait for it
3593
+ if isinstance(value, CSSLFuture):
3594
+ # Block until the future completes
3595
+ import time
3596
+ while value.state in (CSSLFuture.PENDING, CSSLFuture.RUNNING):
3597
+ time.sleep(0.001) # Small sleep to avoid busy-waiting
3598
+ if value.state == CSSLFuture.FAILED:
3599
+ raise CSSLRuntimeError(f"Awaited future failed: {value._exception}")
3600
+ if value.state == CSSLFuture.CANCELLED:
3601
+ raise CSSLRuntimeError("Awaited future was cancelled")
3602
+ return value.result()
3603
+
3604
+ # If it's an async function wrapper, execute it
3605
+ if isinstance(value, CSSLAsyncFunction):
3606
+ future = value.start()
3607
+ # Wait for completion
3608
+ import time
3609
+ while future.state in (CSSLFuture.PENDING, CSSLFuture.RUNNING):
3610
+ time.sleep(0.001)
3611
+ if future.state == CSSLFuture.FAILED:
3612
+ raise CSSLRuntimeError(f"Async function failed: {future._exception}")
3613
+ return future.result()
3614
+
3615
+ # If it's a generator, exhaust it and return last value
3616
+ if isinstance(value, CSSLGenerator):
3617
+ result = None
3618
+ while value.has_next():
3619
+ result = value.next()
3620
+ return result
2508
3621
 
2509
- value = self._evaluate(node.value)
2510
- raise CSSLReturn(value)
3622
+ # For regular values, just return them
3623
+ return value
2511
3624
 
2512
3625
  def _exec_break(self, node: ASTNode) -> Any:
2513
3626
  """Execute break statement"""
@@ -2534,6 +3647,76 @@ class CSSLRuntime:
2534
3647
  message = self._evaluate(node.value)
2535
3648
  raise CSSLThrow(message)
2536
3649
 
3650
+ def _exec_raise(self, node: ASTNode) -> Any:
3651
+ """Execute raise statement - v4.8 (Python-style exceptions)
3652
+
3653
+ Raises a Python-style exception that propagates to the nearest catch block.
3654
+
3655
+ Syntax:
3656
+ raise; # Re-raise current exception
3657
+ raise "Error message"; # Simple error message
3658
+ raise ValueError("message"); # Python exception type
3659
+ raise CustomError("msg", 123); # Custom exception with args
3660
+ """
3661
+ value = node.value
3662
+
3663
+ # Handle re-raise (raise;)
3664
+ if value is None or (isinstance(value, dict) and value.get('type') is None):
3665
+ raise CSSLThrow("Re-raised exception")
3666
+
3667
+ # Map of Python exception types
3668
+ EXCEPTION_MAP = {
3669
+ 'ValueError': ValueError,
3670
+ 'TypeError': TypeError,
3671
+ 'KeyError': KeyError,
3672
+ 'IndexError': IndexError,
3673
+ 'AttributeError': AttributeError,
3674
+ 'RuntimeError': RuntimeError,
3675
+ 'IOError': IOError,
3676
+ 'OSError': OSError,
3677
+ 'FileNotFoundError': FileNotFoundError,
3678
+ 'NameError': NameError,
3679
+ 'ZeroDivisionError': ZeroDivisionError,
3680
+ 'OverflowError': OverflowError,
3681
+ 'StopIteration': StopIteration,
3682
+ 'AssertionError': AssertionError,
3683
+ 'NotImplementedError': NotImplementedError,
3684
+ 'PermissionError': PermissionError,
3685
+ 'TimeoutError': TimeoutError,
3686
+ 'ConnectionError': ConnectionError,
3687
+ 'ImportError': ImportError,
3688
+ 'ModuleNotFoundError': ModuleNotFoundError,
3689
+ 'RecursionError': RecursionError,
3690
+ # Generic fallback
3691
+ 'Error': Exception,
3692
+ 'Exception': Exception,
3693
+ }
3694
+
3695
+ if isinstance(value, dict):
3696
+ exc_type_name = value.get('type', 'Error')
3697
+ args = value.get('args', [])
3698
+ message = value.get('message')
3699
+
3700
+ # Evaluate args if present
3701
+ if args:
3702
+ evaluated_args = [self._evaluate(arg) for arg in args]
3703
+ message_str = evaluated_args[0] if evaluated_args else "Error"
3704
+ elif message:
3705
+ message_str = self._evaluate(message)
3706
+ else:
3707
+ message_str = f"{exc_type_name}: Unknown error"
3708
+
3709
+ # Get the exception class
3710
+ exc_class = EXCEPTION_MAP.get(exc_type_name, Exception)
3711
+
3712
+ # Raise the Python exception
3713
+ # Wrap in CSSLThrow so it can be caught by CSSL try/catch
3714
+ raise CSSLThrow(f"{exc_type_name}: {message_str}", exc_class(message_str))
3715
+ else:
3716
+ # Simple message
3717
+ message = self._evaluate(value)
3718
+ raise CSSLThrow(str(message))
3719
+
2537
3720
  def _exec_constructor(self, node: ASTNode) -> Any:
2538
3721
  """Execute constructor node - only called when encountered directly.
2539
3722
 
@@ -2952,6 +4135,39 @@ class CSSLRuntime:
2952
4135
  result = result if result == filter_val else None
2953
4136
  elif isinstance(result, list):
2954
4137
  result = [item for item in result if isinstance(item, int) and item == filter_val]
4138
+ # v4.8.8: Integer comparison filters
4139
+ elif helper == 'gt': # Greater than
4140
+ if isinstance(result, int):
4141
+ result = result if result > filter_val else None
4142
+ elif isinstance(result, list):
4143
+ result = [item for item in result if isinstance(item, int) and item > filter_val]
4144
+ elif helper == 'lt': # Less than
4145
+ if isinstance(result, int):
4146
+ result = result if result < filter_val else None
4147
+ elif isinstance(result, list):
4148
+ result = [item for item in result if isinstance(item, int) and item < filter_val]
4149
+ elif helper == 'gte' or helper == 'ge': # Greater than or equal
4150
+ if isinstance(result, int):
4151
+ result = result if result >= filter_val else None
4152
+ elif isinstance(result, list):
4153
+ result = [item for item in result if isinstance(item, int) and item >= filter_val]
4154
+ elif helper == 'lte' or helper == 'le': # Less than or equal
4155
+ if isinstance(result, int):
4156
+ result = result if result <= filter_val else None
4157
+ elif isinstance(result, list):
4158
+ result = [item for item in result if isinstance(item, int) and item <= filter_val]
4159
+ elif helper == 'not': # Not equal
4160
+ if isinstance(result, int):
4161
+ result = result if result != filter_val else None
4162
+ elif isinstance(result, list):
4163
+ result = [item for item in result if isinstance(item, int) and item != filter_val]
4164
+ elif helper == 'range': # Range filter [min, max]
4165
+ if isinstance(filter_val, (list, tuple)) and len(filter_val) == 2:
4166
+ min_val, max_val = filter_val
4167
+ if isinstance(result, int):
4168
+ result = result if min_val <= result <= max_val else None
4169
+ elif isinstance(result, list):
4170
+ result = [item for item in result if isinstance(item, int) and min_val <= item <= max_val]
2955
4171
 
2956
4172
  # === JSON HELPERS ===
2957
4173
  elif filter_type == 'json':
@@ -3261,7 +4477,7 @@ class CSSLRuntime:
3261
4477
  Modes:
3262
4478
  - replace: target <== source (replace target with source)
3263
4479
  - add: target +<== source (copy & add to target)
3264
- - move: target -<== source (move from source, remove from source)
4480
+ - remove: target -<== source (remove items in source from target) [v4.9.2]
3265
4481
  """
3266
4482
  target = node.value.get('target')
3267
4483
  source_node = node.value.get('source')
@@ -3364,8 +4580,15 @@ class CSSLRuntime:
3364
4580
  elif isinstance(current_value, str) and isinstance(source, str):
3365
4581
  final_value = current_value + source
3366
4582
  # Handle CSSL container types (DataStruct, Vector, Stack, etc.)
3367
- elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add'):
3368
- if hasattr(current_value, 'append'):
4583
+ elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add') or hasattr(current_value, 'update'):
4584
+ # v4.9.2: Special handling for dict source into containers with update method
4585
+ if isinstance(source, dict) and hasattr(current_value, 'update'):
4586
+ current_value.update(source)
4587
+ elif isinstance(source, dict) and hasattr(current_value, '__setitem__'):
4588
+ # DataStruct, Map, etc. - merge dict key-value pairs
4589
+ for k, v in source.items():
4590
+ current_value[k] = v
4591
+ elif hasattr(current_value, 'append'):
3369
4592
  current_value.append(source)
3370
4593
  elif hasattr(current_value, 'push'):
3371
4594
  current_value.push(source)
@@ -3373,20 +4596,49 @@ class CSSLRuntime:
3373
4596
  current_value.add(source)
3374
4597
  final_value = current_value
3375
4598
  elif current_value is None:
3376
- final_value = [source] if not isinstance(source, list) else source
4599
+ # v4.9.2: Handle None target - wrap source appropriately
4600
+ if isinstance(source, dict):
4601
+ final_value = source # Keep dict as-is
4602
+ elif isinstance(source, list):
4603
+ final_value = source
4604
+ else:
4605
+ final_value = source # Keep single value as-is
3377
4606
  else:
3378
4607
  final_value = [current_value, source]
3379
4608
  elif mode == 'move':
3380
- # Move & remove from source
3381
- final_value = source
3382
- # Clear the source - handle all node types
3383
- if isinstance(source_node, ASTNode):
3384
- if source_node.type == 'identifier':
3385
- self.scope.set(source_node.value, None)
3386
- elif source_node.type == 'module_ref':
3387
- self._set_module_value(source_node.value, None)
3388
- elif source_node.type == 'member_access':
3389
- self._set_member(source_node, None)
4609
+ # v4.9.2: Changed semantics - remove items from target (not move from source)
4610
+ # target -<== source → remove items in source from target
4611
+ if isinstance(current_value, list):
4612
+ if isinstance(source, list):
4613
+ # Remove all items in source from current_value
4614
+ final_value = [item for item in current_value if item not in source]
4615
+ else:
4616
+ # Remove single item
4617
+ final_value = [item for item in current_value if item != source]
4618
+ elif isinstance(current_value, dict) and isinstance(source, (list, tuple)):
4619
+ # Remove keys from dict
4620
+ final_value = {k: v for k, v in current_value.items() if k not in source}
4621
+ elif isinstance(current_value, dict) and isinstance(source, dict):
4622
+ # Remove keys that exist in source dict
4623
+ final_value = {k: v for k, v in current_value.items() if k not in source}
4624
+ elif hasattr(current_value, 'remove') or hasattr(current_value, '__delitem__'):
4625
+ # CSSL container types
4626
+ if isinstance(source, (list, tuple)):
4627
+ for item in source:
4628
+ if hasattr(current_value, 'remove'):
4629
+ try:
4630
+ current_value.remove(item)
4631
+ except (ValueError, KeyError):
4632
+ pass
4633
+ elif hasattr(current_value, 'remove'):
4634
+ try:
4635
+ current_value.remove(source)
4636
+ except (ValueError, KeyError):
4637
+ pass
4638
+ final_value = current_value
4639
+ else:
4640
+ # Fallback - just return source (old behavior)
4641
+ final_value = source
3390
4642
  else:
3391
4643
  final_value = source
3392
4644
 
@@ -3403,6 +4655,15 @@ class CSSLRuntime:
3403
4655
  self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
3404
4656
  elif target.type == 'member_access':
3405
4657
  self._set_member(target, final_value)
4658
+ elif target.type == 'this_access':
4659
+ # v4.9.2: this->member <== value
4660
+ if self._current_instance is None:
4661
+ raise CSSLRuntimeError("'this' used outside of class method context")
4662
+ member = target.value.get('member')
4663
+ if hasattr(self._current_instance, 'set_member'):
4664
+ self._current_instance.set_member(member, final_value)
4665
+ else:
4666
+ setattr(self._current_instance, member, final_value)
3406
4667
  elif target.type == 'call':
3407
4668
  callee = target.value.get('callee')
3408
4669
  if isinstance(callee, ASTNode) and callee.type == 'member_access':
@@ -3452,8 +4713,28 @@ class CSSLRuntime:
3452
4713
  final_value = {**current_value, **source}
3453
4714
  elif isinstance(current_value, str) and isinstance(source, str):
3454
4715
  final_value = current_value + source
4716
+ # v4.9.2: Handle CSSL container types
4717
+ elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add') or hasattr(current_value, 'update'):
4718
+ if isinstance(source, dict) and hasattr(current_value, 'update'):
4719
+ current_value.update(source)
4720
+ elif isinstance(source, dict) and hasattr(current_value, '__setitem__'):
4721
+ for k, v in source.items():
4722
+ current_value[k] = v
4723
+ elif hasattr(current_value, 'append'):
4724
+ current_value.append(source)
4725
+ elif hasattr(current_value, 'push'):
4726
+ current_value.push(source)
4727
+ elif hasattr(current_value, 'add'):
4728
+ current_value.add(source)
4729
+ final_value = current_value
3455
4730
  elif current_value is None:
3456
- final_value = [source] if not isinstance(source, list) else source
4731
+ # v4.9.2: Keep source type when target is None
4732
+ if isinstance(source, dict):
4733
+ final_value = source
4734
+ elif isinstance(source, list):
4735
+ final_value = source
4736
+ else:
4737
+ final_value = source
3457
4738
  else:
3458
4739
  final_value = [current_value, source]
3459
4740
  elif mode == 'move':
@@ -3510,6 +4791,15 @@ class CSSLRuntime:
3510
4791
  self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
3511
4792
  elif target.type == 'member_access':
3512
4793
  self._set_member(target, final_value)
4794
+ elif target.type == 'this_access':
4795
+ # v4.9.2: source ==> this->member
4796
+ if self._current_instance is None:
4797
+ raise CSSLRuntimeError("'this' used outside of class method context")
4798
+ member = target.value.get('member')
4799
+ if hasattr(self._current_instance, 'set_member'):
4800
+ self._current_instance.set_member(member, final_value)
4801
+ else:
4802
+ setattr(self._current_instance, member, final_value)
3513
4803
  elif target.type == 'typed_declaration':
3514
4804
  # Handle typed target: source ==> datastruct<dynamic> Output
3515
4805
  var_name = target.value.get('name')
@@ -3599,6 +4889,26 @@ class CSSLRuntime:
3599
4889
  if not func_name or code_block is None:
3600
4890
  return None
3601
4891
 
4892
+ # v4.8.6: Check if target is actually a function or if it's just a variable
4893
+ # If it's a variable (not a function), fall back to value assignment with capture
4894
+ existing_func = self.scope.get(func_name)
4895
+ if existing_func is None:
4896
+ existing_func = self.global_scope.get(func_name)
4897
+ is_function = (isinstance(existing_func, ASTNode) and existing_func.type == 'function') or callable(existing_func)
4898
+
4899
+ if not is_function and target.type == 'identifier':
4900
+ # Target is a variable, not a function - do value capture assignment instead
4901
+ # This handles: savedVersion <<== { %version; }
4902
+ captured_values = self._scan_and_capture_refs(code_block)
4903
+ old_captured = self._current_captured_values.copy()
4904
+ self._current_captured_values = captured_values
4905
+ try:
4906
+ value = self._evaluate_action_block(code_block)
4907
+ finally:
4908
+ self._current_captured_values = old_captured
4909
+ self.scope.set(func_name, value)
4910
+ return value
4911
+
3602
4912
  if mode == 'add':
3603
4913
  # +<<== : Add code to function (both injection + original execute)
3604
4914
  self.register_function_injection(func_name, code_block)
@@ -3760,6 +5070,18 @@ class CSSLRuntime:
3760
5070
  name = target.value
3761
5071
  _live_objects[name] = value
3762
5072
  self.global_scope.set(f'${name}', SharedObjectProxy(name, value))
5073
+ elif target.type == 'captured_ref':
5074
+ # v4.8.9: %name = value - assign to snapshot directly
5075
+ # This allows: %xyz = othervar, %xyz = "hello", %xyz = (int number = 200)
5076
+ name = target.value
5077
+ self.builtins._snapshots[name] = value
5078
+ elif target.type == 'pointer_ref':
5079
+ # v4.9.0: ?name = value - create pointer to value
5080
+ # The pointer stores an address to the object
5081
+ name = target.value
5082
+ from .cssl_types import Address
5083
+ addr = Address(obj=value)
5084
+ self.scope.set(f'?{name}', addr)
3763
5085
  elif target.type == 'module_ref':
3764
5086
  # @Name = value - store in promoted globals (like global keyword)
3765
5087
  self._promoted_globals[target.value] = value
@@ -3823,21 +5145,6 @@ class CSSLRuntime:
3823
5145
  """Execute type instantiation as statement (e.g., vector<int>)"""
3824
5146
  return self._evaluate(node)
3825
5147
 
3826
- def _exec_await(self, node: ASTNode) -> Any:
3827
- """Execute await statement - waits for expression to complete"""
3828
- # Evaluate the awaited expression
3829
- # The expression is typically a call like wait_for_booted()
3830
- result = self._evaluate(node.value)
3831
-
3832
- # If result is a callable (like a coroutine or future), wait for it
3833
- if hasattr(result, '__await__'):
3834
- import asyncio
3835
- loop = asyncio.get_event_loop()
3836
- return loop.run_until_complete(result)
3837
-
3838
- # If result is a boolean condition waiting function, it already handled the waiting
3839
- return result
3840
-
3841
5148
  def _exec_then(self, node: ASTNode) -> Any:
3842
5149
  """Execute then block"""
3843
5150
  for child in node.children:
@@ -3892,6 +5199,24 @@ class CSSLRuntime:
3892
5199
  return {}
3893
5200
  return None
3894
5201
 
5202
+ # v4.9.0: Byte literal (x^y notation)
5203
+ if node.type == 'byte_literal':
5204
+ from .cssl_types import Byte
5205
+ base = node.value.get('base')
5206
+ weight = node.value.get('weight')
5207
+ return Byte(base, weight)
5208
+
5209
+ # v4.8.9: Typed expression (type name = value) - creates variable and returns value
5210
+ # Used for snapshot assignment: %xyz = (int number = 200)
5211
+ if node.type == 'typed_expression':
5212
+ var_type = node.value.get('type')
5213
+ var_name = node.value.get('name')
5214
+ var_value = self._evaluate(node.value.get('value'))
5215
+ # Create the typed variable in current scope
5216
+ self.scope.set(var_name, var_value)
5217
+ # Return the value so it can be used in assignment
5218
+ return var_value
5219
+
3895
5220
  if node.type == 'identifier':
3896
5221
  name = node.value
3897
5222
 
@@ -3901,16 +5226,34 @@ class CSSLRuntime:
3901
5226
  container_name = parts[0]
3902
5227
  member_name = parts[1]
3903
5228
 
3904
- # Look up the container (enum, class, or namespace)
5229
+ # Look up the container (enum, class, namespace, or module)
3905
5230
  container = self.scope.get(container_name)
3906
5231
  if container is None:
3907
5232
  container = self.global_scope.get(container_name)
3908
5233
  if container is None:
3909
5234
  container = self._promoted_globals.get(container_name)
5235
+ # v4.8: Also check modules for :: access (fmt::green, etc.)
5236
+ if container is None:
5237
+ container = self.get_module(container_name)
3910
5238
 
3911
5239
  if container is not None:
5240
+ # v4.8: Handle CSSLNamespace - supports arbitrarily deep nested access
5241
+ if isinstance(container, CSSLNamespace):
5242
+ # Handle nested :: access (e.g., ns::inner::deep::func)
5243
+ current = container
5244
+ remaining = member_name
5245
+ while '::' in remaining and isinstance(current, CSSLNamespace):
5246
+ parts = remaining.split('::', 1)
5247
+ current = current.get(parts[0])
5248
+ remaining = parts[1]
5249
+ if current is None:
5250
+ break
5251
+ # Final lookup
5252
+ if isinstance(current, CSSLNamespace):
5253
+ return current.get(remaining)
5254
+ return current if current is not None else None
3912
5255
  # If it's a dict-like object (enum or namespace), get the member
3913
- if isinstance(container, dict):
5256
+ elif isinstance(container, dict):
3914
5257
  return container.get(member_name)
3915
5258
  # If it's an object with the member as an attribute
3916
5259
  elif hasattr(container, member_name):
@@ -3923,6 +5266,11 @@ class CSSLRuntime:
3923
5266
  # Fall through to normal lookup if container not found
3924
5267
  return None
3925
5268
 
5269
+ # v4.9.2: When inside a hook execution, return original builtin to prevent recursion
5270
+ # This must be checked BEFORE scope lookup since the hook is stored in scope
5271
+ if name in self._hook_executing and name in self._original_functions:
5272
+ return self._original_functions[name]
5273
+
3926
5274
  value = self.scope.get(name)
3927
5275
  # Check if it's a class member in current instance context
3928
5276
  # This allows accessing members without 'this->' inside methods
@@ -4011,25 +5359,123 @@ class CSSLRuntime:
4011
5359
  if value is not None:
4012
5360
  return value
4013
5361
 
4014
- # 3. Fall back to scope/builtins if no original was captured
5362
+ # 3. v4.8.8: Check snapshots - %name accesses snapshotted values (BEFORE scope!)
5363
+ # This ensures snapshot(var); var = "new"; %var returns the OLD value
5364
+ if hasattr(self.builtins, '_snapshots') and name in self.builtins._snapshots:
5365
+ return self.builtins._snapshots[name]
5366
+
5367
+ # 4. v4.9.2: Only fall back to scope if we're in an injection context
5368
+ # (i.e., _current_captured_values is populated). For direct %name usage
5369
+ # outside injections, return null if no snapshot exists.
5370
+ if self._current_captured_values:
5371
+ # We're in an injection context - fall back to scope/builtins
5372
+ value = self.scope.get(name)
5373
+ if value is None:
5374
+ value = self.global_scope.get(name)
5375
+ if value is None:
5376
+ # For critical builtins like 'exit', create direct wrapper
5377
+ if name == 'exit':
5378
+ runtime = self
5379
+ value = lambda code=0, rt=runtime: rt.exit(code)
5380
+ else:
5381
+ value = getattr(self.builtins, f'builtin_{name}', None)
5382
+ if value is not None:
5383
+ return value
5384
+
5385
+ # Build helpful error for captured reference in injection context
5386
+ hint = f"Variable '{name}' must exist when the infusion is registered, or be snapshotted with snapshot({name}). Check that '%{name}' is defined before use."
5387
+ raise self._format_error(
5388
+ node.line if hasattr(node, 'line') else 0,
5389
+ f"Captured reference '%{name}' not found (no snapshot or captured value)",
5390
+ hint
5391
+ )
5392
+ else:
5393
+ # v4.9.2: Direct %name usage outside injection - no snapshot exists, return null
5394
+ return None
5395
+
5396
+ # v4.9.2: Local reference - local::<name> accesses hooked function's local variables/params
5397
+ if node.type == 'local_ref':
5398
+ name = node.value
5399
+ # Access the hook's local context (set by hook wrapper)
5400
+ if hasattr(self, '_hook_locals') and self._hook_locals:
5401
+ if name in self._hook_locals:
5402
+ return self._hook_locals[name]
5403
+ # Also check _result which is set automatically for append hooks
5404
+ if name == 'result' or name == '_result':
5405
+ result = self.scope.get('_result')
5406
+ if result is not None:
5407
+ return result
5408
+ # Fall back to current scope
4015
5409
  value = self.scope.get(name)
4016
- if value is None:
4017
- value = self.global_scope.get(name)
4018
- if value is None:
4019
- # For critical builtins like 'exit', create direct wrapper
4020
- if name == 'exit':
4021
- runtime = self
4022
- value = lambda code=0, rt=runtime: rt.exit(code)
4023
- else:
4024
- value = getattr(self.builtins, f'builtin_{name}', None)
4025
5410
  if value is not None:
4026
5411
  return value
4027
- # Build helpful error for captured reference
4028
- hint = f"Variable '{name}' must exist when the infusion is registered. Check that '%{name}' is defined before the <<== operator."
5412
+ return None
5413
+
5414
+ # v4.9.2: Local assignment - local::<name> = value
5415
+ if node.type == 'local_assign':
5416
+ name = node.value.get('name')
5417
+ value_node = node.value.get('value')
5418
+ value = self._evaluate(value_node)
5419
+ # Set in hook locals if available, otherwise scope
5420
+ if hasattr(self, '_hook_locals') and self._hook_locals is not None:
5421
+ self._hook_locals[name] = value
5422
+ else:
5423
+ self.scope.set(name, value)
5424
+ return value
5425
+
5426
+ # v4.9.2: Local injection - local::func -<<== {...} or local::func +<<== {...}
5427
+ if node.type == 'local_injection':
5428
+ local_name = node.value.get('local_name')
5429
+ mode = node.value.get('mode') # 'remove', 'add', 'replace'
5430
+ filters = node.value.get('filters', [])
5431
+ code_block = node.value.get('code')
5432
+ # This modifies the local function at runtime - advanced feature
5433
+ # For now, just store the injection intent
5434
+ if not hasattr(self, '_local_injections'):
5435
+ self._local_injections = {}
5436
+ self._local_injections[local_name] = {
5437
+ 'mode': mode,
5438
+ 'filters': filters,
5439
+ 'code': code_block
5440
+ }
5441
+ return None
5442
+
5443
+ # v4.9.0: Pointer reference - ?name can either:
5444
+ # 1. Dereference an existing pointer named ?name
5445
+ # 2. Create an Address to a variable named 'name' (v4.9.2)
5446
+ if node.type == 'pointer_ref':
5447
+ name = node.value
5448
+ # First check if ?name exists as a pointer in scope
5449
+ addr = self.scope.get(f'?{name}')
5450
+ if addr is None:
5451
+ addr = self.global_scope.get(f'?{name}')
5452
+
5453
+ if addr is not None:
5454
+ # Dereference the existing pointer
5455
+ if isinstance(addr, Address):
5456
+ return addr.reflect()
5457
+ # If it's a direct object (simple pointer), return it
5458
+ return addr
5459
+
5460
+ # v4.9.2: No pointer exists - check if 'name' is a variable
5461
+ # If so, create an Address pointing to it (like &name in C)
5462
+ # v4.9.3: If the variable IS an Address, dereference it instead
5463
+ var_value = self.scope.get(name)
5464
+ if var_value is None:
5465
+ var_value = self.global_scope.get(name)
5466
+
5467
+ if var_value is not None:
5468
+ # v4.9.3: If var is already an Address, dereference it
5469
+ if isinstance(var_value, Address):
5470
+ return var_value.reflect()
5471
+ # Otherwise create an Address to this variable
5472
+ return Address(obj=var_value)
5473
+
5474
+ # Neither pointer nor variable exists
4029
5475
  raise self._format_error(
4030
5476
  node.line if hasattr(node, 'line') else 0,
4031
- f"Captured reference '%{name}' not found",
4032
- hint
5477
+ f"Cannot resolve '?{name}': no pointer '?{name}' or variable '{name}' found",
5478
+ f"Either create a pointer: ?{name} = someObject, or declare variable: {name} = value"
4033
5479
  )
4034
5480
 
4035
5481
  if node.type == 'instance_ref':
@@ -4187,6 +5633,11 @@ class CSSLRuntime:
4187
5633
  value = self._evaluate(value_node)
4188
5634
  m.insert(key, value)
4189
5635
  return m
5636
+ elif type_name == 'queue':
5637
+ # v4.7: Create Queue with element_type and size
5638
+ queue_size = node.value.get('queue_size', 'dynamic')
5639
+ q = Queue(element_type, queue_size)
5640
+ return _populate_container(q, init_values)
4190
5641
  else:
4191
5642
  return None
4192
5643
 
@@ -4196,6 +5647,10 @@ class CSSLRuntime:
4196
5647
  if node.type == 'unary':
4197
5648
  return self._eval_unary(node)
4198
5649
 
5650
+ # v4.9.3: await expression
5651
+ if node.type == 'await':
5652
+ return self._exec_await(node)
5653
+
4199
5654
  # Increment: ++i or i++
4200
5655
  if node.type == 'increment':
4201
5656
  return self._eval_increment(node)
@@ -4205,26 +5660,13 @@ class CSSLRuntime:
4205
5660
  return self._eval_decrement(node)
4206
5661
 
4207
5662
  if node.type == 'non_null_assert':
4208
- # *$var, *@module, *identifier - assert value is not null/None
5663
+ # *$var, *@module, *identifier - safe access, returns 0 if null
5664
+ # v4.9.3: Changed from error to safe default (0) for null values
4209
5665
  operand = node.value.get('operand')
4210
5666
  value = self._evaluate(operand)
4211
5667
  if value is None:
4212
- # Get name of the operand for better error message
4213
- operand_name = "unknown"
4214
- if isinstance(operand, ASTNode):
4215
- if operand.type == 'identifier':
4216
- operand_name = operand.value
4217
- elif operand.type == 'shared_ref':
4218
- operand_name = f"${operand.value}"
4219
- elif operand.type == 'module_ref':
4220
- operand_name = f"@{operand.value}"
4221
- elif operand.type == 'global_ref':
4222
- operand_name = f"r@{operand.value}"
4223
- raise self._format_error(
4224
- node.line if hasattr(node, 'line') else 0,
4225
- f"Non-null assertion failed: '{operand_name}' is null/None",
4226
- f"The value accessed via '*{operand_name}' must not be null. Check that it is defined and initialized."
4227
- )
5668
+ # Return 0 as safe default instead of throwing error
5669
+ return 0
4228
5670
  return value
4229
5671
 
4230
5672
  if node.type == 'type_exclude_assert':
@@ -4272,6 +5714,10 @@ class CSSLRuntime:
4272
5714
  if node.type == 'array':
4273
5715
  return [self._evaluate(elem) for elem in node.value]
4274
5716
 
5717
+ # v4.9.2: Tuple literals (0, 10) or (a, b, c)
5718
+ if node.type == 'tuple':
5719
+ return tuple(self._evaluate(elem) for elem in node.value)
5720
+
4275
5721
  if node.type == 'object':
4276
5722
  return {k: self._evaluate(v) for k, v in node.value.items()}
4277
5723
 
@@ -4396,23 +5842,76 @@ class CSSLRuntime:
4396
5842
  if op == 'or' or op == '||':
4397
5843
  return left or right
4398
5844
 
4399
- # === BITWISE OPERATIONS ===
5845
+ # === BITWISE OPERATIONS (or stream/pipe operations) ===
4400
5846
  if op == '&':
4401
5847
  return int(left or 0) & int(right or 0)
4402
5848
  if op == '|':
5849
+ # v4.8.4: Pipe operator support
5850
+ from .cssl_types import Pipe, OutputStream, InputStream, FileStream
5851
+ if isinstance(left, Pipe):
5852
+ if callable(right):
5853
+ return left | right
5854
+ return Pipe(left._data)
5855
+ # If right is a Pipe transform function
5856
+ if callable(right) and hasattr(right, '__name__') and 'pipe' in str(type(right)):
5857
+ return Pipe(left) | right
5858
+ # Bitwise OR for integers
4403
5859
  return int(left or 0) | int(right or 0)
4404
5860
  if op == '^':
4405
5861
  return int(left or 0) ^ int(right or 0)
4406
5862
  if op == '<<':
5863
+ # v4.8.4: Stream output operator support
5864
+ from .cssl_types import OutputStream, FileStream
5865
+ if isinstance(left, (OutputStream, FileStream)):
5866
+ # Handle manipulators
5867
+ if isinstance(right, dict) and right.get('type') == 'manipulator':
5868
+ name = right.get('name')
5869
+ value = right.get('value')
5870
+ if name == 'setprecision':
5871
+ left.setprecision(value)
5872
+ elif name == 'setw':
5873
+ left.setw(value)
5874
+ elif name == 'setfill':
5875
+ left.setfill(value)
5876
+ elif name == 'fixed':
5877
+ left.fixed()
5878
+ elif name == 'scientific':
5879
+ left.scientific()
5880
+ return left
5881
+ # Stream output
5882
+ return left.write(right)
5883
+ # Bitwise left shift for integers
4407
5884
  return int(left or 0) << int(right or 0)
4408
5885
  if op == '>>':
5886
+ # v4.8.4: Stream input operator support
5887
+ from .cssl_types import InputStream, FileStream
5888
+ if isinstance(left, (InputStream, FileStream)):
5889
+ # Read from stream into target type
5890
+ if isinstance(right, type):
5891
+ return left.read(right)
5892
+ return left.read(str)
5893
+ # Bitwise right shift for integers
4409
5894
  return int(left or 0) >> int(right or 0)
4410
5895
 
4411
- # === IN OPERATOR ===
5896
+ # === IN OPERATOR (v4.8.4: C++ optimized with unative fallback) ===
4412
5897
  if op == 'in':
4413
5898
  if right is None:
4414
5899
  return False
4415
- return left in right
5900
+ # Check if unative mode is requested (use Python's native 'in')
5901
+ use_native = node.value.get('unative', False)
5902
+ if use_native:
5903
+ return left in right
5904
+ # C++ optimized containment check
5905
+ return self._contains_fast(right, left)
5906
+
5907
+ # === NOT IN OPERATOR ===
5908
+ if op == 'not in' or op == '!in' or op == 'notin':
5909
+ if right is None:
5910
+ return True
5911
+ use_native = node.value.get('unative', False)
5912
+ if use_native:
5913
+ return left not in right
5914
+ return not self._contains_fast(right, left)
4416
5915
 
4417
5916
  return None
4418
5917
 
@@ -4473,7 +5972,7 @@ class CSSLRuntime:
4473
5972
  elif l > r:
4474
5973
  return 1
4475
5974
  return 0
4476
- except:
5975
+ except (ValueError, TypeError):
4477
5976
  # Fallback to string comparison
4478
5977
  l_str = str(left)
4479
5978
  r_str = str(right)
@@ -4483,6 +5982,61 @@ class CSSLRuntime:
4483
5982
  return 1
4484
5983
  return 0
4485
5984
 
5985
+ def _contains_fast(self, container: Any, item: Any) -> bool:
5986
+ """C++ optimized containment check (v4.8.4).
5987
+
5988
+ Uses optimized algorithms based on container type:
5989
+ - Hash-based (dict, set): O(1) lookup
5990
+ - Sorted data: Binary search O(log n)
5991
+ - Linear: O(n) with early termination
5992
+ """
5993
+ from .cssl_types import Vector, Array, List, Dictionary, Map, DataStruct
5994
+
5995
+ # Hash-based containers - O(1)
5996
+ if isinstance(container, (dict, set, frozenset)):
5997
+ return item in container
5998
+ if isinstance(container, (Dictionary, Map)):
5999
+ if hasattr(container, 'contains'):
6000
+ return container.contains(item)
6001
+ return item in container
6002
+
6003
+ # For list-like containers, check if sorted and use binary search
6004
+ if isinstance(container, (list, tuple, Vector, Array, List, DataStruct)):
6005
+ data = list(container) if not isinstance(container, list) else container
6006
+
6007
+ # For larger collections, check if sorted and use binary search
6008
+ if len(data) > 10:
6009
+ # Quick sorted check on sample
6010
+ sample_size = min(10, len(data))
6011
+ step = max(1, len(data) // sample_size)
6012
+ sample = [data[i] for i in range(0, len(data), step)][:sample_size]
6013
+
6014
+ try:
6015
+ is_sorted = all(sample[i] <= sample[i+1] for i in range(len(sample)-1))
6016
+ if is_sorted:
6017
+ # Binary search for sorted data
6018
+ import bisect
6019
+ idx = bisect.bisect_left(data, item)
6020
+ return idx < len(data) and data[idx] == item
6021
+ except (TypeError, AttributeError):
6022
+ pass # Non-comparable items, fall through to linear
6023
+
6024
+ # Linear search with early termination
6025
+ for x in data:
6026
+ if x == item:
6027
+ return True
6028
+ return False
6029
+
6030
+ # String containment - use native (already optimized in Python)
6031
+ if isinstance(container, str):
6032
+ return str(item) in container
6033
+
6034
+ # Generic fallback
6035
+ try:
6036
+ return item in container
6037
+ except TypeError:
6038
+ return False
6039
+
4486
6040
  def _eval_unary(self, node: ASTNode) -> Any:
4487
6041
  """Evaluate unary operation"""
4488
6042
  op = node.value.get('op')
@@ -4572,6 +6126,27 @@ class CSSLRuntime:
4572
6126
  kwargs_raw = node.value.get('kwargs', {})
4573
6127
  kwargs = {k: self._evaluate(v) for k, v in kwargs_raw.items()} if kwargs_raw else {}
4574
6128
 
6129
+ # v4.9.3: Handle ?FuncName() - call function and handle pointer result
6130
+ if isinstance(callee_node, ASTNode) and callee_node.type == 'pointer_ref':
6131
+ from .cssl_types import Address
6132
+ func_name = callee_node.value
6133
+ # Check if this is a function (not a variable)
6134
+ func = self.scope.get(func_name) or self.global_scope.get(func_name)
6135
+ if func is None:
6136
+ func = self.builtins.get(func_name)
6137
+
6138
+ # If it's a function, call it
6139
+ if callable(func) or (isinstance(func, ASTNode) and func.type == 'function'):
6140
+ if callable(func):
6141
+ result = func(*args, **kwargs) if kwargs else func(*args)
6142
+ else:
6143
+ result = self._call_function(func, args, kwargs)
6144
+ # If result is already an Address, dereference it
6145
+ if isinstance(result, Address):
6146
+ return result.reflect()
6147
+ # Otherwise return pointer to the result
6148
+ return Address(obj=result)
6149
+
4575
6150
  # Get function name for injection check FIRST (before evaluating callee)
4576
6151
  func_name = None
4577
6152
  if isinstance(callee_node, ASTNode):
@@ -4712,6 +6287,50 @@ class CSSLRuntime:
4712
6287
 
4713
6288
  return None
4714
6289
 
6290
+ # v4.9.2: Handle cast<type>(value) - type casting
6291
+ if name == 'cast':
6292
+ if not args:
6293
+ raise CSSLRuntimeError(
6294
+ "cast<type>(value) requires a value argument",
6295
+ node.line,
6296
+ hint="Usage: cast<int>(3.14) or cast<string>(42)"
6297
+ )
6298
+ value = args[0]
6299
+ target_type = type_param.lower()
6300
+
6301
+ try:
6302
+ if target_type in ('int', 'integer'):
6303
+ return int(value)
6304
+ elif target_type in ('float', 'double'):
6305
+ return float(value)
6306
+ elif target_type in ('string', 'str'):
6307
+ return str(value)
6308
+ elif target_type in ('bool', 'boolean'):
6309
+ if isinstance(value, str):
6310
+ return value.lower() not in ('', '0', 'false', 'no', 'null', 'none')
6311
+ return bool(value)
6312
+ elif target_type in ('list', 'array'):
6313
+ if isinstance(value, (list, tuple)):
6314
+ return list(value)
6315
+ return [value]
6316
+ elif target_type in ('dict', 'json'):
6317
+ if isinstance(value, dict):
6318
+ return value
6319
+ return {'value': value}
6320
+ elif target_type == 'dynamic':
6321
+ return value # No conversion
6322
+ else:
6323
+ raise CSSLRuntimeError(
6324
+ f"Unknown cast type: {target_type}",
6325
+ node.line,
6326
+ hint="Supported types: int, float, string, bool, list, dict, dynamic"
6327
+ )
6328
+ except (ValueError, TypeError) as e:
6329
+ raise CSSLRuntimeError(
6330
+ f"Cannot cast {type(value).__name__} to {target_type}: {e}",
6331
+ node.line
6332
+ )
6333
+
4715
6334
  # Fallback: call as regular function with type hint
4716
6335
  func = self.builtins.get_function(name)
4717
6336
  if func and callable(func):
@@ -4720,8 +6339,8 @@ class CSSLRuntime:
4720
6339
  raise CSSLRuntimeError(
4721
6340
  f"Unknown typed function: {name}<{type_param}>",
4722
6341
  node.line,
4723
- context=f"Available typed functions: OpenFind<type>, OpenFind<type, \"name\">",
4724
- hint="Use OpenFind<type>(index) for positional or OpenFind<type, \"name\"> for named params"
6342
+ context=f"Available typed functions: OpenFind<type>, cast<type>",
6343
+ hint="Use cast<int>(value), cast<string>(value), etc."
4725
6344
  )
4726
6345
 
4727
6346
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
@@ -4744,12 +6363,15 @@ class CSSLRuntime:
4744
6363
 
4745
6364
  # v4.2.6: Handle Namespace::ClassName lookup
4746
6365
  if namespace:
4747
- # Look up namespace dict first
4748
- ns_dict = self.scope.get(namespace)
4749
- if ns_dict is None:
4750
- ns_dict = self.global_scope.get(namespace)
4751
- if isinstance(ns_dict, dict) and class_name in ns_dict:
4752
- class_def = ns_dict[class_name]
6366
+ # Look up namespace first
6367
+ ns_obj = self.scope.get(namespace)
6368
+ if ns_obj is None:
6369
+ ns_obj = self.global_scope.get(namespace)
6370
+ # v4.8: Handle CSSLNamespace objects
6371
+ if isinstance(ns_obj, CSSLNamespace):
6372
+ class_def = ns_obj.classes.get(class_name)
6373
+ elif isinstance(ns_obj, dict) and class_name in ns_obj:
6374
+ class_def = ns_obj[class_name]
4753
6375
 
4754
6376
  # Get class definition from scope (if not found in namespace)
4755
6377
  if class_def is None:
@@ -4851,13 +6473,41 @@ class CSSLRuntime:
4851
6473
  self._call_parent_constructor(instance, evaluated_extends_args)
4852
6474
  instance._parent_constructor_called = True
4853
6475
 
4854
- # Execute all constructors defined with 'constr' keyword (in order)
6476
+ # v4.8.8: Separate constructors by modifier type
6477
+ regular_constructors = []
6478
+ secure_constructors = []
6479
+ callable_constructors = []
6480
+
4855
6481
  for constr in constructors:
4856
- self._call_constructor(instance, constr, args, kwargs, param_values)
6482
+ constr_info = constr.value
6483
+ if constr_info.get('is_secure'):
6484
+ secure_constructors.append(constr)
6485
+ elif constr_info.get('is_callable'):
6486
+ callable_constructors.append(constr)
6487
+ else:
6488
+ regular_constructors.append(constr)
6489
+
6490
+ # Store callable constructors on instance for manual invocation
6491
+ instance._callable_constructors = callable_constructors
6492
+ instance._secure_constructors = secure_constructors
4857
6493
 
4858
- # Call primary constructor (old-style) if defined
4859
- if class_def.constructor:
4860
- self._call_method(instance, class_def.constructor, args, kwargs)
6494
+ # Execute regular constructors (not callable, not secure)
6495
+ # Wrap in try/except to call secure constructors on exception
6496
+ try:
6497
+ for constr in regular_constructors:
6498
+ self._call_constructor(instance, constr, args, kwargs, param_values)
6499
+
6500
+ # Call primary constructor (old-style) if defined
6501
+ if class_def.constructor:
6502
+ self._call_method(instance, class_def.constructor, args, kwargs)
6503
+ except Exception as e:
6504
+ # v4.8.8: Call all secure constructors on exception
6505
+ for secure_constr in secure_constructors:
6506
+ try:
6507
+ self._call_constructor(instance, secure_constr, args, kwargs, param_values)
6508
+ except:
6509
+ pass # Secure constructor failed, continue with others
6510
+ raise # Re-raise the original exception
4861
6511
 
4862
6512
  return instance
4863
6513
 
@@ -4918,6 +6568,11 @@ class CSSLRuntime:
4918
6568
  """
4919
6569
  from .cssl_builtins import CSSLizedPythonObject
4920
6570
 
6571
+ # v4.9.2: Handle &builtinName ++ syntax (stored as __builtins__::builtinName)
6572
+ if ref_class == '__builtins__' and ref_member:
6573
+ ref_class = ref_member
6574
+ ref_member = None
6575
+
4921
6576
  # Handle direct function reference: &FunctionName ++ (no ::member part)
4922
6577
  if ref_member is None and not is_constructor:
4923
6578
  # ref_class is actually a function name
@@ -4925,8 +6580,20 @@ class CSSLRuntime:
4925
6580
  if func_name.startswith('$'):
4926
6581
  func_name = func_name[1:]
4927
6582
 
4928
- # Look for the function in scope
4929
- func = self.scope.get(func_name) or self.global_scope.get(func_name)
6583
+ # v4.9.2: For builtin hooks, check _original_functions FIRST to avoid infinite recursion
6584
+ # When &builtin ++ is used, scope contains the wrapper, but we want the original
6585
+ func = None
6586
+ if hasattr(self, '_original_functions'):
6587
+ func = self._original_functions.get(func_name)
6588
+
6589
+ # If not an overwritten builtin, check scope
6590
+ if func is None:
6591
+ func = self.scope.get(func_name) or self.global_scope.get(func_name)
6592
+
6593
+ # v4.9.2: Check in builtins._functions if still not found (for non-overwritten builtins)
6594
+ if func is None and hasattr(self, 'builtins') and hasattr(self.builtins, '_functions'):
6595
+ func = self.builtins._functions.get(func_name)
6596
+
4930
6597
  if func is not None:
4931
6598
  if isinstance(func, ASTNode) and func.type in ('function', 'FUNCTION'):
4932
6599
  # Execute the referenced function
@@ -5077,6 +6744,14 @@ class CSSLRuntime:
5077
6744
  prev_scope = self.scope
5078
6745
  self.scope = new_scope
5079
6746
 
6747
+ # v4.8.8: Set 'this' and 'super' in constructor scope
6748
+ new_scope.set('this', instance)
6749
+ parent_class = getattr(instance, '_parent_class', None)
6750
+ if parent_class is None and hasattr(instance, '_class'):
6751
+ parent_class = getattr(instance._class, 'parent', None)
6752
+ if parent_class:
6753
+ new_scope.set('super', SuperProxy(instance, parent_class, self))
6754
+
5080
6755
  try:
5081
6756
  for stmt in constr_node.children:
5082
6757
  self._execute_node(stmt)
@@ -5084,6 +6759,41 @@ class CSSLRuntime:
5084
6759
  self.scope = prev_scope
5085
6760
  self._current_instance = prev_instance
5086
6761
 
6762
+ def _call_destructor(self, instance: CSSLInstance, destr_node: ASTNode):
6763
+ """v4.8.8: Call a destructor on an instance.
6764
+
6765
+ Destructors are defined with constr ~Name() { } syntax.
6766
+ They are called by delete(instance) or delete(instance, "Name").
6767
+
6768
+ Args:
6769
+ instance: The CSSLInstance to clean up
6770
+ destr_node: The destructor AST node
6771
+ """
6772
+ # Save previous instance context
6773
+ prev_instance = self._current_instance
6774
+ self._current_instance = instance
6775
+
6776
+ # Create new scope for destructor
6777
+ new_scope = Scope(parent=self.scope)
6778
+ prev_scope = self.scope
6779
+ self.scope = new_scope
6780
+
6781
+ # v4.8.8: Set 'this' and 'super' in destructor scope
6782
+ new_scope.set('this', instance)
6783
+ parent_class = getattr(instance, '_parent_class', None)
6784
+ if parent_class is None and hasattr(instance, '_class'):
6785
+ parent_class = getattr(instance._class, 'parent', None)
6786
+ if parent_class:
6787
+ new_scope.set('super', SuperProxy(instance, parent_class, self))
6788
+
6789
+ try:
6790
+ # Execute destructor body
6791
+ for child in destr_node.children:
6792
+ self._execute_node(child)
6793
+ finally:
6794
+ self.scope = prev_scope
6795
+ self._current_instance = prev_instance
6796
+
5087
6797
  def _eval_this_access(self, node: ASTNode) -> Any:
5088
6798
  """Evaluate 'this->member' access.
5089
6799
 
@@ -5182,6 +6892,16 @@ class CSSLRuntime:
5182
6892
  # Check for undefined modifier
5183
6893
  is_undefined = 'undefined' in modifiers
5184
6894
 
6895
+ # v4.7: Handle bytearrayed modifier for methods
6896
+ if 'bytearrayed' in modifiers:
6897
+ # Set up instance context for the bytearrayed execution
6898
+ old_instance = self._current_instance
6899
+ self._current_instance = instance
6900
+ try:
6901
+ return self._execute_bytearrayed_function(method_node)
6902
+ finally:
6903
+ self._current_instance = old_instance
6904
+
5185
6905
  # Create new scope for method
5186
6906
  new_scope = Scope(parent=self.scope)
5187
6907
 
@@ -5203,6 +6923,14 @@ class CSSLRuntime:
5203
6923
  # Set up method context
5204
6924
  self.scope = new_scope
5205
6925
  self._current_instance = instance
6926
+ # v4.7: Set 'this' in scope for this.member access
6927
+ new_scope.set('this', instance)
6928
+ # v4.8.8: Set 'super' for parent method access
6929
+ parent_class = getattr(instance, '_parent_class', None)
6930
+ if parent_class is None and hasattr(instance, '_class'):
6931
+ parent_class = getattr(instance._class, 'parent', None)
6932
+ if parent_class:
6933
+ new_scope.set('super', SuperProxy(instance, parent_class, self))
5206
6934
 
5207
6935
  original_return = None
5208
6936
  try:
@@ -5240,14 +6968,103 @@ class CSSLRuntime:
5240
6968
  # If no return in appended code, use original's return
5241
6969
  return original_return
5242
6970
 
6971
+ def _call_method_with_super(self, instance: CSSLInstance, method_node: ASTNode,
6972
+ args: list, kwargs: dict, super_parent_class) -> Any:
6973
+ """v4.8.8: Call a method with a specific super context.
6974
+
6975
+ Used when calling parent methods via super->method() to ensure the super
6976
+ inside the parent method points to the grandparent, not the instance's parent.
6977
+
6978
+ Args:
6979
+ instance: The CSSLInstance to use as 'this'
6980
+ method_node: The method AST node to execute
6981
+ args: Method arguments
6982
+ kwargs: Keyword arguments
6983
+ super_parent_class: The class to use for 'super' (typically grandparent)
6984
+ """
6985
+ kwargs = kwargs or {}
6986
+ func_info = method_node.value
6987
+ params = func_info.get('params', [])
6988
+ modifiers = func_info.get('modifiers', [])
6989
+
6990
+ # Check for undefined modifier
6991
+ is_undefined = 'undefined' in modifiers
6992
+
6993
+ # Create new scope for method
6994
+ new_scope = Scope(parent=self.scope)
6995
+
6996
+ # Bind parameters
6997
+ for i, param in enumerate(params):
6998
+ param_name = param['name'] if isinstance(param, dict) else param
6999
+ if param_name in kwargs:
7000
+ new_scope.set(param_name, kwargs[param_name])
7001
+ elif i < len(args):
7002
+ new_scope.set(param_name, args[i])
7003
+ else:
7004
+ new_scope.set(param_name, None)
7005
+
7006
+ # Save current state
7007
+ old_scope = self.scope
7008
+ old_instance = self._current_instance
7009
+
7010
+ # Set up method context
7011
+ self.scope = new_scope
7012
+ self._current_instance = instance
7013
+ # Set 'this' in scope for this.member access
7014
+ new_scope.set('this', instance)
7015
+ # Set 'super' with the specified parent class (typically grandparent)
7016
+ if super_parent_class:
7017
+ new_scope.set('super', SuperProxy(instance, super_parent_class, self))
7018
+
7019
+ try:
7020
+ for child in method_node.children:
7021
+ if not self._running:
7022
+ break
7023
+ self._execute_node(child)
7024
+ except CSSLReturn as ret:
7025
+ return ret.value
7026
+ except Exception as e:
7027
+ if is_undefined:
7028
+ return None
7029
+ raise
7030
+ finally:
7031
+ # Restore previous state
7032
+ self.scope = old_scope
7033
+ self._current_instance = old_instance
7034
+
7035
+ return None
7036
+
7037
+ # v4.7.1: Attribute blacklist for security - prevents access to dangerous Python attributes
7038
+ ATTR_BLACKLIST = frozenset({
7039
+ '__class__', '__dict__', '__bases__', '__mro__', '__subclasses__',
7040
+ '__import__', '__builtins__', '__globals__', '__locals__',
7041
+ '__code__', '__call__', '__delattr__', '__setattr__', '__getattribute__',
7042
+ '__reduce__', '__reduce_ex__', '__init_subclass__', '__new__',
7043
+ '__module__', '__qualname__', '__annotations__', '__slots__',
7044
+ '__weakref__', '__func__', '__self__', '__wrapped__',
7045
+ })
7046
+
5243
7047
  def _eval_member_access(self, node: ASTNode) -> Any:
5244
7048
  """Evaluate member access"""
5245
7049
  obj = self._evaluate(node.value.get('object'))
5246
7050
  member = node.value.get('member')
5247
7051
 
7052
+ # v4.9.3: Special handling for null checks - null.is_null() returns true
5248
7053
  if obj is None:
7054
+ if member == 'is_null':
7055
+ return lambda: True
5249
7056
  return None
5250
7057
 
7058
+ # v4.7.1: Security - block access to dangerous Python attributes
7059
+ if member in self.ATTR_BLACKLIST:
7060
+ raise CSSLRuntimeError(
7061
+ f"Access to '{member}' is not allowed for security reasons."
7062
+ )
7063
+ if member.startswith('__') and member.endswith('__'):
7064
+ raise CSSLRuntimeError(
7065
+ f"Access to dunder attributes ('{member}') is not allowed."
7066
+ )
7067
+
5251
7068
  # Special handling for Parameter.return() -> Parameter.return_()
5252
7069
  # since 'return' is a Python keyword
5253
7070
  if isinstance(obj, Parameter) and member == 'return':
@@ -5255,28 +7072,39 @@ class CSSLRuntime:
5255
7072
 
5256
7073
  # === ServiceDefinition (from include()) ===
5257
7074
  if isinstance(obj, ServiceDefinition):
5258
- # Check functions dict first
7075
+ # v4.8.6: Check classes first (for new MyClass() pattern)
7076
+ if member in obj.classes:
7077
+ return obj.classes[member]
7078
+ # Check functions
5259
7079
  if member in obj.functions:
5260
7080
  func_node = obj.functions[member]
5261
7081
  return lambda *args, **kwargs: self._call_function(func_node, list(args), kwargs)
5262
- # Check structs dict
7082
+ # Check structs
5263
7083
  if member in obj.structs:
5264
7084
  return obj.structs[member]
7085
+ # v4.8.6: Check enums
7086
+ if member in obj.enums:
7087
+ return obj.enums[member]
7088
+ # v4.8.6: Check namespaces
7089
+ if member in obj.namespaces:
7090
+ return obj.namespaces[member]
5265
7091
  # Check regular attributes
5266
7092
  if hasattr(obj, member):
5267
7093
  return getattr(obj, member)
5268
7094
  # Build helpful error
5269
- available = list(obj.functions.keys()) + list(obj.structs.keys())
7095
+ available = (list(obj.classes.keys()) + list(obj.functions.keys()) +
7096
+ list(obj.structs.keys()) + list(obj.enums.keys()) +
7097
+ list(obj.namespaces.keys()))
5270
7098
  similar = _find_similar_names(member, available)
5271
7099
  if similar:
5272
7100
  hint = f"Did you mean: {', '.join(similar)}?"
5273
7101
  elif available:
5274
7102
  hint = f"Available: {', '.join(available[:10])}"
5275
7103
  else:
5276
- hint = "No functions or structs defined in this module."
7104
+ hint = "No exports defined in this module."
5277
7105
  raise self._format_error(
5278
7106
  node.line if hasattr(node, 'line') else 0,
5279
- f"Module has no function or struct '{member}'",
7107
+ f"Module has no export '{member}'",
5280
7108
  hint
5281
7109
  )
5282
7110
 
@@ -5313,6 +7141,35 @@ class CSSLRuntime:
5313
7141
  hint
5314
7142
  )
5315
7143
 
7144
+ # === v4.8.8: SUPER PROXY - Parent class method access ===
7145
+ if isinstance(obj, SuperProxy):
7146
+ # Get method from parent class
7147
+ method_node = obj.get_method(member)
7148
+ if method_node:
7149
+ # Return a callable that invokes the parent method with correct super context
7150
+ # Key: super inside the parent method should point to grandparent!
7151
+ parent_of_parent = getattr(obj._parent_class, 'parent', None)
7152
+
7153
+ def call_parent_method(*args, **kwargs):
7154
+ return self._call_method_with_super(
7155
+ obj._instance, method_node, list(args), kwargs,
7156
+ parent_of_parent # Pass grandparent as the super context
7157
+ )
7158
+ return call_parent_method
7159
+
7160
+ # Check for member
7161
+ member_val = obj.get_member(member)
7162
+ if member_val is not None:
7163
+ return member_val
7164
+
7165
+ # Error: no such method in parent
7166
+ parent_name = obj._parent_class.name if obj._parent_class else "parent"
7167
+ raise self._format_error(
7168
+ node.line if hasattr(node, 'line') else 0,
7169
+ f"Parent class '{parent_name}' has no method '{member}'",
7170
+ "Check that the parent class defines this method."
7171
+ )
7172
+
5316
7173
  # === UNIVERSAL INSTANCE METHODS ===
5317
7174
  from .cssl_types import UniversalInstance
5318
7175
  if isinstance(obj, UniversalInstance):
@@ -5362,11 +7219,28 @@ class CSSLRuntime:
5362
7219
  return string_methods
5363
7220
 
5364
7221
  # === LIST/ARRAY METHODS for plain lists ===
5365
- if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array)):
7222
+ # Exclude DataStruct and other custom containers - they have their own methods
7223
+ from .cssl_types import DataStruct, List, Dictionary, Queue, Bit, Byte, Address
7224
+ if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array, DataStruct, List)):
5366
7225
  list_methods = self._get_list_method(obj, member)
5367
7226
  if list_methods is not None:
5368
7227
  return list_methods
5369
7228
 
7229
+ # === CSSL CONTAINER TYPES (Stack, Vector, Array, Map, Queue, etc.) ===
7230
+ # v4.8.7: Explicit handling for CSSL container methods to ensure they're found
7231
+ # v4.9.0: Added Bit and Byte types
7232
+ if isinstance(obj, (Stack, Vector, Array, DataStruct, List, Dictionary, Map, Queue, Bit, Byte, Address)):
7233
+ # Try to get the method directly from the object
7234
+ method = getattr(obj, member, None)
7235
+ if method is not None:
7236
+ return method
7237
+ # Also check the class for methods (handles inheritance)
7238
+ for cls in type(obj).__mro__:
7239
+ if hasattr(cls, member):
7240
+ method = getattr(obj, member)
7241
+ if method is not None:
7242
+ return method
7243
+
5370
7244
  if hasattr(obj, member):
5371
7245
  return getattr(obj, member)
5372
7246
 
@@ -5492,10 +7366,12 @@ class CSSLRuntime:
5492
7366
  elif method == 'toInt' or method == 'toInteger':
5493
7367
  return lambda base=10: int(s, base) if s.lstrip('-').isdigit() else 0
5494
7368
  elif method == 'toFloat' or method == 'toDouble':
5495
- try:
5496
- return lambda: float(s)
5497
- except:
5498
- return lambda: 0.0
7369
+ def _to_float():
7370
+ try:
7371
+ return float(s)
7372
+ except (ValueError, TypeError):
7373
+ return 0.0
7374
+ return _to_float
5499
7375
  elif method == 'toBool':
5500
7376
  return lambda: s.lower() in ('true', '1', 'yes', 'on')
5501
7377
 
@@ -5559,6 +7435,8 @@ class CSSLRuntime:
5559
7435
  return push_back_item
5560
7436
  elif method == 'toArray':
5561
7437
  return lambda: list(lst)
7438
+ elif method == 'content':
7439
+ return lambda: list(lst)
5562
7440
  # === C++ ITERATOR STYLE ===
5563
7441
  elif method == 'begin':
5564
7442
  return lambda: 0
@@ -6051,17 +7929,30 @@ def run_cssl(source: str, service_engine=None, force_python: bool = False) -> An
6051
7929
  Returns:
6052
7930
  Execution result
6053
7931
  """
7932
+ # v4.8.8: Python-only builtins that require subprocess/import magic
7933
+ # These don't work in C++ interpreter and must use Python
7934
+ # v4.9.2: Added address/reflect/memory for pointer system, hooks syntax
7935
+ PYTHON_ONLY_FEATURES = ['includecpp(', 'snapshot(', '%', 'address(', 'reflect(', 'memory(', ' &', '&$', 'destroy(', 'execute(']
7936
+
7937
+ # Check if source uses Python-only features
7938
+ needs_python = any(feat in source for feat in PYTHON_ONLY_FEATURES)
7939
+
6054
7940
  # Try C++ interpreter first (10-20x faster)
6055
- if not force_python:
7941
+ if not force_python and not needs_python:
6056
7942
  cpp_interp = _get_cpp_interpreter()
6057
7943
  if cpp_interp:
6058
7944
  try:
6059
7945
  return cpp_interp.run_string(source)
6060
7946
  except Exception as e:
6061
7947
  # C++ doesn't support this feature, fall back to Python
7948
+ # v4.8.5: Extended fallback triggers for advanced CSSL syntax
6062
7949
  error_msg = str(e).lower()
6063
- # Only fall back for unsupported features, not syntax errors
6064
- if 'unsupported' in error_msg or 'not implemented' in error_msg:
7950
+ fallback_triggers = [
7951
+ 'unsupported', 'not implemented', 'unexpected', 'expected',
7952
+ 'syntax error', 'unknown identifier', 'undefined', 'not defined'
7953
+ ]
7954
+ should_fallback = any(trigger in error_msg for trigger in fallback_triggers)
7955
+ if should_fallback:
6065
7956
  pass # Fall through to Python
6066
7957
  else:
6067
7958
  # Re-raise actual errors
@@ -6077,15 +7968,34 @@ def run_cssl_file(filepath: str, service_engine=None, force_python: bool = False
6077
7968
 
6078
7969
  Uses C++ interpreter for maximum performance when available.
6079
7970
  """
7971
+ # v4.8.8: Python-only builtins that require subprocess/import magic
7972
+ # v4.9.2: Added address/reflect/memory for pointer system, hooks syntax
7973
+ PYTHON_ONLY_FEATURES = ['includecpp(', 'snapshot(', '%', 'address(', 'reflect(', 'memory(', ' &', '&$', 'destroy(', 'execute(']
7974
+
7975
+ # Check file content for Python-only features
7976
+ needs_python = False
7977
+ try:
7978
+ with open(filepath, 'r', encoding='utf-8') as f:
7979
+ source = f.read()
7980
+ needs_python = any(feat in source for feat in PYTHON_ONLY_FEATURES)
7981
+ except Exception:
7982
+ pass # If we can't read, let the runtime handle it
7983
+
6080
7984
  # Try C++ interpreter first
6081
- if not force_python:
7985
+ if not force_python and not needs_python:
6082
7986
  cpp_interp = _get_cpp_interpreter()
6083
7987
  if cpp_interp:
6084
7988
  try:
6085
7989
  return cpp_interp.run(filepath)
6086
7990
  except Exception as e:
7991
+ # v4.8.5: Extended fallback triggers
6087
7992
  error_msg = str(e).lower()
6088
- if 'unsupported' in error_msg or 'not implemented' in error_msg:
7993
+ fallback_triggers = [
7994
+ 'unsupported', 'not implemented', 'unexpected', 'expected',
7995
+ 'syntax error', 'unknown identifier', 'undefined', 'not defined'
7996
+ ]
7997
+ should_fallback = any(trigger in error_msg for trigger in fallback_triggers)
7998
+ if should_fallback:
6089
7999
  pass # Fall through to Python
6090
8000
  else:
6091
8001
  raise CSSLRuntimeError(str(e))