IncludeCPP 3.7.1__py3-none-any.whl → 3.7.25__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.
@@ -19,6 +19,45 @@ from .cssl_types import (
19
19
  )
20
20
 
21
21
 
22
+ # Global custom filter registry
23
+ # Structure: { "type::helper": callback_function }
24
+ # Callback signature: (source, filter_value, runtime) -> Any
25
+ _custom_filters: Dict[str, Callable[[Any, Any, Any], Any]] = {}
26
+
27
+
28
+ def register_filter(filter_type: str, helper: str, callback: Callable[[Any, Any, Any], Any]) -> None:
29
+ """Register a custom filter.
30
+
31
+ Args:
32
+ filter_type: The filter type (e.g., "mytype")
33
+ helper: The helper name (e.g., "where", "index", or "*" for catch-all)
34
+ callback: Function(source, filter_value, runtime) -> filtered_result
35
+
36
+ Usage in CSSL:
37
+ result <==[mytype::where="value"] source;
38
+
39
+ Usage from Python:
40
+ from includecpp.core.cssl.cssl_runtime import register_filter
41
+ register_filter("mytype", "where", lambda src, val, rt: ...)
42
+ """
43
+ key = f"{filter_type}::{helper}"
44
+ _custom_filters[key] = callback
45
+
46
+
47
+ def unregister_filter(filter_type: str, helper: str) -> bool:
48
+ """Unregister a custom filter."""
49
+ key = f"{filter_type}::{helper}"
50
+ if key in _custom_filters:
51
+ del _custom_filters[key]
52
+ return True
53
+ return False
54
+
55
+
56
+ def get_custom_filters() -> Dict[str, Callable]:
57
+ """Get all registered custom filters."""
58
+ return _custom_filters.copy()
59
+
60
+
22
61
  class CSSLRuntimeError(Exception):
23
62
  """Runtime error during CSSL execution with detailed context"""
24
63
  def __init__(self, message: str, line: int = 0, context: str = None, hint: str = None):
@@ -401,6 +440,9 @@ class CSSLRuntime:
401
440
  elif child.type == 'instance_declaration':
402
441
  # Handle instance declaration: instance<"name"> varName;
403
442
  result = self._exec_instance_declaration(child)
443
+ elif child.type == 'super_func':
444
+ # Super-function for .cssl-pl payload files (#$run, #$exec, #$printl)
445
+ result = self._exec_super_func(child)
404
446
  elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
405
447
  'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
406
448
  result = self._execute_node(child)
@@ -788,8 +830,18 @@ class CSSLRuntime:
788
830
  if hasattr(instance, 'append'):
789
831
  instance.append(init_value)
790
832
 
833
+ # Check for global modifier
834
+ modifiers = decl.get('modifiers', [])
835
+ is_global = 'global' in modifiers
836
+
791
837
  # Store in scope
792
838
  self.scope.set(var_name, instance)
839
+
840
+ # If global, also store in promoted_globals and global_scope
841
+ if is_global:
842
+ self._promoted_globals[var_name] = instance
843
+ self.global_scope.set(var_name, instance)
844
+
793
845
  return instance
794
846
 
795
847
  def _exec_instance_declaration(self, node: ASTNode) -> Any:
@@ -820,6 +872,82 @@ class CSSLRuntime:
820
872
  self.scope.set(var_name, instance)
821
873
  return instance
822
874
 
875
+ def _exec_super_func(self, node: ASTNode) -> Any:
876
+ """Execute super-function for .cssl-pl payload files.
877
+
878
+ Super-functions are pre-execution hooks that run when payload() loads a file.
879
+
880
+ Supported super-functions:
881
+ #$run(funcName) - Call a function defined in the payload
882
+ #$exec(expression) - Execute an expression immediately
883
+ #$printl(message) - Print a message during load
884
+
885
+ Example .cssl-pl file:
886
+ void initDatabase() {
887
+ printl("DB initialized");
888
+ }
889
+
890
+ #$run(initDatabase); // Calls initDatabase when payload loads
891
+ #$printl("Payload loaded"); // Prints during load
892
+ """
893
+ super_info = node.value
894
+ super_name = super_info.get('name', '') # e.g., "#$run", "#$exec", "#$printl"
895
+ args = super_info.get('args', [])
896
+
897
+ # Extract the function name part (after #$)
898
+ if super_name.startswith('#$'):
899
+ func_type = super_name[2:] # "run", "exec", "printl"
900
+ else:
901
+ func_type = super_name
902
+
903
+ if func_type == 'run':
904
+ # #$run(funcName) - Call a function by name
905
+ if args:
906
+ func_ref = args[0]
907
+ if isinstance(func_ref, ASTNode):
908
+ if func_ref.type == 'identifier':
909
+ func_name = func_ref.value
910
+ elif func_ref.type == 'call':
911
+ # Direct call like #$run(setup())
912
+ return self._eval_call(func_ref)
913
+ else:
914
+ func_name = self._evaluate(func_ref)
915
+ else:
916
+ func_name = str(func_ref)
917
+
918
+ # Look up and call the function
919
+ func_node = self.scope.get(func_name)
920
+ if func_node and isinstance(func_node, ASTNode) and func_node.type == 'function':
921
+ return self._call_function(func_node, [])
922
+ else:
923
+ raise CSSLRuntimeError(f"#$run: Function '{func_name}' not found", node.line)
924
+
925
+ elif func_type == 'exec':
926
+ # #$exec(expression) - Execute an expression
927
+ if args:
928
+ return self._evaluate(args[0])
929
+
930
+ elif func_type == 'printl':
931
+ # #$printl(message) - Print a message
932
+ if args:
933
+ msg = self._evaluate(args[0])
934
+ print(str(msg))
935
+ self.output_buffer.append(str(msg))
936
+ return None
937
+
938
+ elif func_type == 'print':
939
+ # #$print(message) - Print without newline
940
+ if args:
941
+ msg = self._evaluate(args[0])
942
+ print(str(msg), end='')
943
+ self.output_buffer.append(str(msg))
944
+ return None
945
+
946
+ else:
947
+ raise CSSLRuntimeError(f"Unknown super-function: {super_name}", node.line)
948
+
949
+ return None
950
+
823
951
  def _exec_global_assignment(self, node: ASTNode) -> Any:
824
952
  """Execute global variable assignment: global Name = value
825
953
 
@@ -860,6 +988,14 @@ class CSSLRuntime:
860
988
  result = self._evaluate(inner.value)
861
989
  return result
862
990
 
991
+ # Handle typed declaration: global datastruct<int> data;
992
+ elif inner.type == 'typed_declaration':
993
+ # Add global modifier to the declaration
994
+ if isinstance(inner.value, dict):
995
+ inner.value['modifiers'] = inner.value.get('modifiers', []) + ['global']
996
+ result = self._exec_typed_declaration(inner)
997
+ return result
998
+
863
999
  # Fallback: execute normally
864
1000
  return self._execute_node(inner)
865
1001
 
@@ -884,10 +1020,20 @@ class CSSLRuntime:
884
1020
 
885
1021
  # Bind parameters - handle both positional and named arguments
886
1022
  for i, param in enumerate(params):
887
- # Extract param name from dict format: {'name': 'a', 'type': 'int'}
888
- param_name = param['name'] if isinstance(param, dict) else param
889
-
890
- if param_name in kwargs:
1023
+ # Extract param name and type from dict format: {'name': 'a', 'type': 'int'}
1024
+ if isinstance(param, dict):
1025
+ param_name = param['name']
1026
+ param_type = param.get('type', '')
1027
+ else:
1028
+ param_name = param
1029
+ param_type = ''
1030
+
1031
+ # Check if this is an 'open' parameter - receives all args as a list
1032
+ if param_type == 'open' or param_name == 'Params':
1033
+ # 'open Params' receives all arguments as a list
1034
+ new_scope.set(param_name, list(args))
1035
+ new_scope.set('Params', list(args)) # Also set 'Params' for OpenFind
1036
+ elif param_name in kwargs:
891
1037
  # Named argument takes priority
892
1038
  new_scope.set(param_name, kwargs[param_name])
893
1039
  elif i < len(args):
@@ -1097,8 +1243,20 @@ class CSSLRuntime:
1097
1243
  return None
1098
1244
 
1099
1245
  def _exec_return(self, node: ASTNode) -> Any:
1100
- """Execute return statement"""
1101
- value = self._evaluate(node.value) if node.value else None
1246
+ """Execute return statement.
1247
+
1248
+ Supports multiple return values for shuffled functions:
1249
+ return a, b, c; // Returns tuple (a, b, c)
1250
+ """
1251
+ if node.value is None:
1252
+ raise CSSLReturn(None)
1253
+
1254
+ # Check if this is a multiple return value
1255
+ if isinstance(node.value, dict) and node.value.get('multiple'):
1256
+ values = [self._evaluate(v) for v in node.value.get('values', [])]
1257
+ raise CSSLReturn(tuple(values))
1258
+
1259
+ value = self._evaluate(node.value)
1102
1260
  raise CSSLReturn(value)
1103
1261
 
1104
1262
  def _exec_break(self, node: ASTNode) -> Any:
@@ -1173,8 +1331,11 @@ class CSSLRuntime:
1173
1331
 
1174
1332
  return command_name
1175
1333
 
1176
- def _apply_injection_filter(self, source: Any, filter_info: dict) -> Any:
1177
- """Apply injection filter to extract specific data from source.
1334
+ def _apply_injection_filter(self, source: Any, filter_info) -> Any:
1335
+ """Apply injection filter(s) to extract specific data from source.
1336
+
1337
+ Supports both single filter dict and list of filter dicts for chained filters.
1338
+ Example: [dynamic::content=10][dynamic::content=100] applies both filters.
1178
1339
 
1179
1340
  All BruteInjector Helpers:
1180
1341
  - string::where=VALUE - Filter strings containing VALUE
@@ -1191,10 +1352,31 @@ class CSSLRuntime:
1191
1352
  - combo::blocked - Get blocked items from combo
1192
1353
  - dynamic::VarName=VALUE - Filter by dynamic variable value
1193
1354
  - sql::data - Return only SQL-compatible data
1355
+ - instance::class - Get classes from object
1356
+ - instance::method - Get methods from object
1357
+ - instance::var - Get variables from object
1358
+ - instance::all - Get all categorized (methods, classes, vars)
1359
+ - instance::"ClassName" - Get specific class by name
1360
+ - name::"Name" - Filter by name (class, dict key, attribute)
1194
1361
  """
1195
1362
  if not filter_info:
1196
1363
  return source
1197
1364
 
1365
+ # Handle list of filters (chained filters)
1366
+ if isinstance(filter_info, list):
1367
+ result = source
1368
+ for single_filter in filter_info:
1369
+ result = self._apply_single_filter(result, single_filter)
1370
+ return result
1371
+
1372
+ # Single filter (dict)
1373
+ return self._apply_single_filter(source, filter_info)
1374
+
1375
+ def _apply_single_filter(self, source: Any, filter_info: dict) -> Any:
1376
+ """Apply a single injection filter to extract specific data from source."""
1377
+ if not filter_info:
1378
+ return source
1379
+
1198
1380
  result = source
1199
1381
 
1200
1382
  for filter_key, filter_value in filter_info.items():
@@ -1202,6 +1384,18 @@ class CSSLRuntime:
1202
1384
  filter_type, helper = filter_key.split('::', 1)
1203
1385
  filter_val = self._evaluate(filter_value) if isinstance(filter_value, ASTNode) else filter_value
1204
1386
 
1387
+ # === CHECK CUSTOM FILTERS FIRST ===
1388
+ custom_key = f"{filter_type}::{helper}"
1389
+ if custom_key in _custom_filters:
1390
+ result = _custom_filters[custom_key](result, filter_val, self)
1391
+ continue
1392
+
1393
+ # Check for catch-all custom filter (type::*)
1394
+ catchall_key = f"{filter_type}::*"
1395
+ if catchall_key in _custom_filters:
1396
+ result = _custom_filters[catchall_key](result, filter_val, self)
1397
+ continue
1398
+
1205
1399
  # === STRING HELPERS ===
1206
1400
  if filter_type == 'string':
1207
1401
  if helper == 'where':
@@ -1378,13 +1572,112 @@ class CSSLRuntime:
1378
1572
 
1379
1573
  # === DYNAMIC HELPERS ===
1380
1574
  elif filter_type == 'dynamic':
1381
- # dynamic::VarName=VALUE - Match if variable equals value
1382
- var_name = helper
1383
- var_value = self.scope.get(var_name)
1384
- if var_value == filter_val:
1385
- pass # Keep result
1575
+ if helper == 'content':
1576
+ # dynamic::content=VALUE - General content filter for any type
1577
+ # Filters elements where content equals VALUE
1578
+ if isinstance(result, (list, tuple)):
1579
+ # Filter list/tuple elements by content value
1580
+ result = [item for item in result if item == filter_val]
1581
+ elif isinstance(result, dict):
1582
+ # Filter dict by values matching filter_val
1583
+ result = {k: v for k, v in result.items() if v == filter_val}
1584
+ elif result == filter_val:
1585
+ pass # Keep result if it matches
1586
+ else:
1587
+ result = None
1588
+ elif helper == 'not':
1589
+ # dynamic::not=VALUE - Exclude elements equal to VALUE
1590
+ if isinstance(result, (list, tuple)):
1591
+ result = [item for item in result if item != filter_val]
1592
+ elif isinstance(result, dict):
1593
+ result = {k: v for k, v in result.items() if v != filter_val}
1594
+ elif result != filter_val:
1595
+ pass # Keep result if it doesn't match
1596
+ else:
1597
+ result = None
1598
+ elif helper == 'gt':
1599
+ # dynamic::gt=VALUE - Greater than
1600
+ if isinstance(result, (list, tuple)):
1601
+ result = [item for item in result if item > filter_val]
1602
+ elif result > filter_val:
1603
+ pass
1604
+ else:
1605
+ result = None
1606
+ elif helper == 'lt':
1607
+ # dynamic::lt=VALUE - Less than
1608
+ if isinstance(result, (list, tuple)):
1609
+ result = [item for item in result if item < filter_val]
1610
+ elif result < filter_val:
1611
+ pass
1612
+ else:
1613
+ result = None
1614
+ elif helper == 'gte':
1615
+ # dynamic::gte=VALUE - Greater than or equal
1616
+ if isinstance(result, (list, tuple)):
1617
+ result = [item for item in result if item >= filter_val]
1618
+ elif result >= filter_val:
1619
+ pass
1620
+ else:
1621
+ result = None
1622
+ elif helper == 'lte':
1623
+ # dynamic::lte=VALUE - Less than or equal
1624
+ if isinstance(result, (list, tuple)):
1625
+ result = [item for item in result if item <= filter_val]
1626
+ elif result <= filter_val:
1627
+ pass
1628
+ else:
1629
+ result = None
1630
+ elif helper == 'mod':
1631
+ # dynamic::mod=VALUE - Modulo filter (item % VALUE == 0)
1632
+ if isinstance(result, (list, tuple)):
1633
+ result = [item for item in result if isinstance(item, (int, float)) and item % filter_val == 0]
1634
+ elif isinstance(result, (int, float)) and result % filter_val == 0:
1635
+ pass
1636
+ else:
1637
+ result = None
1638
+ elif helper == 'range':
1639
+ # dynamic::range="min:max" - Filter values in range
1640
+ if isinstance(filter_val, str) and ':' in filter_val:
1641
+ parts = filter_val.split(':')
1642
+ min_val = int(parts[0]) if parts[0] else None
1643
+ max_val = int(parts[1]) if parts[1] else None
1644
+ if isinstance(result, (list, tuple)):
1645
+ def in_range(x):
1646
+ if min_val is not None and x < min_val:
1647
+ return False
1648
+ if max_val is not None and x > max_val:
1649
+ return False
1650
+ return True
1651
+ result = [item for item in result if in_range(item)]
1652
+ elif isinstance(result, (int, float)):
1653
+ if min_val is not None and result < min_val:
1654
+ result = None
1655
+ elif max_val is not None and result > max_val:
1656
+ result = None
1657
+ elif helper == 'even':
1658
+ # dynamic::even - Filter even numbers
1659
+ if isinstance(result, (list, tuple)):
1660
+ result = [item for item in result if isinstance(item, int) and item % 2 == 0]
1661
+ elif isinstance(result, int) and result % 2 == 0:
1662
+ pass
1663
+ else:
1664
+ result = None
1665
+ elif helper == 'odd':
1666
+ # dynamic::odd - Filter odd numbers
1667
+ if isinstance(result, (list, tuple)):
1668
+ result = [item for item in result if isinstance(item, int) and item % 2 != 0]
1669
+ elif isinstance(result, int) and result % 2 != 0:
1670
+ pass
1671
+ else:
1672
+ result = None
1386
1673
  else:
1387
- result = None
1674
+ # dynamic::VarName=VALUE - Match if variable equals value
1675
+ var_name = helper
1676
+ var_value = self.scope.get(var_name)
1677
+ if var_value == filter_val:
1678
+ pass # Keep result
1679
+ else:
1680
+ result = None
1388
1681
 
1389
1682
  # === SQL HELPERS ===
1390
1683
  elif filter_type == 'sql':
@@ -1395,6 +1688,128 @@ class CSSLRuntime:
1395
1688
  else:
1396
1689
  result = str(result) # Convert to string
1397
1690
 
1691
+ # === INSTANCE HELPERS ===
1692
+ # Works on CSSLInstance, Python objects, dicts, modules
1693
+ elif filter_type == 'instance':
1694
+ from .cssl_types import CSSLInstance
1695
+ import inspect
1696
+
1697
+ if isinstance(result, CSSLInstance):
1698
+ # Filter CSSL instances
1699
+ if helper in ('class', 'classes'):
1700
+ classes = {}
1701
+ for name, member in result._members.items():
1702
+ if isinstance(member, CSSLInstance):
1703
+ classes[name] = member
1704
+ result = classes if classes else None
1705
+ elif helper in ('method', 'methods'):
1706
+ result = dict(result._class.methods)
1707
+ elif helper in ('var', 'vars', 'variable', 'variables'):
1708
+ vars_dict = {}
1709
+ for name, member in result._members.items():
1710
+ if not isinstance(member, CSSLInstance):
1711
+ vars_dict[name] = member
1712
+ result = vars_dict
1713
+ elif helper in ('all',):
1714
+ result = {
1715
+ 'methods': list(result._class.methods.keys()),
1716
+ 'classes': [result._class.name] + [m._class.name for m in result._members.values() if isinstance(m, CSSLInstance)],
1717
+ 'vars': [n for n, m in result._members.items() if not isinstance(m, CSSLInstance)]
1718
+ }
1719
+ else:
1720
+ # Filter by class name
1721
+ class_name = filter_val if filter_val else helper
1722
+ found = None
1723
+ if result._class.name == class_name:
1724
+ found = result
1725
+ else:
1726
+ for name, member in result._members.items():
1727
+ if isinstance(member, CSSLInstance) and member._class.name == class_name:
1728
+ found = member
1729
+ break
1730
+ result = found
1731
+ elif isinstance(result, dict):
1732
+ # Filter dicts
1733
+ if helper in ('class', 'classes'):
1734
+ result = {k: v for k, v in result.items() if inspect.isclass(v)}
1735
+ elif helper in ('method', 'methods', 'func', 'function', 'functions'):
1736
+ result = {k: v for k, v in result.items() if callable(v)}
1737
+ elif helper in ('var', 'vars', 'variable', 'variables'):
1738
+ result = {k: v for k, v in result.items() if not callable(v) and not inspect.isclass(v)}
1739
+ else:
1740
+ # Get by key
1741
+ key_name = filter_val if filter_val else helper
1742
+ result = result.get(key_name)
1743
+ elif hasattr(result, '__dict__') or hasattr(result, '__class__'):
1744
+ # Filter Python objects/modules
1745
+ if helper in ('class', 'classes'):
1746
+ classes = {}
1747
+ for name in dir(result):
1748
+ if not name.startswith('_'):
1749
+ attr = getattr(result, name, None)
1750
+ if inspect.isclass(attr):
1751
+ classes[name] = attr
1752
+ result = classes if classes else None
1753
+ elif helper in ('method', 'methods', 'func', 'function', 'functions'):
1754
+ methods = {}
1755
+ for name in dir(result):
1756
+ if not name.startswith('_'):
1757
+ attr = getattr(result, name, None)
1758
+ if callable(attr):
1759
+ methods[name] = attr
1760
+ result = methods if methods else None
1761
+ elif helper in ('var', 'vars', 'variable', 'variables'):
1762
+ vars_dict = {}
1763
+ for name in dir(result):
1764
+ if not name.startswith('_'):
1765
+ attr = getattr(result, name, None)
1766
+ if not callable(attr) and not inspect.isclass(attr):
1767
+ vars_dict[name] = attr
1768
+ result = vars_dict if vars_dict else None
1769
+ elif helper in ('all',):
1770
+ all_info = {'methods': [], 'classes': [], 'vars': []}
1771
+ for name in dir(result):
1772
+ if not name.startswith('_'):
1773
+ attr = getattr(result, name, None)
1774
+ if inspect.isclass(attr):
1775
+ all_info['classes'].append(name)
1776
+ elif callable(attr):
1777
+ all_info['methods'].append(name)
1778
+ else:
1779
+ all_info['vars'].append(name)
1780
+ result = all_info
1781
+ else:
1782
+ # Get attribute by name
1783
+ attr_name = filter_val if filter_val else helper
1784
+ result = getattr(result, attr_name, None)
1785
+
1786
+ # === NAME HELPERS ===
1787
+ # General name filter for any object type
1788
+ elif filter_type == 'name':
1789
+ from .cssl_types import CSSLInstance
1790
+ target_name = filter_val if filter_val else helper
1791
+
1792
+ if isinstance(result, CSSLInstance):
1793
+ if result._class.name == target_name:
1794
+ pass # Keep result
1795
+ else:
1796
+ found = None
1797
+ for name, member in result._members.items():
1798
+ if isinstance(member, CSSLInstance) and member._class.name == target_name:
1799
+ found = member
1800
+ break
1801
+ result = found
1802
+ elif isinstance(result, dict):
1803
+ result = result.get(target_name)
1804
+ elif isinstance(result, list):
1805
+ result = [item for item in result if str(item) == target_name or (hasattr(item, 'name') and item.name == target_name)]
1806
+ elif hasattr(result, target_name):
1807
+ result = getattr(result, target_name)
1808
+ elif hasattr(result, '__class__') and result.__class__.__name__ == target_name:
1809
+ pass # Keep result if class name matches
1810
+ else:
1811
+ result = None
1812
+
1398
1813
  return result
1399
1814
 
1400
1815
  def _exec_inject(self, node: ASTNode) -> Any:
@@ -1452,7 +1867,15 @@ class CSSLRuntime:
1452
1867
  final_value = source
1453
1868
  elif mode == 'add':
1454
1869
  # Copy & add - preserve target and add source
1455
- if isinstance(current_value, list):
1870
+ from .cssl_types import CSSLInstance
1871
+
1872
+ # Special handling for CSSLInstance - merge classes
1873
+ if isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
1874
+ # Add the new class instance as a member with class name as key
1875
+ class_name = source._class.name
1876
+ current_value._members[class_name] = source
1877
+ final_value = current_value
1878
+ elif isinstance(current_value, list):
1456
1879
  if isinstance(source, list):
1457
1880
  final_value = current_value + source
1458
1881
  else:
@@ -1468,9 +1891,14 @@ class CSSLRuntime:
1468
1891
  elif mode == 'move':
1469
1892
  # Move & remove from source
1470
1893
  final_value = source
1471
- # Clear the source
1472
- if isinstance(source_node, ASTNode) and source_node.type == 'identifier':
1473
- self.scope.set(source_node.value, None)
1894
+ # Clear the source - handle all node types
1895
+ if isinstance(source_node, ASTNode):
1896
+ if source_node.type == 'identifier':
1897
+ self.scope.set(source_node.value, None)
1898
+ elif source_node.type == 'module_ref':
1899
+ self._set_module_value(source_node.value, None)
1900
+ elif source_node.type == 'member_access':
1901
+ self._set_member(source_node, None)
1474
1902
  else:
1475
1903
  final_value = source
1476
1904
 
@@ -1542,9 +1970,36 @@ class CSSLRuntime:
1542
1970
  final_value = [current_value, source]
1543
1971
  elif mode == 'move':
1544
1972
  final_value = source
1545
- # Clear the source
1546
- if isinstance(source_node, ASTNode) and source_node.type == 'identifier':
1547
- self.scope.set(source_node.value, None)
1973
+ # Remove filtered elements from source (not clear entirely)
1974
+ if isinstance(source_node, ASTNode):
1975
+ if filter_info:
1976
+ # Get original source value
1977
+ original_source = self._evaluate(source_node)
1978
+ if isinstance(original_source, list) and isinstance(final_value, list):
1979
+ # Remove filtered items from original list
1980
+ remaining = [item for item in original_source if item not in final_value]
1981
+ if source_node.type == 'identifier':
1982
+ self.scope.set(source_node.value, remaining)
1983
+ elif source_node.type == 'module_ref':
1984
+ self._set_module_value(source_node.value, remaining)
1985
+ elif source_node.type == 'member_access':
1986
+ self._set_member(source_node, remaining)
1987
+ else:
1988
+ # Single value filter - set source to None
1989
+ if source_node.type == 'identifier':
1990
+ self.scope.set(source_node.value, None)
1991
+ elif source_node.type == 'module_ref':
1992
+ self._set_module_value(source_node.value, None)
1993
+ elif source_node.type == 'member_access':
1994
+ self._set_member(source_node, None)
1995
+ else:
1996
+ # No filter - clear entire source
1997
+ if source_node.type == 'identifier':
1998
+ self.scope.set(source_node.value, None)
1999
+ elif source_node.type == 'module_ref':
2000
+ self._set_module_value(source_node.value, None)
2001
+ elif source_node.type == 'member_access':
2002
+ self._set_member(source_node, None)
1548
2003
  else:
1549
2004
  final_value = source
1550
2005
 
@@ -1567,6 +2022,42 @@ class CSSLRuntime:
1567
2022
  self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
1568
2023
  elif target.type == 'member_access':
1569
2024
  self._set_member(target, final_value)
2025
+ elif target.type == 'typed_declaration':
2026
+ # Handle typed target: source ==> datastruct<dynamic> Output
2027
+ var_name = target.value.get('name')
2028
+ type_name = target.value.get('type_name')
2029
+ element_type = target.value.get('element_type', 'dynamic')
2030
+
2031
+ # Create appropriate container with final_value
2032
+ from .cssl_types import (create_datastruct, create_vector, create_array,
2033
+ create_stack, create_list, create_dictionary, create_map)
2034
+
2035
+ container = None
2036
+ if type_name == 'datastruct':
2037
+ container = create_datastruct(element_type)
2038
+ elif type_name == 'vector':
2039
+ container = create_vector(element_type)
2040
+ elif type_name == 'array':
2041
+ container = create_array(element_type)
2042
+ elif type_name == 'stack':
2043
+ container = create_stack(element_type)
2044
+ elif type_name == 'list':
2045
+ container = create_list(element_type)
2046
+ elif type_name == 'dictionary':
2047
+ container = create_dictionary()
2048
+ elif type_name == 'map':
2049
+ container = create_map()
2050
+
2051
+ if container is not None:
2052
+ # Add final_value to container
2053
+ if isinstance(final_value, (list, tuple)):
2054
+ container.extend(final_value)
2055
+ elif final_value is not None:
2056
+ container.append(final_value)
2057
+ self.scope.set(var_name, container)
2058
+ else:
2059
+ # Unknown type, just set the value directly
2060
+ self.scope.set(var_name, final_value)
1570
2061
 
1571
2062
  return final_value
1572
2063
 
@@ -1725,10 +2216,44 @@ class CSSLRuntime:
1725
2216
 
1726
2217
  return value
1727
2218
 
2219
+ def _exec_tuple_assignment(self, node: ASTNode) -> Any:
2220
+ """Execute tuple unpacking assignment: a, b, c = shuffled_func()
2221
+
2222
+ Used with shuffled functions that return multiple values.
2223
+ """
2224
+ targets = node.value.get('targets', [])
2225
+ value = self._evaluate(node.value.get('value'))
2226
+
2227
+ # Convert value to list if it's a tuple or iterable
2228
+ if isinstance(value, (list, tuple)):
2229
+ values = list(value)
2230
+ elif hasattr(value, '__iter__') and not isinstance(value, (str, dict)):
2231
+ values = list(value)
2232
+ else:
2233
+ # Single value - assign to first target only
2234
+ values = [value]
2235
+
2236
+ # Assign values to targets
2237
+ for i, target in enumerate(targets):
2238
+ if i < len(values):
2239
+ var_name = target.value if isinstance(target, ASTNode) else target
2240
+ self.scope.set(var_name, values[i])
2241
+ else:
2242
+ # More targets than values - set to None
2243
+ var_name = target.value if isinstance(target, ASTNode) else target
2244
+ self.scope.set(var_name, None)
2245
+
2246
+ # Assignment statements don't produce a visible result
2247
+ return None
2248
+
1728
2249
  def _exec_expression(self, node: ASTNode) -> Any:
1729
2250
  """Execute expression statement"""
1730
2251
  return self._evaluate(node.value)
1731
2252
 
2253
+ def _exec_type_instantiation(self, node: ASTNode) -> Any:
2254
+ """Execute type instantiation as statement (e.g., vector<int>)"""
2255
+ return self._evaluate(node)
2256
+
1732
2257
  def _exec_await(self, node: ASTNode) -> Any:
1733
2258
  """Execute await statement - waits for expression to complete"""
1734
2259
  # Evaluate the awaited expression
@@ -1778,9 +2303,12 @@ class CSSLRuntime:
1778
2303
 
1779
2304
  if node.type == 'literal':
1780
2305
  value = node.value
1781
- # NEW: String interpolation - replace <variable> with scope values
1782
- if isinstance(value, str) and '<' in value and '>' in value:
1783
- value = self._interpolate_string(value)
2306
+ # String interpolation - replace {var} or <var> with scope values
2307
+ if isinstance(value, str):
2308
+ has_fstring = '{' in value and '}' in value
2309
+ has_legacy = '<' in value and '>' in value
2310
+ if has_fstring or has_legacy:
2311
+ value = self._interpolate_string(value)
1784
2312
  return value
1785
2313
 
1786
2314
  # NEW: Type literals (list, dict) - create empty instances
@@ -1807,12 +2335,13 @@ class CSSLRuntime:
1807
2335
  return value
1808
2336
 
1809
2337
  if node.type == 'module_ref':
1810
- # Check modules first, then promoted globals, then scope
1811
- value = self.get_module(node.value)
1812
- if value is None:
1813
- value = self._promoted_globals.get(node.value)
2338
+ # User-defined globals have priority over SDK modules
2339
+ # Check promoted globals first, then global scope, then SDK modules
2340
+ value = self._promoted_globals.get(node.value)
1814
2341
  if value is None:
1815
2342
  value = self.global_scope.get(node.value)
2343
+ if value is None:
2344
+ value = self.get_module(node.value) # SDK modules as fallback
1816
2345
  return value
1817
2346
 
1818
2347
  if node.type == 'self_ref':
@@ -1890,16 +2419,34 @@ class CSSLRuntime:
1890
2419
  return self._eval_this_access(node)
1891
2420
 
1892
2421
  if node.type == 'type_instantiation':
1893
- # Create new instance of a type: stack<string>, vector<int>, etc.
2422
+ # Create new instance of a type: stack<string>, vector<int>, map<K,V>, etc.
1894
2423
  type_name = node.value.get('type')
1895
2424
  element_type = node.value.get('element_type', 'dynamic')
2425
+ value_type = node.value.get('value_type') # For map<K, V>
2426
+ init_values = node.value.get('init_values') # For inline init: map<K,V>{...}
2427
+
2428
+ # Helper to populate container with init values
2429
+ def _populate_container(container, init_vals):
2430
+ if init_vals and isinstance(init_vals, list):
2431
+ for val_node in init_vals:
2432
+ val = self._evaluate(val_node) if isinstance(val_node, ASTNode) else val_node
2433
+ if hasattr(container, 'push'):
2434
+ container.push(val)
2435
+ elif hasattr(container, 'add'):
2436
+ container.add(val)
2437
+ elif hasattr(container, 'append'):
2438
+ container.append(val)
2439
+ return container
1896
2440
 
1897
2441
  if type_name == 'stack':
1898
- return Stack(element_type)
2442
+ s = Stack(element_type)
2443
+ return _populate_container(s, init_values)
1899
2444
  elif type_name == 'vector':
1900
- return Vector(element_type)
2445
+ v = Vector(element_type)
2446
+ return _populate_container(v, init_values)
1901
2447
  elif type_name == 'datastruct':
1902
- return DataStruct(element_type)
2448
+ d = DataStruct(element_type)
2449
+ return _populate_container(d, init_values)
1903
2450
  elif type_name == 'shuffled':
1904
2451
  return Shuffled(element_type)
1905
2452
  elif type_name == 'iterator':
@@ -1911,11 +2458,22 @@ class CSSLRuntime:
1911
2458
  elif type_name == 'openquote':
1912
2459
  return OpenQuote()
1913
2460
  elif type_name == 'array':
1914
- return Array(element_type)
2461
+ a = Array(element_type)
2462
+ return _populate_container(a, init_values)
1915
2463
  elif type_name == 'list':
1916
- return List(element_type)
2464
+ l = List(element_type)
2465
+ return _populate_container(l, init_values)
1917
2466
  elif type_name in ('dictionary', 'dict'):
1918
2467
  return Dictionary(element_type)
2468
+ elif type_name == 'map':
2469
+ # Create Map with key_type and value_type
2470
+ m = Map(element_type, value_type or 'dynamic')
2471
+ # If inline initialization provided, populate the map
2472
+ if init_values:
2473
+ for key, value_node in init_values.items():
2474
+ value = self._evaluate(value_node)
2475
+ m.insert(key, value)
2476
+ return m
1919
2477
  else:
1920
2478
  return None
1921
2479
 
@@ -2691,12 +3249,20 @@ class CSSLRuntime:
2691
3249
  pass
2692
3250
 
2693
3251
  def _set_module_value(self, path: str, value: Any):
2694
- """Set a value on a module path"""
3252
+ """Set a value on a module path or promoted global"""
2695
3253
  parts = path.split('.')
2696
3254
  if len(parts) < 2:
3255
+ # Single name (no dots) - set in promoted_globals and global_scope
3256
+ self._promoted_globals[path] = value
3257
+ self.global_scope.set(path, value)
2697
3258
  return
2698
3259
 
2699
3260
  obj = self._modules.get(parts[0])
3261
+ # Also check promoted_globals for the base object
3262
+ if obj is None:
3263
+ obj = self._promoted_globals.get(parts[0])
3264
+ if obj is None:
3265
+ obj = self.global_scope.get(parts[0])
2700
3266
  if obj is None:
2701
3267
  return
2702
3268
 
@@ -2714,14 +3280,24 @@ class CSSLRuntime:
2714
3280
  elif isinstance(obj, dict):
2715
3281
  obj[final_attr] = value
2716
3282
 
2717
- # NEW: String interpolation
3283
+ # String interpolation (supports both <var> and {var} syntax)
2718
3284
  def _interpolate_string(self, string: str) -> str:
2719
- """Replace <variable> placeholders with values from scope - NEW
3285
+ """Replace {variable} and <variable> placeholders with values from scope.
3286
+
3287
+ Both syntaxes are supported (variables only, not expressions):
3288
+ "Hello {name}!" -> "Hello John!" (f-string style)
3289
+ "Hello <name>!" -> "Hello John!" (legacy CSSL style)
3290
+
3291
+ Examples:
3292
+ string name = "Alice";
3293
+ int age = 30;
3294
+ printl("Hello {name}, you are {age} years old!");
3295
+ printl("Hello <name>, you are <age> years old!");
2720
3296
 
2721
- Example: "Hello <name>!" becomes "Hello John!" if name = "John"
3297
+ Note: Only simple variable names are supported, not expressions.
3298
+ Use string concatenation for complex expressions.
2722
3299
  """
2723
3300
  import re
2724
- pattern = r'<([A-Za-z_][A-Za-z0-9_]*)>'
2725
3301
 
2726
3302
  def replacer(match):
2727
3303
  var_name = match.group(1)
@@ -2733,10 +3309,23 @@ class CSSLRuntime:
2733
3309
  # Try modules
2734
3310
  if value is None:
2735
3311
  value = self._modules.get(var_name)
3312
+ # Try global scope
3313
+ if value is None:
3314
+ value = self.global_scope.get(var_name)
2736
3315
  # Return string representation or empty string if None
2737
3316
  return str(value) if value is not None else ''
2738
3317
 
2739
- return re.sub(pattern, replacer, string)
3318
+ # Support both {var} and <var> syntax - simple variable names only
3319
+ # Pattern: {identifier} or <identifier>
3320
+ patterns = [
3321
+ r'\{([A-Za-z_][A-Za-z0-9_]*)\}', # {name} f-string style
3322
+ r'<([A-Za-z_][A-Za-z0-9_]*)>', # <name> legacy CSSL style
3323
+ ]
3324
+
3325
+ result = string
3326
+ for pattern in patterns:
3327
+ result = re.sub(pattern, replacer, result)
3328
+ return result
2740
3329
 
2741
3330
  # NEW: Promote variable to global scope via global()
2742
3331
  def promote_to_global(self, s_ref_name: str):