IncludeCPP 4.2.2__py3-none-any.whl → 4.5.2__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 (33) hide show
  1. includecpp/CHANGELOG.md +104 -115
  2. includecpp/DOCUMENTATION.md +208 -355
  3. includecpp/__init__.py +1 -1
  4. includecpp/__init__.pyi +1 -4
  5. includecpp/cli/commands.py +1220 -27
  6. includecpp/core/cpp_api_extensions.pyi +204 -200
  7. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1505 -1467
  8. includecpp/core/cssl/__init__.py +317 -0
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/cssl_core.pyi +323 -0
  11. includecpp/core/cssl/cpp/build/libgcc_s_seh-1.dll +0 -0
  12. includecpp/core/cssl/cpp/build/libstdc++-6.dll +0 -0
  13. includecpp/core/cssl/cpp/build/libwinpthread-1.dll +0 -0
  14. includecpp/core/cssl/cpp/cssl_core.cp +108 -0
  15. includecpp/core/cssl/cpp/cssl_lexer.hpp +280 -0
  16. includecpp/core/cssl/cssl_builtins.py +245 -20
  17. includecpp/core/cssl/cssl_compiler.py +448 -0
  18. includecpp/core/cssl/cssl_optimizer.py +833 -0
  19. includecpp/core/cssl/cssl_parser.py +945 -40
  20. includecpp/core/cssl/cssl_runtime.py +751 -38
  21. includecpp/core/cssl/cssl_syntax.py +17 -0
  22. includecpp/core/cssl/cssl_types.py +321 -0
  23. includecpp/core/cssl_bridge.py +36 -2
  24. includecpp/generator/parser.cpp +38 -14
  25. includecpp/vscode/cssl/package.json +15 -0
  26. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +134 -2
  27. includecpp-4.5.2.dist-info/METADATA +277 -0
  28. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/RECORD +32 -23
  29. includecpp-4.2.2.dist-info/METADATA +0 -1008
  30. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/WHEEL +0 -0
  31. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/entry_points.txt +0 -0
  32. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/licenses/LICENSE +0 -0
  33. {includecpp-4.2.2.dist-info → includecpp-4.5.2.dist-info}/top_level.txt +0 -0
@@ -36,6 +36,8 @@ class TokenCategory(Enum):
36
36
  LIBINCLUDE_KW = auto() # libinclude (yellow/gold)
37
37
  LANG_PREFIX = auto() # Language prefix before $ (cyan): cpp$, py$, java$
38
38
  LANG_INSTANCE = auto() # Instance name after $ (orange): cpp$ClassName
39
+ # v4.6.0: C++ execution control
40
+ NATIVE_KW = auto() # native keyword (cyan/bright) - forces C++ execution
39
41
 
40
42
 
41
43
  @dataclass
@@ -65,6 +67,9 @@ KEYWORDS = {
65
67
  # v4.1.0: Multi-language keywords with special highlighting
66
68
  MULTI_LANG_KEYWORDS = {'supports', 'libinclude'}
67
69
 
70
+ # v4.6.0: C++ execution control keyword
71
+ NATIVE_KEYWORD = {'native'} # Forces C++ execution (no Python fallback)
72
+
68
73
  # v4.1.0: Language identifiers for cross-language instance access
69
74
  LANGUAGE_IDS = {'cpp', 'py', 'python', 'java', 'csharp', 'js', 'javascript'}
70
75
 
@@ -171,6 +176,12 @@ class CSSLSyntaxRules:
171
176
  category=TokenCategory.LIBINCLUDE_KW
172
177
  ))
173
178
 
179
+ # v4.6.0: 'native' keyword (cyan/bright) - forces C++ execution
180
+ rules.append(HighlightRule(
181
+ pattern=r'\bnative\b',
182
+ category=TokenCategory.NATIVE_KW
183
+ ))
184
+
174
185
  # v4.1.0: Language$Instance patterns (cpp$ClassName, py$Object)
175
186
  # Match language prefix before $ (cyan)
176
187
  rules.append(HighlightRule(
@@ -278,6 +289,8 @@ class ColorScheme:
278
289
  TokenCategory.LIBINCLUDE_KW: '#f1fa8c',# Yellow/Gold for 'libinclude'
279
290
  TokenCategory.LANG_PREFIX: '#8be9fd', # Cyan for language prefix (cpp$)
280
291
  TokenCategory.LANG_INSTANCE: '#ffb86c',# Orange for instance name ($ClassName)
292
+ # v4.6.0: C++ execution control
293
+ TokenCategory.NATIVE_KW: '#50fa7b', # Green/Cyan for 'native' (C++ forced)
281
294
  }
282
295
 
283
296
  # Light theme variant
@@ -302,6 +315,8 @@ class ColorScheme:
302
315
  TokenCategory.LIBINCLUDE_KW: '#b8860b',# DarkGoldenrod for 'libinclude'
303
316
  TokenCategory.LANG_PREFIX: '#0d6efd', # Blue for language prefix (cpp$)
304
317
  TokenCategory.LANG_INSTANCE: '#fd7e14',# Orange for instance name ($ClassName)
318
+ # v4.6.0: C++ execution control
319
+ TokenCategory.NATIVE_KW: '#198754', # Green for 'native' (C++ forced)
305
320
  }
306
321
 
307
322
 
@@ -389,6 +404,8 @@ def highlight_cssl_ansi(source: str) -> str:
389
404
  TokenCategory.LIBINCLUDE_KW: '\033[93m',# Yellow for 'libinclude'
390
405
  TokenCategory.LANG_PREFIX: '\033[96m', # Cyan for language prefix (cpp$)
391
406
  TokenCategory.LANG_INSTANCE: '\033[33m',# Orange/Yellow for instance name
407
+ # v4.6.0: C++ execution control
408
+ TokenCategory.NATIVE_KW: '\033[92m', # Bright Green for 'native'
392
409
  }
393
410
  RESET = '\033[0m'
394
411
 
@@ -1443,6 +1443,327 @@ def create_map(key_type: str = 'dynamic', value_type: str = 'dynamic') -> Map:
1443
1443
  return Map(key_type, value_type)
1444
1444
 
1445
1445
 
1446
+ class ByteArrayed:
1447
+ """Function-to-byte mapping with pattern matching (v4.2.5).
1448
+
1449
+ Maps function references to byte positions and executes pattern matching
1450
+ based on function return values.
1451
+
1452
+ Usage:
1453
+ bytearrayed MyBytes {
1454
+ &func1; // Position 0x0
1455
+ &func2; // Position 0x1
1456
+ case {0, 1} { // Match when func1=0, func2=1
1457
+ printl("Match!");
1458
+ }
1459
+ default {
1460
+ printl("No match");
1461
+ }
1462
+ }
1463
+
1464
+ MyBytes(); // Execute pattern matching
1465
+ x = MyBytes["0x0"]; // Get value at position 0
1466
+ x = MyBytes[0]; // Get value at position 0
1467
+ """
1468
+
1469
+ def __init__(self, name: str, func_refs: List[Dict], cases: List[Dict],
1470
+ default_block: Any = None, runtime: Any = None):
1471
+ self.name = name
1472
+ self.func_refs = func_refs # [{position, hex_pos, func_ref}, ...]
1473
+ self.cases = cases # [{pattern, body}, ...]
1474
+ self.default_block = default_block
1475
+ self._runtime = runtime
1476
+ self._cached_values: Dict[int, Any] = {} # Cached return values
1477
+
1478
+ def __call__(self, *args, **kwargs) -> Any:
1479
+ """Execute pattern matching - probe functions and match cases.
1480
+
1481
+ Functions are executed "invisibly" (side-effect free where possible)
1482
+ to get their current return values, then patterns are matched.
1483
+ """
1484
+ # Get current return values from all referenced functions
1485
+ values = self._probe_functions()
1486
+
1487
+ # Try to match each case pattern
1488
+ for case in self.cases:
1489
+ pattern = case['pattern']
1490
+ body = case['body']
1491
+ if self._match_pattern(pattern, values):
1492
+ return self._execute_body(body)
1493
+
1494
+ # No case matched - execute default if present
1495
+ if self.default_block:
1496
+ return self._execute_body(self.default_block)
1497
+
1498
+ return None
1499
+
1500
+ def _probe_functions(self, simulate: bool = True) -> List[Any]:
1501
+ """Probe referenced functions to get their return values.
1502
+
1503
+ v4.3.2: When simulate=True, analyzes function return statements without
1504
+ full execution. This is more precise for pattern matching.
1505
+
1506
+ Args:
1507
+ simulate: If True, analyze return values without executing.
1508
+ If False, execute functions to get actual return values.
1509
+ """
1510
+ values = []
1511
+ for ref in self.func_refs:
1512
+ func_name = ref['func_ref']
1513
+ position = ref['position']
1514
+ func_args = ref.get('args', []) # v4.3.2: Support function arguments
1515
+
1516
+ # Look up the function in runtime scope
1517
+ func = None
1518
+ if self._runtime:
1519
+ func = self._runtime.scope.get(func_name)
1520
+ if func is None:
1521
+ func = self._runtime.global_scope.get(func_name)
1522
+ if func is None:
1523
+ func = self._runtime.builtins.get_function(func_name)
1524
+
1525
+ result = None
1526
+
1527
+ if func is not None:
1528
+ try:
1529
+ # v4.3.2: Evaluate arguments if present
1530
+ evaluated_args = []
1531
+ for arg in func_args:
1532
+ if hasattr(arg, 'type'):
1533
+ evaluated_args.append(self._runtime._evaluate(arg))
1534
+ else:
1535
+ evaluated_args.append(arg)
1536
+
1537
+ if simulate and hasattr(func, 'type') and func.type == 'function':
1538
+ # v4.3.2: Simulate - analyze return statements without full execution
1539
+ result = self._simulate_function_return(func, evaluated_args)
1540
+ elif callable(func):
1541
+ result = func(*evaluated_args) if evaluated_args else func()
1542
+ elif hasattr(func, 'type') and func.type == 'function':
1543
+ # CSSL function node - execute with args
1544
+ result = self._runtime._call_function(func, evaluated_args)
1545
+ else:
1546
+ result = func
1547
+ except Exception:
1548
+ result = None
1549
+
1550
+ values.append(result)
1551
+ self._cached_values[position] = result
1552
+
1553
+ return values
1554
+
1555
+ def _simulate_function_return(self, func_node, args: List[Any] = None) -> Any:
1556
+ """Simulate a function and extract its return value without full execution.
1557
+
1558
+ v4.3.2: Analyzes the function's return statements and evaluates them
1559
+ in isolation to get precise return values for pattern matching.
1560
+ """
1561
+ if not self._runtime or not func_node:
1562
+ return None
1563
+
1564
+ # Create a temporary scope with function parameters bound to args
1565
+ func_info = func_node.value
1566
+ params = func_info.get('params', [])
1567
+ args = args or []
1568
+
1569
+ # Bind parameters to arguments in a temporary scope
1570
+ old_scope = self._runtime.scope
1571
+ # v4.3.2: Create child scope manually (Scope is a dataclass)
1572
+ from includecpp.core.cssl.cssl_runtime import Scope
1573
+ self._runtime.scope = Scope(variables={}, parent=old_scope)
1574
+
1575
+ try:
1576
+ # Bind parameters
1577
+ for i, param in enumerate(params):
1578
+ if isinstance(param, dict):
1579
+ param_name = param.get('name')
1580
+ default_value = param.get('default')
1581
+ if i < len(args):
1582
+ self._runtime.scope.set(param_name, args[i])
1583
+ elif default_value is not None:
1584
+ val = self._runtime._evaluate(default_value) if hasattr(default_value, 'type') else default_value
1585
+ self._runtime.scope.set(param_name, val)
1586
+ else:
1587
+ if i < len(args):
1588
+ self._runtime.scope.set(param, args[i])
1589
+
1590
+ # Find and evaluate the first return statement
1591
+ for child in func_node.children:
1592
+ ret_val = self._extract_return_value(child)
1593
+ if ret_val is not None:
1594
+ return ret_val
1595
+
1596
+ return None
1597
+ finally:
1598
+ # Restore original scope
1599
+ self._runtime.scope = old_scope
1600
+
1601
+ def _extract_return_value(self, node) -> Any:
1602
+ """Extract return value from a node, handling conditionals and blocks.
1603
+
1604
+ v4.3.2: Properly evaluates if/else conditions to find the correct return path.
1605
+ """
1606
+ if not hasattr(node, 'type'):
1607
+ return None
1608
+
1609
+ if node.type == 'return':
1610
+ # Found a return - evaluate it
1611
+ if node.value is None:
1612
+ return None
1613
+ if isinstance(node.value, dict) and node.value.get('multiple'):
1614
+ # Multiple return values (shuffled)
1615
+ return tuple(
1616
+ self._runtime._evaluate(v) for v in node.value.get('values', [])
1617
+ )
1618
+ return self._runtime._evaluate(node.value)
1619
+
1620
+ # v4.3.2: Handle if statements by evaluating condition
1621
+ if node.type == 'if':
1622
+ condition = node.value.get('condition')
1623
+ if condition:
1624
+ # Evaluate the condition
1625
+ cond_result = self._runtime._evaluate(condition)
1626
+ if cond_result:
1627
+ # Condition is true - check children (then block)
1628
+ if node.children:
1629
+ for child in node.children:
1630
+ ret_val = self._extract_return_value(child)
1631
+ if ret_val is not None:
1632
+ return ret_val
1633
+ else:
1634
+ # Condition is false - check else_block if present
1635
+ else_block = node.value.get('else_block')
1636
+ if else_block:
1637
+ for child in else_block:
1638
+ ret_val = self._extract_return_value(child)
1639
+ if ret_val is not None:
1640
+ return ret_val
1641
+ return None
1642
+
1643
+ # Check children for returns
1644
+ if hasattr(node, 'children') and node.children:
1645
+ for child in node.children:
1646
+ ret_val = self._extract_return_value(child)
1647
+ if ret_val is not None:
1648
+ return ret_val
1649
+
1650
+ return None
1651
+
1652
+ def _match_pattern(self, pattern: List[Dict], values: List[Any]) -> bool:
1653
+ """Check if pattern matches the current values."""
1654
+ for i, p in enumerate(pattern):
1655
+ if i >= len(values):
1656
+ return False
1657
+
1658
+ p_type = p.get('type')
1659
+ value = values[i]
1660
+
1661
+ if p_type == 'wildcard':
1662
+ # _ matches anything
1663
+ continue
1664
+ elif p_type == 'value':
1665
+ # Exact value match
1666
+ if value != p.get('value'):
1667
+ return False
1668
+ elif p_type == 'indexed':
1669
+ # Match at specific index
1670
+ idx = p.get('index')
1671
+ if isinstance(idx, str) and idx.startswith('0x'):
1672
+ idx = int(idx, 16)
1673
+ if idx < len(values):
1674
+ if values[idx] != self._runtime._evaluate(p.get('value')) if hasattr(p.get('value'), 'type') else p.get('value'):
1675
+ return False
1676
+ elif p_type == 'type_match':
1677
+ # Match by type
1678
+ type_name = p.get('type_name')
1679
+ if not self._check_type(value, type_name):
1680
+ return False
1681
+ elif p_type == 'variable':
1682
+ # Match against variable value
1683
+ var_name = p.get('name')
1684
+ var_value = self._runtime.scope.get(var_name)
1685
+ if var_value is None:
1686
+ var_value = self._runtime.global_scope.get(var_name)
1687
+ if value != var_value:
1688
+ return False
1689
+ elif p_type == 'list':
1690
+ # v4.3.2: Match against list value: ["read", "write"]
1691
+ pattern_list = p.get('values', [])
1692
+ if not isinstance(value, (list, tuple)):
1693
+ return False
1694
+ if len(value) != len(pattern_list):
1695
+ return False
1696
+ for j, pval in enumerate(pattern_list):
1697
+ if value[j] != pval:
1698
+ return False
1699
+
1700
+ return True
1701
+
1702
+ def _check_type(self, value: Any, type_name: str) -> bool:
1703
+ """Check if value matches the specified type."""
1704
+ type_checks = {
1705
+ 'int': lambda v: isinstance(v, int) and not isinstance(v, bool),
1706
+ 'float': lambda v: isinstance(v, float),
1707
+ 'string': lambda v: isinstance(v, str),
1708
+ 'bool': lambda v: isinstance(v, bool),
1709
+ 'list': lambda v: isinstance(v, list),
1710
+ 'dict': lambda v: isinstance(v, dict),
1711
+ 'dynamic': lambda v: True,
1712
+ }
1713
+ if type_name in type_checks:
1714
+ return type_checks[type_name](value)
1715
+ # Check for generic types like vector<string>
1716
+ if '<' in type_name:
1717
+ base = type_name.split('<')[0]
1718
+ return isinstance(value, (list, tuple, set))
1719
+ return True
1720
+
1721
+ def _execute_body(self, body: List) -> Any:
1722
+ """Execute a case body block."""
1723
+ if not self._runtime:
1724
+ return None
1725
+ result = None
1726
+ try:
1727
+ for node in body:
1728
+ result = self._runtime._execute_node(node)
1729
+ except Exception as e:
1730
+ # v4.3.2: Catch CSSLReturn exception by name to handle return statements
1731
+ if type(e).__name__ == 'CSSLReturn':
1732
+ return e.value
1733
+ raise
1734
+ return result
1735
+
1736
+ def __getitem__(self, key: Union[int, str]) -> Any:
1737
+ """Access byte value by index or hex position.
1738
+
1739
+ MyBytes[0] - Get value at position 0
1740
+ MyBytes["0x0"] - Get value at position 0
1741
+ """
1742
+ if isinstance(key, str):
1743
+ if key.startswith('0x') or key.startswith('0X'):
1744
+ key = int(key, 16)
1745
+ elif key.isdigit():
1746
+ key = int(key)
1747
+ else:
1748
+ raise KeyError(f"Invalid bytearrayed key: {key}")
1749
+
1750
+ if key in self._cached_values:
1751
+ return self._cached_values[key]
1752
+
1753
+ # Probe functions to get values if not cached
1754
+ if not self._cached_values:
1755
+ self._probe_functions()
1756
+
1757
+ return self._cached_values.get(key)
1758
+
1759
+ def __len__(self) -> int:
1760
+ """Return number of byte positions."""
1761
+ return len(self.func_refs)
1762
+
1763
+ def __repr__(self) -> str:
1764
+ return f"ByteArrayed({self.name}, positions={len(self.func_refs)})"
1765
+
1766
+
1446
1767
  class CSSLClass:
1447
1768
  """Represents a CSSL class definition.
1448
1769
 
@@ -410,6 +410,7 @@ class CsslLang:
410
410
  Execute CSSL code or file.
411
411
 
412
412
  This is the primary method for running CSSL code in v3.8.0+.
413
+ Uses C++ acceleration when available (375x+ faster).
413
414
 
414
415
  Args:
415
416
  path_or_code: Path to .cssl file or CSSL code string
@@ -425,22 +426,44 @@ class CsslLang:
425
426
  printl("Hello " + parameter.get(0));
426
427
  ''', "World")
427
428
  """
428
- runtime = self._get_runtime()
429
-
430
429
  # Check if it's a file path (not code)
431
430
  # Code detection: contains newlines, semicolons, or braces = definitely code
432
431
  is_likely_code = '\n' in path_or_code or ';' in path_or_code or '{' in path_or_code
433
432
  source = path_or_code
433
+ is_file = False
434
+ file_path = None
434
435
 
435
436
  if not is_likely_code:
436
437
  try:
437
438
  path = Path(path_or_code)
438
439
  if path.exists() and path.suffix in ('.cssl', '.cssl-mod', '.cssl-pl'):
440
+ is_file = True
441
+ file_path = str(path.absolute())
439
442
  source = path.read_text(encoding='utf-8')
440
443
  except OSError:
441
444
  # Path too long or invalid - treat as code
442
445
  pass
443
446
 
447
+ # Try C++ accelerated execution first (375x faster)
448
+ # Only use C++ for simple scripts without parameter passing
449
+ if not args:
450
+ try:
451
+ from .cssl import run_cssl, run_cssl_file
452
+ if is_file and file_path:
453
+ return run_cssl_file(file_path)
454
+ else:
455
+ return run_cssl(source)
456
+ except Exception as cpp_error:
457
+ # Fall back to Python for unsupported features
458
+ error_msg = str(cpp_error).lower()
459
+ if 'unsupported' not in error_msg and 'not implemented' not in error_msg:
460
+ # Real error - re-raise it
461
+ raise RuntimeError(str(cpp_error)) from cpp_error
462
+ # Otherwise fall through to Python
463
+
464
+ # Python execution (for scripts with args or when C++ fails)
465
+ runtime = self._get_runtime()
466
+
444
467
  # Set arguments in runtime scope
445
468
  from .cssl import Parameter
446
469
  param = Parameter(list(args))
@@ -459,6 +482,17 @@ class CsslLang:
459
482
  return returns[0] if len(returns) == 1 else returns
460
483
 
461
484
  return result
485
+ except UnicodeEncodeError as e:
486
+ # v4.3.2: Catch unicode/emoji encoding errors and provide helpful message
487
+ char = e.object[e.start:e.end] if hasattr(e, 'object') else '?'
488
+ error_msg = (
489
+ f"Unicode encoding error: Character '{char}' cannot be displayed.\n"
490
+ f" The console encoding ({e.encoding}) doesn't support this character.\n\n"
491
+ f" Hint: Use encode() to safely handle emojis/unicode:\n"
492
+ f" printl(\"Status: \" + encode(\"{char}\"));\n"
493
+ f" printl(encode(\"Your text with emojis\", \"[emoji]\"));"
494
+ )
495
+ raise RuntimeError(error_msg) from e
462
496
  except Exception as e:
463
497
  # Format error message nicely - don't add prefixes, let CLI handle that
464
498
  error_msg = str(e)
@@ -992,19 +992,32 @@ std::string generate_struct_bindings(const StructBinding& sb, const ModuleDescri
992
992
  code << " })\n";
993
993
 
994
994
  // Auto-generate from_dict() static method
995
- code << " .def_static(\"from_dict\", [](py::dict d) {\n";
996
- code << " " << cpp_type << " obj;\n";
995
+ // v4.3.2: Check if all fields have known types (not "auto")
996
+ bool template_all_types_known = true;
997
997
  for (const auto& [field_type, field_name] : sb.fields) {
998
998
  std::string actual_type = field_type;
999
- if (actual_type == "T") {
1000
- actual_type = ttype;
999
+ if (actual_type == "T") actual_type = ttype;
1000
+ if (actual_type == "auto" || actual_type.empty()) {
1001
+ template_all_types_known = false;
1002
+ break;
1001
1003
  }
1004
+ }
1002
1005
 
1003
- code << " obj." << field_name << " = d[\"" << field_name
1004
- << "\"].cast<" << actual_type << ">();\n";
1006
+ if (template_all_types_known && !sb.fields.empty()) {
1007
+ code << " .def_static(\"from_dict\", [](py::dict d) {\n";
1008
+ code << " " << cpp_type << " obj;\n";
1009
+ for (const auto& [field_type, field_name] : sb.fields) {
1010
+ std::string actual_type = field_type;
1011
+ if (actual_type == "T") {
1012
+ actual_type = ttype;
1013
+ }
1014
+
1015
+ code << " obj." << field_name << " = d[\"" << field_name
1016
+ << "\"].cast<" << actual_type << ">();\n";
1017
+ }
1018
+ code << " return obj;\n";
1019
+ code << " })\n";
1005
1020
  }
1006
- code << " return obj;\n";
1007
- code << " })\n";
1008
1021
 
1009
1022
  // v2.8: Add __repr__ for better debugging output
1010
1023
  code << " .def(\"__repr__\", [](" << cpp_type << "& self) {\n";
@@ -1052,14 +1065,25 @@ std::string generate_struct_bindings(const StructBinding& sb, const ModuleDescri
1052
1065
  code << " })\n";
1053
1066
 
1054
1067
  // Auto-generate from_dict() static method
1055
- code << " .def_static(\"from_dict\", [](py::dict d) {\n";
1056
- code << " " << sb.struct_name << " obj;\n";
1068
+ // v4.3.2: Check if all fields have known types (not "auto")
1069
+ bool all_types_known = true;
1057
1070
  for (const auto& [field_type, field_name] : sb.fields) {
1058
- code << " obj." << field_name << " = d[\"" << field_name
1059
- << "\"].cast<" << field_type << ">();\n";
1071
+ if (field_type == "auto" || field_type.empty()) {
1072
+ all_types_known = false;
1073
+ break;
1074
+ }
1075
+ }
1076
+
1077
+ if (all_types_known && !sb.fields.empty()) {
1078
+ code << " .def_static(\"from_dict\", [](py::dict d) {\n";
1079
+ code << " " << sb.struct_name << " obj;\n";
1080
+ for (const auto& [field_type, field_name] : sb.fields) {
1081
+ code << " obj." << field_name << " = d[\"" << field_name
1082
+ << "\"].cast<" << field_type << ">();\n";
1083
+ }
1084
+ code << " return obj;\n";
1085
+ code << " })\n";
1060
1086
  }
1061
- code << " return obj;\n";
1062
- code << " })\n";
1063
1087
 
1064
1088
  // v2.8: Add __repr__ for better debugging output
1065
1089
  code << " .def(\"__repr__\", [](" << sb.struct_name << "& self) {\n";
@@ -56,6 +56,16 @@
56
56
  "light": "./images/cssl_pl.png",
57
57
  "dark": "./images/cssl_pl.png"
58
58
  }
59
+ },
60
+ {
61
+ "id": "cp",
62
+ "aliases": ["IncludeCPP Plugin", "cp"],
63
+ "extensions": [".cp"],
64
+ "configuration": "./language-configuration.json",
65
+ "icon": {
66
+ "light": "./images/cssl.png",
67
+ "dark": "./images/cssl.png"
68
+ }
59
69
  }
60
70
  ],
61
71
  "grammars": [
@@ -73,6 +83,11 @@
73
83
  "language": "cssl-pl",
74
84
  "scopeName": "source.cssl",
75
85
  "path": "./syntaxes/cssl.tmLanguage.json"
86
+ },
87
+ {
88
+ "language": "cp",
89
+ "scopeName": "source.cssl",
90
+ "path": "./syntaxes/cssl.tmLanguage.json"
76
91
  }
77
92
  ],
78
93
  "snippets": [