IncludeCPP 3.7.21__py3-none-any.whl → 3.7.23__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.
includecpp/__init__.py CHANGED
@@ -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.21"
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")
@@ -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 == '>':
@@ -1915,32 +1919,42 @@ class CSSLParser:
1915
1919
  self._expect(TokenType.BLOCK_END)
1916
1920
  return node
1917
1921
 
1918
- def _parse_injection_filter(self) -> Optional[dict]:
1919
- """Parse injection filter: [type::helper=value]"""
1920
- 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):
1921
1928
  return None
1922
1929
 
1923
- filter_info = {}
1924
- # Parse type::helper=value patterns
1925
- while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
1926
- if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1927
- filter_type = self._advance().value
1928
- if self._match(TokenType.DOUBLE_COLON):
1929
- helper = self._advance().value
1930
- if self._match(TokenType.EQUALS):
1931
- value = self._parse_expression()
1932
- 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
1933
1946
  else:
1934
- filter_info[f'{filter_type}::{helper}'] = True
1947
+ filter_info['type'] = filter_type
1948
+ elif self._check(TokenType.COMMA):
1949
+ self._advance()
1935
1950
  else:
1936
- filter_info['type'] = filter_type
1937
- elif self._check(TokenType.COMMA):
1938
- self._advance()
1939
- else:
1940
- break
1951
+ break
1941
1952
 
1942
- self._expect(TokenType.BRACKET_END)
1943
- 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
1944
1958
 
1945
1959
  def _parse_expression_statement(self) -> Optional[ASTNode]:
1946
1960
  expr = self._parse_expression()
@@ -1331,8 +1331,11 @@ class CSSLRuntime:
1331
1331
 
1332
1332
  return command_name
1333
1333
 
1334
- def _apply_injection_filter(self, source: Any, filter_info: dict) -> Any:
1335
- """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.
1336
1339
 
1337
1340
  All BruteInjector Helpers:
1338
1341
  - string::where=VALUE - Filter strings containing VALUE
@@ -1359,6 +1362,21 @@ class CSSLRuntime:
1359
1362
  if not filter_info:
1360
1363
  return source
1361
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
+
1362
1380
  result = source
1363
1381
 
1364
1382
  for filter_key, filter_value in filter_info.items():
@@ -1554,13 +1572,112 @@ class CSSLRuntime:
1554
1572
 
1555
1573
  # === DYNAMIC HELPERS ===
1556
1574
  elif filter_type == 'dynamic':
1557
- # dynamic::VarName=VALUE - Match if variable equals value
1558
- var_name = helper
1559
- var_value = self.scope.get(var_name)
1560
- if var_value == filter_val:
1561
- 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
1562
1673
  else:
1563
- 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
1564
1681
 
1565
1682
  # === SQL HELPERS ===
1566
1683
  elif filter_type == 'sql':
@@ -1853,14 +1970,36 @@ class CSSLRuntime:
1853
1970
  final_value = [current_value, source]
1854
1971
  elif mode == 'move':
1855
1972
  final_value = source
1856
- # Clear the source - handle all node types
1973
+ # Remove filtered elements from source (not clear entirely)
1857
1974
  if isinstance(source_node, ASTNode):
1858
- if source_node.type == 'identifier':
1859
- self.scope.set(source_node.value, None)
1860
- elif source_node.type == 'module_ref':
1861
- self._set_module_value(source_node.value, None)
1862
- elif source_node.type == 'member_access':
1863
- self._set_member(source_node, None)
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)
1864
2003
  else:
1865
2004
  final_value = source
1866
2005
 
@@ -1883,6 +2022,42 @@ class CSSLRuntime:
1883
2022
  self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
1884
2023
  elif target.type == 'member_access':
1885
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)
1886
2061
 
1887
2062
  return final_value
1888
2063
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.7.21
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,9 +1,9 @@
1
- includecpp/__init__.py,sha256=hYkRPXgViZO8YrWxqA7IX284xwW81zMDqqjHUgMlOEM,1673
1
+ includecpp/__init__.py,sha256=zlBkXO5fb20CjZcrm3aTHXIxpDg3fCujzh3dqpZHM8o,1673
2
2
  includecpp/__init__.pyi,sha256=uSDYlbqd2TinmrdepmE_zvN25jd3Co2cgyPzXgDpopM,7193
3
3
  includecpp/__main__.py,sha256=d6QK0PkvUe1ENofpmHRAg3bwNbZr8PiRscfI3-WRfVg,72
4
4
  includecpp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  includecpp/cli/__init__.py,sha256=Yda-4a5QJb_tKu35YQNfc5lu-LewTsM5abqNNkzS47M,113
6
- includecpp/cli/commands.py,sha256=AeHirETVkzNqNcqh5RVO0ABPz_6gfuV2EpHcWK9BuDY,349032
6
+ includecpp/cli/commands.py,sha256=HDoRuPEPTzxZQcR4rF45GrRVtcw3zDj6cGiwLCZB44Y,349597
7
7
  includecpp/cli/config_parser.py,sha256=KveeYUg2TA9sC5hKVzYYfgdNm2WfLG5y7_yxgBWn9yM,4886
8
8
  includecpp/core/__init__.py,sha256=L1bT6oikTjdto-6Px7DpjePtM07ymo3Bnov1saZzsGg,390
9
9
  includecpp/core/ai_integration.py,sha256=PW6yFDqdXjfchpfKTKg59AOLhLry9kqJEGf_65BztrY,87603
@@ -25,8 +25,8 @@ includecpp/core/cssl/cssl_builtins.py,sha256=r-FX4WQeKxerkepqodIiwhtL_kxxa4PJym_
25
25
  includecpp/core/cssl/cssl_builtins.pyi,sha256=3ai2V4LyhzPBhAKjRRf0rLVu_bg9ECmTfTkdFKM64iA,127430
26
26
  includecpp/core/cssl/cssl_events.py,sha256=nupIcXW_Vjdud7zCU6hdwkQRQ0MujlPM7Tk2u7eDAiY,21013
27
27
  includecpp/core/cssl/cssl_modules.py,sha256=cUg0-zdymMnWWTsA_BUrW5dx4R04dHpKcUhm-Wfiwwo,103006
28
- includecpp/core/cssl/cssl_parser.py,sha256=SaFgehSB2300s4DtC_v1lmVyaBpg2jIRZcEeEiJ9E_o,115774
29
- includecpp/core/cssl/cssl_runtime.py,sha256=EETUsUOZL7PZ1a9Chz0JeJFP0w68DtSthoqSebigxrw,144834
28
+ includecpp/core/cssl/cssl_parser.py,sha256=JJ5mKbKCgevOmShxSpEKpKzo79rjU2_qvnBfxsWlH_w,116431
29
+ includecpp/core/cssl/cssl_runtime.py,sha256=iZgNcKDoTeV50-IsXcsrpuTyDPxEHWyQz5uR3s5UVHg,154613
30
30
  includecpp/core/cssl/cssl_syntax.py,sha256=bgo3NFehoPTQa5dqwNd_CstkVGVCNYAXbGYUcu5BEN0,16982
31
31
  includecpp/core/cssl/cssl_types.py,sha256=gVjtfxk0Uzfj-H7_LD79oqspFMYDf79ZrRrlZk8eAEs,50647
32
32
  includecpp/generator/__init__.py,sha256=Rsy41bwimaEloD3gDRR_znPfIJzIsCFuWZgCTJBLJlc,62
@@ -43,9 +43,9 @@ includecpp/vscode/cssl/images/cssl.png,sha256=BxAGsnfS0ZzzCvqV6Zb1OAJAZpDUoXlR86
43
43
  includecpp/vscode/cssl/images/cssl_pl.png,sha256=z4WMk7g6YCTbUUbSFk343BO6yi_OmNEVYkRenWGydwM,799
44
44
  includecpp/vscode/cssl/snippets/cssl.snippets.json,sha256=l4SCEPR3CsPxA8HIVLKYY__I979TfKzYWtH1KYIsboo,33062
45
45
  includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json,sha256=XKRLBOHlCqDDnGPvmNHDQPsIMR1UD9PBaJIlgZOiPqM,21173
46
- includecpp-3.7.21.dist-info/licenses/LICENSE,sha256=fWCsGGsiWZir0UzDd20Hh-3wtRyk1zqUntvtVuAWhvc,1093
47
- includecpp-3.7.21.dist-info/METADATA,sha256=3M6Vqu-_8hFmQBEst3XiQFaISZ70aTT2BGahWg-Bmpk,32122
48
- includecpp-3.7.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- includecpp-3.7.21.dist-info/entry_points.txt,sha256=6A5Mif9gi0139Bf03W5plAb3wnAgbNaEVe1HJoGE-2o,59
50
- includecpp-3.7.21.dist-info/top_level.txt,sha256=RFUaR1KG-M6mCYwP6w4ydP5Cgc8yNbP78jxGAvyjMa8,11
51
- includecpp-3.7.21.dist-info/RECORD,,
46
+ includecpp-3.7.23.dist-info/licenses/LICENSE,sha256=fWCsGGsiWZir0UzDd20Hh-3wtRyk1zqUntvtVuAWhvc,1093
47
+ includecpp-3.7.23.dist-info/METADATA,sha256=v4tDVGdh6c2ejyc9w_gUj6LTa0kzrjExECJkyqS-Qd4,32122
48
+ includecpp-3.7.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ includecpp-3.7.23.dist-info/entry_points.txt,sha256=6A5Mif9gi0139Bf03W5plAb3wnAgbNaEVe1HJoGE-2o,59
50
+ includecpp-3.7.23.dist-info/top_level.txt,sha256=RFUaR1KG-M6mCYwP6w4ydP5Cgc8yNbP78jxGAvyjMa8,11
51
+ includecpp-3.7.23.dist-info/RECORD,,