IncludeCPP 3.7.17__tar.gz → 3.7.23__tar.gz

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 (59) hide show
  1. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/PKG-INFO +1 -1
  2. {includecpp-3.7.17 → includecpp-3.7.23}/PKG-INFO +1 -1
  3. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/__init__.py +1 -1
  4. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/cli/commands.py +7 -1
  5. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/__init__.py +6 -1
  6. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_builtins.py +143 -8
  7. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_builtins.pyi +79 -0
  8. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_parser.py +93 -27
  9. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_runtime.py +446 -25
  10. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_types.py +70 -0
  11. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl_bridge.py +64 -6
  12. {includecpp-3.7.17 → includecpp-3.7.23}/pyproject.toml +1 -1
  13. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  14. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  15. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/entry_points.txt +0 -0
  16. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/requires.txt +0 -0
  17. {includecpp-3.7.17 → includecpp-3.7.23}/IncludeCPP.egg-info/top_level.txt +0 -0
  18. {includecpp-3.7.17 → includecpp-3.7.23}/LICENSE +0 -0
  19. {includecpp-3.7.17 → includecpp-3.7.23}/MANIFEST.in +0 -0
  20. {includecpp-3.7.17 → includecpp-3.7.23}/README.md +0 -0
  21. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/__init__.pyi +0 -0
  22. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/__main__.py +0 -0
  23. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/cli/__init__.py +0 -0
  24. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/cli/config_parser.py +0 -0
  25. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/__init__.py +0 -0
  26. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/ai_integration.py +0 -0
  27. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/build_manager.py +0 -0
  28. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cpp_api.py +0 -0
  29. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cpp_api.pyi +0 -0
  30. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cppy_converter.py +0 -0
  31. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/CSSL_DOCUMENTATION.md +0 -0
  32. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_events.py +0 -0
  33. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_modules.py +0 -0
  34. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl/cssl_syntax.py +0 -0
  35. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/cssl_bridge.pyi +0 -0
  36. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/error_catalog.py +0 -0
  37. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/error_formatter.py +0 -0
  38. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/exceptions.py +0 -0
  39. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/path_discovery.py +0 -0
  40. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/project_ui.py +0 -0
  41. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/core/settings_ui.py +0 -0
  42. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/generator/__init__.py +0 -0
  43. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/generator/parser.cpp +0 -0
  44. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/generator/parser.h +0 -0
  45. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/generator/type_resolver.cpp +0 -0
  46. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/generator/type_resolver.h +0 -0
  47. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/py.typed +0 -0
  48. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/templates/cpp.proj.template +0 -0
  49. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/__init__.py +0 -0
  50. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/__init__.py +0 -0
  51. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/images/cssl.png +0 -0
  52. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/images/cssl_pl.png +0 -0
  53. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/language-configuration.json +0 -0
  54. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/package.json +0 -0
  55. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/snippets/cssl.snippets.json +0 -0
  56. {includecpp-3.7.17 → includecpp-3.7.23}/includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +0 -0
  57. {includecpp-3.7.17 → includecpp-3.7.23}/requirements.txt +0 -0
  58. {includecpp-3.7.17 → includecpp-3.7.23}/setup.cfg +0 -0
  59. {includecpp-3.7.17 → includecpp-3.7.23}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.7.17
3
+ Version: 3.7.23
4
4
  Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
5
5
  Home-page: https://github.com/liliassg/IncludeCPP
6
6
  Author: Lilias Hatterscheidt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.7.17
3
+ Version: 3.7.23
4
4
  Summary: Professional C++ Python bindings with type-generic templates, pystubs and native threading
5
5
  Home-page: https://github.com/liliassg/IncludeCPP
6
6
  Author: Lilias Hatterscheidt
@@ -2,7 +2,7 @@ from .core.cpp_api import CppApi
2
2
  from .core import cssl_bridge as CSSL
3
3
  import warnings
4
4
 
5
- __version__ = "3.7.17"
5
+ __version__ = "3.7.23"
6
6
  __all__ = ["CppApi", "CSSL"]
7
7
 
8
8
  # Module-level cache for C++ modules
@@ -7720,7 +7720,13 @@ def cssl_doc(search, list_sections):
7720
7720
  click.echo("Or use: includecpp cssl doc --list")
7721
7721
  else:
7722
7722
  # Full documentation mode
7723
- click.echo_via_pager(content)
7723
+ # Replace Unicode characters that may not be supported on all terminals
7724
+ safe_content = content.replace('✓', '[OK]').replace('✗', '[X]').replace('→', '->').replace('←', '<-').replace('•', '*').replace('─', '-').replace('│', '|').replace('└', '+').replace('├', '+').replace('▸', '>').replace('▾', 'v')
7725
+ try:
7726
+ click.echo_via_pager(safe_content)
7727
+ except UnicodeEncodeError:
7728
+ # Fallback: encode with errors='replace'
7729
+ click.echo(safe_content.encode('ascii', errors='replace').decode('ascii'))
7724
7730
  else:
7725
7731
  click.secho("Documentation file not found.", fg='yellow')
7726
7732
  click.echo("Looking for: CSSL_DOCUMENTATION.md")
@@ -16,7 +16,10 @@ from .cssl_parser import (
16
16
  CSSLSyntaxError, CSSLLexer, CSSLParser, ASTNode,
17
17
  KEYWORDS, TYPE_GENERICS, TYPE_PARAM_FUNCTIONS, INJECTION_HELPERS
18
18
  )
19
- from .cssl_runtime import CSSLRuntime, CSSLRuntimeError, CSSLServiceRunner, run_cssl, run_cssl_file
19
+ from .cssl_runtime import (
20
+ CSSLRuntime, CSSLRuntimeError, CSSLServiceRunner, run_cssl, run_cssl_file,
21
+ register_filter, unregister_filter, get_custom_filters
22
+ )
20
23
  from .cssl_types import (
21
24
  DataStruct, Shuffled, Iterator, Combo, DataSpace, OpenQuote,
22
25
  OpenFind, Parameter, Stack, Vector, Array,
@@ -33,6 +36,8 @@ __all__ = [
33
36
  # Runtime
34
37
  'CSSLRuntime', 'CSSLRuntimeError', 'CSSLServiceRunner',
35
38
  'run_cssl', 'run_cssl_file',
39
+ # Filter Registration
40
+ 'register_filter', 'unregister_filter', 'get_custom_filters',
36
41
  # Data Types
37
42
  'DataStruct', 'Shuffled', 'Iterator', 'Combo', 'DataSpace', 'OpenQuote',
38
43
  'OpenFind', 'Parameter', 'Stack', 'Vector', 'Array',
@@ -307,6 +307,10 @@ class CSSLBuiltins:
307
307
  self._functions['dataspace'] = self.builtin_dataspace
308
308
  self._functions['openquote'] = self.builtin_openquote
309
309
  self._functions['OpenFind'] = self.builtin_openfind
310
+ self._functions['vector'] = self.builtin_vector
311
+ self._functions['array'] = self.builtin_array
312
+ self._functions['stack'] = self.builtin_stack
313
+ self._functions['map'] = self.builtin_map
310
314
 
311
315
  # Print aliases for CSSL
312
316
  self._functions['printl'] = self.builtin_println # CSSL uses printl for println
@@ -433,13 +437,50 @@ class CSSLBuiltins:
433
437
  # ============= Type Checking =============
434
438
 
435
439
  def builtin_typeof(self, value: Any) -> str:
436
- """Get type name"""
440
+ """Get type name - returns CSSL-specific type names for CSSL types"""
437
441
  if value is None:
438
442
  return 'null'
443
+
444
+ # Check CSSL-specific types first
445
+ from .cssl_types import (Vector, Array, Stack, DataStruct,
446
+ List as CSSLList, Dictionary, Map,
447
+ Shuffled, Iterator, Combo, DataSpace,
448
+ OpenQuote, Parameter, CSSLInstance)
449
+
450
+ if isinstance(value, Vector):
451
+ return 'vector'
452
+ elif isinstance(value, Array):
453
+ return 'array'
454
+ elif isinstance(value, Stack):
455
+ return 'stack'
456
+ elif isinstance(value, DataStruct):
457
+ return 'datastruct'
458
+ elif isinstance(value, CSSLList):
459
+ return 'list'
460
+ elif isinstance(value, Dictionary):
461
+ return 'dictionary'
462
+ elif isinstance(value, Map):
463
+ return 'map'
464
+ elif isinstance(value, Shuffled):
465
+ return 'shuffled'
466
+ elif isinstance(value, Iterator):
467
+ return 'iterator'
468
+ elif isinstance(value, Combo):
469
+ return 'combo'
470
+ elif isinstance(value, DataSpace):
471
+ return 'dataspace'
472
+ elif isinstance(value, OpenQuote):
473
+ return 'openquote'
474
+ elif isinstance(value, Parameter):
475
+ return 'parameter'
476
+ elif isinstance(value, CSSLInstance):
477
+ return value._class.name
478
+
479
+ # Python types as fallback
439
480
  type_map = {
440
481
  int: 'int',
441
482
  float: 'float',
442
- str: 'str',
483
+ str: 'string',
443
484
  bool: 'bool',
444
485
  list: 'list',
445
486
  dict: 'dict',
@@ -1063,13 +1104,18 @@ class CSSLBuiltins:
1063
1104
  def builtin_instance_getClasses(self, obj: Any) -> list:
1064
1105
  """Get all classes from an object/module.
1065
1106
  Usage: instance::getClasses(@module)
1066
- Returns list of class names.
1107
+ Returns list of class names (including merged classes).
1067
1108
  """
1068
1109
  from .cssl_types import CSSLInstance
1069
1110
 
1070
- # Handle CSSL class instances - they don't contain nested classes
1111
+ # Handle CSSL class instances
1071
1112
  if isinstance(obj, CSSLInstance):
1072
- return [obj._class.name] # Return the class name itself
1113
+ classes = [obj._class.name] # Primary class
1114
+ # Check for merged class instances in members
1115
+ for name, member in obj._members.items():
1116
+ if isinstance(member, CSSLInstance):
1117
+ classes.append(member._class.name)
1118
+ return classes
1073
1119
 
1074
1120
  # Handle Python objects
1075
1121
  import inspect
@@ -1084,13 +1130,18 @@ class CSSLBuiltins:
1084
1130
  def builtin_instance_getVars(self, obj: Any) -> list:
1085
1131
  """Get all variables/attributes (non-callable) from an object.
1086
1132
  Usage: instance::getVars(@module)
1087
- Returns list of variable names.
1133
+ Returns list of variable names (excludes merged class instances).
1088
1134
  """
1089
1135
  from .cssl_types import CSSLInstance
1090
1136
 
1091
1137
  # Handle CSSL class instances
1092
1138
  if isinstance(obj, CSSLInstance):
1093
- return list(obj._members.keys())
1139
+ vars_list = []
1140
+ for name, member in obj._members.items():
1141
+ # Exclude merged class instances
1142
+ if not isinstance(member, CSSLInstance):
1143
+ vars_list.append(name)
1144
+ return vars_list
1094
1145
 
1095
1146
  # Handle Python objects
1096
1147
  vars_list = []
@@ -1118,7 +1169,15 @@ class CSSLBuiltins:
1118
1169
  if isinstance(obj, CSSLInstance):
1119
1170
  result['methods'] = list(obj._class.methods.keys())
1120
1171
  result['classes'] = [obj._class.name]
1121
- result['vars'] = list(obj._members.keys())
1172
+
1173
+ # Separate regular vars from merged class instances
1174
+ for name, member in obj._members.items():
1175
+ if isinstance(member, CSSLInstance):
1176
+ # Merged class - add to classes list
1177
+ result['classes'].append(member._class.name)
1178
+ else:
1179
+ # Regular variable
1180
+ result['vars'].append(name)
1122
1181
  return result
1123
1182
 
1124
1183
  # Handle Python objects
@@ -1197,6 +1256,50 @@ class CSSLBuiltins:
1197
1256
  # Otherwise, check if the object is not None (for $name or instance<"name">)
1198
1257
  return name_or_obj is not None
1199
1258
 
1259
+ # ============= Filter Registration Functions =============
1260
+
1261
+ def builtin_filter_register(self, filter_type: str, helper: str, callback: Any) -> bool:
1262
+ """Register a custom filter.
1263
+ Usage: filter::register("mytype", "where", myCallback)
1264
+
1265
+ The callback receives (source, filter_value, runtime) and returns filtered result.
1266
+ Use "*" as helper for catch-all.
1267
+
1268
+ Example:
1269
+ define myFilter(source, value, runtime) {
1270
+ return source + value;
1271
+ }
1272
+ filter::register("custom", "add", myFilter);
1273
+
1274
+ result <==[custom::add=10] 5; // result = 15
1275
+ """
1276
+ from .cssl_runtime import register_filter
1277
+ register_filter(filter_type, helper, callback)
1278
+ return True
1279
+
1280
+ def builtin_filter_unregister(self, filter_type: str, helper: str) -> bool:
1281
+ """Unregister a custom filter.
1282
+ Usage: filter::unregister("mytype", "where")
1283
+ """
1284
+ from .cssl_runtime import unregister_filter
1285
+ return unregister_filter(filter_type, helper)
1286
+
1287
+ def builtin_filter_list(self) -> list:
1288
+ """List all registered custom filters.
1289
+ Usage: filter::list()
1290
+ Returns list of filter keys like ["mytype::where", "custom::*"]
1291
+ """
1292
+ from .cssl_runtime import get_custom_filters
1293
+ return list(get_custom_filters().keys())
1294
+
1295
+ def builtin_filter_exists(self, filter_type: str, helper: str) -> bool:
1296
+ """Check if a custom filter exists.
1297
+ Usage: filter::exists("mytype", "where")
1298
+ """
1299
+ from .cssl_runtime import get_custom_filters
1300
+ key = f"{filter_type}::{helper}"
1301
+ return key in get_custom_filters()
1302
+
1200
1303
  # ============= Regex Functions =============
1201
1304
 
1202
1305
  def builtin_match(self, pattern: str, string: str) -> Optional[dict]:
@@ -2260,6 +2363,38 @@ class CSSLBuiltins:
2260
2363
  from .cssl_types import OpenQuote
2261
2364
  return OpenQuote(db_ref)
2262
2365
 
2366
+ def builtin_vector(self, element_type: str = 'dynamic') -> Any:
2367
+ """Create a vector container.
2368
+
2369
+ Usage: vector<int> myVector; or vector('int')
2370
+ """
2371
+ from .cssl_types import Vector
2372
+ return Vector(element_type)
2373
+
2374
+ def builtin_array(self, element_type: str = 'dynamic') -> Any:
2375
+ """Create an array container.
2376
+
2377
+ Usage: array<string> myArray; or array('string')
2378
+ """
2379
+ from .cssl_types import Array
2380
+ return Array(element_type)
2381
+
2382
+ def builtin_stack(self, element_type: str = 'dynamic') -> Any:
2383
+ """Create a stack container.
2384
+
2385
+ Usage: stack<int> myStack; or stack('int')
2386
+ """
2387
+ from .cssl_types import Stack
2388
+ return Stack(element_type)
2389
+
2390
+ def builtin_map(self, key_type: str = 'dynamic', value_type: str = 'dynamic') -> Any:
2391
+ """Create a map container.
2392
+
2393
+ Usage: map<string, int> myMap; or map('string', 'int')
2394
+ """
2395
+ from .cssl_types import Map
2396
+ return Map(key_type, value_type)
2397
+
2263
2398
  def builtin_openfind(self, combo_or_type: Any, index: int = 0, params: list = None) -> Any:
2264
2399
  """Find open parameter by type or combo space.
2265
2400
 
@@ -2082,6 +2082,85 @@ def isavailable(name_or_obj: Any) -> bool:
2082
2082
  """
2083
2083
  ...
2084
2084
 
2085
+ # =============================================================================
2086
+ # FILTER REGISTRATION FUNCTIONS (filter:: namespace)
2087
+ # =============================================================================
2088
+ # Custom filters allow extending the BruteInjection system with user-defined
2089
+ # filter types. Register filters that can then be used in injection syntax:
2090
+ # result <==[mytype::myhelper="value"] source;
2091
+
2092
+ def filter_register(filter_type: str, helper: str, callback: Callable[[Any, Any, Any], Any]) -> bool:
2093
+ """Register a custom filter.
2094
+
2095
+ Usage in CSSL: filter::register("mytype", "helper", callback)
2096
+
2097
+ Args:
2098
+ filter_type: The filter type name (e.g., "custom", "mytype")
2099
+ helper: The helper name (e.g., "where", "add") or "*" for catch-all
2100
+ callback: Function(source, filter_value, runtime) -> filtered_result
2101
+
2102
+ Example:
2103
+ // Define a custom filter callback
2104
+ define addFilter(source, value, runtime) {
2105
+ return source + value;
2106
+ }
2107
+
2108
+ // Register the filter
2109
+ filter::register("math", "add", addFilter);
2110
+
2111
+ // Use the filter
2112
+ result <==[math::add=10] 5; // result = 15
2113
+
2114
+ // Register a catch-all filter
2115
+ define catchAll(source, value, runtime) {
2116
+ printl("Filter called with: " + str(value));
2117
+ return source;
2118
+ }
2119
+ filter::register("debug", "*", catchAll);
2120
+ """
2121
+ ...
2122
+
2123
+ def filter_unregister(filter_type: str, helper: str) -> bool:
2124
+ """Unregister a custom filter.
2125
+
2126
+ Usage in CSSL: filter::unregister("mytype", "helper")
2127
+
2128
+ Returns:
2129
+ True if filter was found and removed, False otherwise
2130
+
2131
+ Example:
2132
+ filter::unregister("math", "add");
2133
+ """
2134
+ ...
2135
+
2136
+ def filter_list() -> List[str]:
2137
+ """List all registered custom filters.
2138
+
2139
+ Usage in CSSL: filter::list()
2140
+
2141
+ Returns:
2142
+ List of filter keys like ["math::add", "debug::*"]
2143
+
2144
+ Example:
2145
+ stack<string> filters = filter::list();
2146
+ foreach (f in filters) {
2147
+ printl("Registered filter: " + f);
2148
+ }
2149
+ """
2150
+ ...
2151
+
2152
+ def filter_exists(filter_type: str, helper: str) -> bool:
2153
+ """Check if a custom filter exists.
2154
+
2155
+ Usage in CSSL: filter::exists("mytype", "helper")
2156
+
2157
+ Example:
2158
+ if (filter::exists("math", "add")) {
2159
+ result <==[math::add=5] 10;
2160
+ }
2161
+ """
2162
+ ...
2163
+
2085
2164
  # =============================================================================
2086
2165
  # REGEX FUNCTIONS
2087
2166
  # =============================================================================
@@ -252,8 +252,15 @@ class CSSLLexer:
252
252
  # $<name> shared object reference
253
253
  self._read_shared_ref()
254
254
  elif char == '%':
255
- # %<name> captured reference (for infusion)
256
- self._read_captured_ref()
255
+ # Check if this is %<name> captured reference or % modulo operator
256
+ next_char = self._peek(1)
257
+ if next_char and (next_char.isalpha() or next_char == '_'):
258
+ # %<name> captured reference (for infusion)
259
+ self._read_captured_ref()
260
+ else:
261
+ # % modulo operator
262
+ self._add_token(TokenType.MODULO, '%')
263
+ self._advance()
257
264
  elif char == '&':
258
265
  # & for references
259
266
  if self._peek(1) == '&':
@@ -321,9 +328,6 @@ class CSSLLexer:
321
328
  else:
322
329
  # Already handled by // comment check above, but just in case
323
330
  self._skip_comment()
324
- elif char == '%':
325
- self._add_token(TokenType.MODULO, '%')
326
- self._advance()
327
331
  elif char == '<':
328
332
  self._read_less_than()
329
333
  elif char == '>':
@@ -848,7 +852,7 @@ class CSSLParser:
848
852
  if self._match_keyword('open'):
849
853
  param_info['open'] = True
850
854
 
851
- # Handle type annotations
855
+ # Handle type annotations (builtin types like int, string, etc.)
852
856
  if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
853
857
  param_info['type'] = self._advance().value
854
858
 
@@ -872,6 +876,39 @@ class CSSLParser:
872
876
  self._advance()
873
877
  param_info['generic'] = ''.join(generic_parts)
874
878
 
879
+ # Handle custom class types (identifier followed by another identifier = type + name)
880
+ elif self._check(TokenType.IDENTIFIER):
881
+ # Look ahead: if next token is also an identifier, current is the type
882
+ saved_pos = self.pos
883
+ potential_type = self._advance().value
884
+
885
+ # Check for generic type parameter <T> on custom type
886
+ if self._check(TokenType.COMPARE_LT):
887
+ self._advance()
888
+ generic_parts = []
889
+ depth = 1
890
+ while depth > 0 and not self._is_at_end():
891
+ if self._check(TokenType.COMPARE_LT):
892
+ depth += 1
893
+ generic_parts.append('<')
894
+ elif self._check(TokenType.COMPARE_GT):
895
+ depth -= 1
896
+ if depth > 0:
897
+ generic_parts.append('>')
898
+ elif self._check(TokenType.COMMA):
899
+ generic_parts.append(',')
900
+ else:
901
+ generic_parts.append(self._current().value)
902
+ self._advance()
903
+ param_info['generic'] = ''.join(generic_parts)
904
+
905
+ # If followed by identifier, this is "Type name" pattern
906
+ if self._check(TokenType.IDENTIFIER):
907
+ param_info['type'] = potential_type
908
+ else:
909
+ # Not a type, restore position - this is just a param name
910
+ self.pos = saved_pos
911
+
875
912
  # Handle reference operator &
876
913
  if self._match(TokenType.AMPERSAND):
877
914
  param_info['ref'] = True
@@ -1882,32 +1919,42 @@ class CSSLParser:
1882
1919
  self._expect(TokenType.BLOCK_END)
1883
1920
  return node
1884
1921
 
1885
- def _parse_injection_filter(self) -> Optional[dict]:
1886
- """Parse injection filter: [type::helper=value]"""
1887
- if not self._match(TokenType.BRACKET_START):
1922
+ def _parse_injection_filter(self) -> Optional[list]:
1923
+ """Parse injection filter(s): [type::helper=value] or [f1][f2][f3]...
1924
+
1925
+ Returns a list of filter dictionaries to support chained filters.
1926
+ """
1927
+ if not self._check(TokenType.BRACKET_START):
1888
1928
  return None
1889
1929
 
1890
- filter_info = {}
1891
- # Parse type::helper=value patterns
1892
- while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
1893
- if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1894
- filter_type = self._advance().value
1895
- if self._match(TokenType.DOUBLE_COLON):
1896
- helper = self._advance().value
1897
- if self._match(TokenType.EQUALS):
1898
- value = self._parse_expression()
1899
- filter_info[f'{filter_type}::{helper}'] = value
1930
+ filters = []
1931
+
1932
+ # Parse multiple consecutive filter brackets
1933
+ while self._match(TokenType.BRACKET_START):
1934
+ filter_info = {}
1935
+ # Parse type::helper=value patterns within this bracket
1936
+ while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
1937
+ if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1938
+ filter_type = self._advance().value
1939
+ if self._match(TokenType.DOUBLE_COLON):
1940
+ helper = self._advance().value
1941
+ if self._match(TokenType.EQUALS):
1942
+ value = self._parse_expression()
1943
+ filter_info[f'{filter_type}::{helper}'] = value
1944
+ else:
1945
+ filter_info[f'{filter_type}::{helper}'] = True
1900
1946
  else:
1901
- filter_info[f'{filter_type}::{helper}'] = True
1947
+ filter_info['type'] = filter_type
1948
+ elif self._check(TokenType.COMMA):
1949
+ self._advance()
1902
1950
  else:
1903
- filter_info['type'] = filter_type
1904
- elif self._check(TokenType.COMMA):
1905
- self._advance()
1906
- else:
1907
- break
1951
+ break
1908
1952
 
1909
- self._expect(TokenType.BRACKET_END)
1910
- return filter_info if filter_info else None
1953
+ self._expect(TokenType.BRACKET_END)
1954
+ if filter_info:
1955
+ filters.append(filter_info)
1956
+
1957
+ return filters if filters else None
1911
1958
 
1912
1959
  def _parse_expression_statement(self) -> Optional[ASTNode]:
1913
1960
  expr = self._parse_expression()
@@ -2118,6 +2165,11 @@ class CSSLParser:
2118
2165
  elif self._match(TokenType.COMPARE_GE):
2119
2166
  right = self._parse_term()
2120
2167
  left = ASTNode('binary', value={'op': '>=', 'left': left, 'right': right})
2168
+ elif self._check(TokenType.KEYWORD) and self._peek().value == 'in':
2169
+ # 'in' operator for containment: item in list
2170
+ self._advance() # consume 'in'
2171
+ right = self._parse_term()
2172
+ left = ASTNode('binary', value={'op': 'in', 'left': left, 'right': right})
2121
2173
  else:
2122
2174
  break
2123
2175
 
@@ -2504,6 +2556,20 @@ class CSSLParser:
2504
2556
 
2505
2557
  self._expect(TokenType.BLOCK_END) # consume }
2506
2558
 
2559
+ # Check for array-style initialization: vector<int>[1, 2, 3], array<string>["a", "b"]
2560
+ elif self._check(TokenType.BRACKET_START):
2561
+ self._advance() # consume [
2562
+ init_values = []
2563
+
2564
+ while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
2565
+ init_values.append(self._parse_expression())
2566
+
2567
+ # Optional comma
2568
+ if self._check(TokenType.COMMA):
2569
+ self._advance()
2570
+
2571
+ self._expect(TokenType.BRACKET_END) # consume ]
2572
+
2507
2573
  return ASTNode('type_instantiation', value={
2508
2574
  'type': name,
2509
2575
  'element_type': element_type,