IncludeCPP 3.7.2__tar.gz → 3.7.6__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 (57) hide show
  1. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/PKG-INFO +1 -1
  2. {includecpp-3.7.2 → includecpp-3.7.6}/PKG-INFO +1 -1
  3. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/__init__.py +1 -1
  4. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/cli/commands.py +82 -63
  5. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/CSSL_DOCUMENTATION.md +27 -8
  6. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_parser.py +174 -11
  7. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_runtime.py +183 -15
  8. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl_bridge.py +477 -48
  9. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/cssl/language-configuration.json +1 -4
  10. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +96 -11
  11. {includecpp-3.7.2 → includecpp-3.7.6}/pyproject.toml +1 -1
  12. {includecpp-3.7.2 → includecpp-3.7.6}/setup.py +1 -1
  13. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  14. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  15. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/entry_points.txt +0 -0
  16. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/requires.txt +0 -0
  17. {includecpp-3.7.2 → includecpp-3.7.6}/IncludeCPP.egg-info/top_level.txt +0 -0
  18. {includecpp-3.7.2 → includecpp-3.7.6}/LICENSE +0 -0
  19. {includecpp-3.7.2 → includecpp-3.7.6}/MANIFEST.in +0 -0
  20. {includecpp-3.7.2 → includecpp-3.7.6}/README.md +0 -0
  21. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/__init__.pyi +0 -0
  22. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/__main__.py +0 -0
  23. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/cli/__init__.py +0 -0
  24. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/cli/config_parser.py +0 -0
  25. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/__init__.py +0 -0
  26. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/ai_integration.py +0 -0
  27. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/build_manager.py +0 -0
  28. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cpp_api.py +0 -0
  29. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cpp_api.pyi +0 -0
  30. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cppy_converter.py +0 -0
  31. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/__init__.py +0 -0
  32. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_builtins.py +0 -0
  33. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_builtins.pyi +0 -0
  34. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_events.py +0 -0
  35. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_modules.py +0 -0
  36. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_syntax.py +0 -0
  37. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl/cssl_types.py +0 -0
  38. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/cssl_bridge.pyi +0 -0
  39. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/error_catalog.py +0 -0
  40. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/error_formatter.py +0 -0
  41. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/exceptions.py +0 -0
  42. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/path_discovery.py +0 -0
  43. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/project_ui.py +0 -0
  44. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/core/settings_ui.py +0 -0
  45. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/generator/__init__.py +0 -0
  46. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/generator/parser.cpp +0 -0
  47. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/generator/parser.h +0 -0
  48. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/generator/type_resolver.cpp +0 -0
  49. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/generator/type_resolver.h +0 -0
  50. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/py.typed +0 -0
  51. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/templates/cpp.proj.template +0 -0
  52. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/__init__.py +0 -0
  53. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/cssl/__init__.py +0 -0
  54. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/cssl/package.json +0 -0
  55. {includecpp-3.7.2 → includecpp-3.7.6}/includecpp/vscode/cssl/snippets/cssl.snippets.json +0 -0
  56. {includecpp-3.7.2 → includecpp-3.7.6}/requirements.txt +0 -0
  57. {includecpp-3.7.2 → includecpp-3.7.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.7.2
3
+ Version: 3.7.6
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.2
3
+ Version: 3.7.6
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.2"
5
+ __version__ = "3.7.6"
6
6
  __all__ = ["CppApi", "CSSL"]
7
7
 
8
8
  # Module-level cache for C++ modules
@@ -7480,13 +7480,12 @@ def cssl_exec(path, code):
7480
7480
  # Execute
7481
7481
  click.secho("--- Output ---", fg='green')
7482
7482
  try:
7483
- result = cssl_lang.exec(source)
7483
+ result = cssl_lang.run(source)
7484
7484
 
7485
7485
  # Output is already printed to stdout during execution via runtime.output()
7486
- # No need to print buffer again - this was causing double output
7487
-
7488
- if result is not None:
7489
- click.echo(f"Result: {result}")
7486
+ # Don't print "Result:" automatically - users should use printl() for output
7487
+ # This prevents unwanted output for function calls like: Function();
7488
+ pass
7490
7489
 
7491
7490
  except Exception as e:
7492
7491
  click.secho(f"CSSL Error: {e}", fg='red')
@@ -7595,75 +7594,95 @@ def cssl_doc(search, list_sections):
7595
7594
  click.secho("=" * 50, fg='cyan')
7596
7595
  click.echo()
7597
7596
 
7598
- # Split into sections (## headers)
7599
- sections = re.split(r'(?=^## )', content, flags=re.MULTILINE)
7600
-
7601
- found_sections = []
7602
- for section in sections:
7603
- if search.lower() in section.lower():
7604
- found_sections.append(section)
7605
-
7606
- if found_sections:
7607
- # Also find specific lines with the search term
7608
- all_lines = content.split('\n')
7609
- matching_lines = []
7610
- for i, line in enumerate(all_lines):
7611
- if search.lower() in line.lower():
7612
- # Get context (2 lines before and after)
7613
- start = max(0, i - 2)
7614
- end = min(len(all_lines), i + 3)
7615
- context = '\n'.join(all_lines[start:end])
7616
- if context not in matching_lines:
7617
- matching_lines.append((i + 1, context))
7618
-
7619
- # Show summary
7620
- click.secho(f"Found in {len(found_sections)} section(s):", fg='green')
7621
- for section in found_sections:
7622
- # Extract section title
7623
- title_match = re.match(r'^##\s+(.+)$', section, re.MULTILINE)
7624
- if title_match:
7625
- click.echo(f" - {title_match.group(1)}")
7597
+ # Split into subsections (### headers) for focused results
7598
+ subsections = re.split(r'(?=^### )', content, flags=re.MULTILINE)
7626
7599
 
7627
- click.echo()
7600
+ # Also split into main sections (## headers)
7601
+ main_sections = re.split(r'(?=^## )', content, flags=re.MULTILINE)
7628
7602
 
7629
- # Show matching contexts with highlighting
7630
- click.secho("Matching content:", fg='yellow', bold=True)
7631
- click.secho("-" * 50, fg='yellow')
7603
+ # Find matching subsections (### level) - most focused
7604
+ matching_subsections = []
7605
+ for subsection in subsections:
7606
+ if search.lower() in subsection.lower():
7607
+ # Extract title
7608
+ title_match = re.match(r'^###\s+(.+)$', subsection, re.MULTILINE)
7609
+ if title_match:
7610
+ # Trim subsection to just the content until next ### or ##
7611
+ lines = subsection.split('\n')
7612
+ trimmed_lines = []
7613
+ for line in lines:
7614
+ if line.startswith('## ') and not line.startswith('### '):
7615
+ break
7616
+ trimmed_lines.append(line)
7617
+ matching_subsections.append((title_match.group(1), '\n'.join(trimmed_lines)))
7618
+
7619
+ if matching_subsections:
7620
+ click.secho(f"Found {len(matching_subsections)} matching subsection(s):", fg='green')
7621
+ click.echo()
7632
7622
 
7633
- for line_num, context in matching_lines[:15]: # Limit to 15 matches
7634
- click.echo(f"\nLine {line_num}:")
7635
- # Highlight the search term
7623
+ # Show focused subsections (limit output)
7624
+ for title, sub_content in matching_subsections[:5]:
7625
+ click.secho(f"### {title}", fg='yellow', bold=True)
7626
+ # Highlight search term in content
7636
7627
  highlighted = re.sub(
7637
7628
  f'({re.escape(search)})',
7638
7629
  click.style(r'\1', fg='green', bold=True),
7639
- context,
7630
+ sub_content,
7640
7631
  flags=re.IGNORECASE
7641
7632
  )
7642
- click.echo(highlighted)
7643
- click.echo("-" * 30)
7644
-
7645
- if len(matching_lines) > 15:
7646
- click.echo(f"\n... and {len(matching_lines) - 15} more matches")
7647
-
7648
- # Offer to show full sections
7649
- click.echo()
7650
- click.secho("Full sections containing your search:", fg='cyan')
7651
- click.echo("(Scroll or pipe to a file for full content)")
7652
- click.echo()
7653
-
7654
- for section in found_sections:
7655
- click.echo(section)
7633
+ # Limit lines per subsection
7634
+ lines = highlighted.split('\n')
7635
+ if len(lines) > 30:
7636
+ click.echo('\n'.join(lines[:30]))
7637
+ click.secho(f" ... ({len(lines) - 30} more lines)", fg='cyan')
7638
+ else:
7639
+ click.echo(highlighted)
7640
+ click.echo()
7641
+ click.secho("-" * 40, fg='cyan')
7656
7642
  click.echo()
7657
7643
 
7644
+ if len(matching_subsections) > 5:
7645
+ click.secho(f"... and {len(matching_subsections) - 5} more subsections", fg='cyan')
7646
+ click.echo("Use --list to see all sections")
7658
7647
  else:
7659
- click.secho(f"No matches found for '{search}'", fg='yellow')
7660
- click.echo()
7661
- click.echo("Try searching for:")
7662
- click.echo(" - Keywords: class, function, define, open, global")
7663
- click.echo(" - Syntax: $, @, ::, this->, <<==, <==")
7664
- click.echo(" - Types: string, int, stack, vector, map, json")
7665
- click.echo()
7666
- click.echo("Or use: includecpp cssl doc --list")
7648
+ # Fall back to main section search (## level)
7649
+ found_sections = []
7650
+ for section in main_sections:
7651
+ if search.lower() in section.lower():
7652
+ title_match = re.match(r'^##\s+(.+)$', section, re.MULTILINE)
7653
+ if title_match:
7654
+ found_sections.append((title_match.group(1), section))
7655
+
7656
+ if found_sections:
7657
+ click.secho(f"Found in {len(found_sections)} section(s):", fg='green')
7658
+ for title, _ in found_sections:
7659
+ click.echo(f" - {title}")
7660
+ click.echo()
7661
+
7662
+ # Show first matching section, trimmed
7663
+ title, section = found_sections[0]
7664
+ click.secho(f"## {title}", fg='yellow', bold=True)
7665
+ highlighted = re.sub(
7666
+ f'({re.escape(search)})',
7667
+ click.style(r'\1', fg='green', bold=True),
7668
+ section,
7669
+ flags=re.IGNORECASE
7670
+ )
7671
+ lines = highlighted.split('\n')
7672
+ if len(lines) > 40:
7673
+ click.echo('\n'.join(lines[:40]))
7674
+ click.secho(f"\n... ({len(lines) - 40} more lines in this section)", fg='cyan')
7675
+ else:
7676
+ click.echo(highlighted)
7677
+ else:
7678
+ click.secho(f"No matches found for '{search}'", fg='yellow')
7679
+ click.echo()
7680
+ click.echo("Try searching for:")
7681
+ click.echo(" - Keywords: class, function, define, open, global, shuffled")
7682
+ click.echo(" - Syntax: $, @, ::, this->, <<==, <==, #$")
7683
+ click.echo(" - Types: string, int, stack, vector, map, json")
7684
+ click.echo()
7685
+ click.echo("Or use: includecpp cssl doc --list")
7667
7686
  else:
7668
7687
  # Full documentation mode
7669
7688
  click.echo_via_pager(content)
@@ -1,6 +1,6 @@
1
1
  # CSSL - C-Style Scripting Language
2
2
 
3
- > Version 3.7.2 | A modern scripting language with C++-style syntax and unique features like CodeInfusion and BruteInjection.
3
+ > Version 3.7.6 | A modern scripting language with C++-style syntax and unique features like CodeInfusion and BruteInjection.
4
4
 
5
5
  ---
6
6
 
@@ -41,22 +41,25 @@
41
41
  ```python
42
42
  from includecpp import CSSL
43
43
 
44
- # Initialize CSSL
45
- CSSL.CsslLang()
46
-
47
- # Execute code
48
- CSSL.exec("""
44
+ # Execute code (v3.7.6+: use run() instead of exec())
45
+ CSSL.run("""
49
46
  printl("Hello CSSL!");
50
47
  """)
51
48
 
52
49
  # With parameters and return value
53
- result = CSSL.exec("""
50
+ result = CSSL.run("""
54
51
  string name = parameter.get(0);
55
52
  printl("Hello " + name);
56
53
  parameter.return(true);
57
54
  """, "World")
58
55
 
59
56
  print(result) # True
57
+
58
+ # Create typed scripts and modules (v3.7.6+)
59
+ main = CSSL.script("cssl", '''printl("Main");''')
60
+ payload = CSSL.script("cssl-pl", '''void helper() { printl("Helper!"); }''')
61
+ mod = CSSL.makemodule(main, payload, "mymod")
62
+ mod.helper() # Call function directly
60
63
  ```
61
64
 
62
65
  ### CLI Execution
@@ -701,12 +704,24 @@ super void forceRun() {
701
704
 
702
705
  ### shuffled
703
706
 
704
- Allows multiple return values.
707
+ Allows multiple return values with tuple unpacking.
705
708
 
706
709
  ```cssl
707
710
  shuffled string getNames() {
708
711
  return "Alice", "Bob", "Charlie";
709
712
  }
713
+
714
+ // Tuple unpacking (v3.7.6+)
715
+ a, b, c = getNames();
716
+ printl(a); // "Alice"
717
+ printl(b); // "Bob"
718
+ printl(c); // "Charlie"
719
+
720
+ // Works with any types
721
+ shuffled getValues() {
722
+ return "text", 42, true;
723
+ }
724
+ name, num, flag = getValues();
710
725
  ```
711
726
 
712
727
  ---
@@ -1143,6 +1158,10 @@ print(stats.total) # 50 - Persisted!
1143
1158
 
1144
1159
  CodeInfusion enables modifying functions at runtime.
1145
1160
 
1161
+ > **Important**: Injection operators must be written **without spaces**:
1162
+ > - ✓ `func() <<==` / `func() +<<==` / `func() -<<==` (correct)
1163
+ > - ✗ `func() < <==` / `func() + <<==` / `func() - <<==` (wrong)
1164
+
1146
1165
  ### <<== (Replace)
1147
1166
 
1148
1167
  Replaces function content.
@@ -108,6 +108,8 @@ class TokenType(Enum):
108
108
  COMMENT = auto()
109
109
  NEWLINE = auto()
110
110
  EOF = auto()
111
+ # Super-functions for .cssl-pl payload files (v3.8.0)
112
+ SUPER_FUNC = auto() # #$run(), #$exec(), #$printl() - pre-execution hooks
111
113
 
112
114
 
113
115
  KEYWORDS = {
@@ -212,9 +214,14 @@ class CSSLLexer:
212
214
 
213
215
  char = self.source[self.pos]
214
216
 
215
- # Comments: both # and // style
217
+ # Super-functions (#$) or Comments (# and // style)
216
218
  if char == '#':
217
- self._skip_comment()
219
+ if self._peek(1) == '$':
220
+ # Super-function: #$run(), #$exec(), #$printl()
221
+ self._read_super_function()
222
+ else:
223
+ # Regular comment
224
+ self._skip_comment()
218
225
  elif char == '/' and self._peek(1) == '/':
219
226
  # C-style // comment - NEW
220
227
  self._skip_comment()
@@ -446,6 +453,31 @@ class CSSLLexer:
446
453
  else:
447
454
  self._add_token(TokenType.IDENTIFIER, value)
448
455
 
456
+ def _read_super_function(self):
457
+ """Read #$<name>(...) super-function call for .cssl-pl payloads.
458
+
459
+ Super-functions are pre-execution hooks that run when a payload is loaded.
460
+ Valid super-functions: #$run(), #$exec(), #$printl()
461
+
462
+ Syntax:
463
+ #$run(initFunction); // Call a function at load time
464
+ #$exec(setup()); // Execute expression at load time
465
+ #$printl("Payload loaded"); // Print at load time
466
+ """
467
+ start = self.pos
468
+ self._advance() # skip '#'
469
+ self._advance() # skip '$'
470
+
471
+ # Read the super-function name (run, exec, printl, etc.)
472
+ name_start = self.pos
473
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
474
+ self._advance()
475
+ func_name = self.source[name_start:self.pos]
476
+
477
+ # Store as #$<name> token value
478
+ value = f'#${func_name}'
479
+ self._add_token(TokenType.SUPER_FUNC, value)
480
+
449
481
  def _read_self_ref(self):
450
482
  """Read s@<name> or s@<name>.<member>... self-reference"""
451
483
  start = self.pos
@@ -1377,6 +1409,9 @@ class CSSLParser:
1377
1409
  elif self._looks_like_function_declaration():
1378
1410
  # Nested typed function (e.g., void Level2() { ... })
1379
1411
  return self._parse_typed_function()
1412
+ elif self._check(TokenType.SUPER_FUNC):
1413
+ # Super-function for .cssl-pl payload files
1414
+ return self._parse_super_function()
1380
1415
  elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1381
1416
  self._check(TokenType.CAPTURED_REF) or self._check(TokenType.SHARED_REF) or
1382
1417
  self._check(TokenType.GLOBAL_REF) or self._check(TokenType.SELF_REF) or
@@ -1594,23 +1629,42 @@ class CSSLParser:
1594
1629
  return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
1595
1630
 
1596
1631
  def _parse_python_style_for(self) -> ASTNode:
1597
- """Parse Python-style for loop: for (i in range(...)) { }
1632
+ """Parse Python-style for loop: for (i in range(...)) { } or for (item in collection) { }
1598
1633
 
1599
1634
  Supports:
1600
1635
  for (i in range(n)) { } - 0 to n-1
1601
1636
  for (i in range(start, end)) { } - start to end-1
1602
1637
  for (i in range(start, end, step)) { }
1638
+ for (item in collection) { } - iterate over list/vector
1639
+ for (item in @global_collection) { } - iterate over global
1603
1640
  """
1604
1641
  var_name = self._advance().value
1605
1642
  self._expect(TokenType.KEYWORD) # 'in'
1606
1643
 
1607
- # 'range' can be keyword or identifier
1644
+ # Check if this is range() or collection iteration
1645
+ is_range = False
1608
1646
  if self._check(TokenType.KEYWORD) and self._peek().value == 'range':
1609
1647
  self._advance() # consume 'range' keyword
1648
+ is_range = True
1610
1649
  elif self._check(TokenType.IDENTIFIER) and self._peek().value == 'range':
1611
1650
  self._advance() # consume 'range' identifier
1612
- else:
1613
- self.error(f"Expected 'range', got {self._peek().value}")
1651
+ is_range = True
1652
+
1653
+ # If not range, parse as collection iteration
1654
+ if not is_range:
1655
+ iterable = self._parse_expression()
1656
+ self._expect(TokenType.PAREN_END)
1657
+
1658
+ node = ASTNode('foreach', value={'var': var_name, 'iterable': iterable}, children=[])
1659
+ self._expect(TokenType.BLOCK_START)
1660
+
1661
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1662
+ stmt = self._parse_statement()
1663
+ if stmt:
1664
+ node.children.append(stmt)
1665
+
1666
+ self._expect(TokenType.BLOCK_END)
1667
+ return node
1614
1668
 
1615
1669
  self._expect(TokenType.PAREN_START)
1616
1670
  first_arg = self._parse_expression()
@@ -1722,11 +1776,56 @@ class CSSLParser:
1722
1776
  return node
1723
1777
 
1724
1778
  def _parse_return(self) -> ASTNode:
1725
- value = None
1779
+ """Parse return statement, supporting multiple values for shuffled functions.
1780
+
1781
+ Syntax:
1782
+ return; // Return None
1783
+ return value; // Return single value
1784
+ return a, b, c; // Return multiple values (for shuffled)
1785
+ """
1786
+ values = []
1726
1787
  if not self._check(TokenType.SEMICOLON) and not self._check(TokenType.BLOCK_END):
1727
- value = self._parse_expression()
1788
+ values.append(self._parse_expression())
1789
+
1790
+ # Check for comma-separated return values (shuffled return)
1791
+ while self._check(TokenType.COMMA):
1792
+ self._advance() # consume comma
1793
+ values.append(self._parse_expression())
1794
+
1795
+ self._match(TokenType.SEMICOLON)
1796
+
1797
+ if len(values) == 0:
1798
+ return ASTNode('return', value=None)
1799
+ elif len(values) == 1:
1800
+ return ASTNode('return', value=values[0])
1801
+ else:
1802
+ # Multiple return values - create tuple return
1803
+ return ASTNode('return', value={'multiple': True, 'values': values})
1804
+
1805
+ def _parse_super_function(self) -> ASTNode:
1806
+ """Parse super-function for .cssl-pl payload files.
1807
+
1808
+ Syntax:
1809
+ #$run(initFunction); // Call function at load time
1810
+ #$exec(setup()); // Execute expression at load time
1811
+ #$printl("Payload loaded"); // Print at load time
1812
+
1813
+ These are pre-execution hooks that run when payload() loads the file.
1814
+ """
1815
+ token = self._advance() # Get the SUPER_FUNC token
1816
+ super_name = token.value # e.g., "#$run", "#$exec", "#$printl"
1817
+
1818
+ # Parse the arguments
1819
+ self._expect(TokenType.PAREN_START)
1820
+ args = []
1821
+ if not self._check(TokenType.PAREN_END):
1822
+ args.append(self._parse_expression())
1823
+ while self._match(TokenType.COMMA):
1824
+ args.append(self._parse_expression())
1825
+ self._expect(TokenType.PAREN_END)
1728
1826
  self._match(TokenType.SEMICOLON)
1729
- return ASTNode('return', value=value)
1827
+
1828
+ return ASTNode('super_func', value={'name': super_name, 'args': args})
1730
1829
 
1731
1830
  def _parse_try(self) -> ASTNode:
1732
1831
  node = ASTNode('try', children=[])
@@ -1813,6 +1912,25 @@ class CSSLParser:
1813
1912
  def _parse_expression_statement(self) -> Optional[ASTNode]:
1814
1913
  expr = self._parse_expression()
1815
1914
 
1915
+ # === TUPLE UNPACKING: a, b, c = shuffled_func() ===
1916
+ # Check if we have comma-separated identifiers before =
1917
+ if expr.type == 'identifier' and self._check(TokenType.COMMA):
1918
+ targets = [expr]
1919
+ while self._match(TokenType.COMMA):
1920
+ next_expr = self._parse_expression()
1921
+ if next_expr.type == 'identifier':
1922
+ targets.append(next_expr)
1923
+ else:
1924
+ # Not a simple identifier list, this is something else
1925
+ # Restore and fall through to normal parsing
1926
+ break
1927
+
1928
+ # Check if followed by =
1929
+ if self._match(TokenType.EQUALS):
1930
+ value = self._parse_expression()
1931
+ self._match(TokenType.SEMICOLON)
1932
+ return ASTNode('tuple_assignment', value={'targets': targets, 'value': value})
1933
+
1816
1934
  # === BASIC INJECTION: <== (replace target with source) ===
1817
1935
  if self._match(TokenType.INJECT_LEFT):
1818
1936
  # Check if this is a createcmd injection with a code block
@@ -2335,17 +2453,62 @@ class CSSLParser:
2335
2453
  self._expect(TokenType.COMPARE_GT) # consume >
2336
2454
  return ASTNode('instance_ref', value=instance_name)
2337
2455
 
2338
- # Check for type generic instantiation: stack<string>, vector<int>, etc.
2456
+ # Check for type generic instantiation: stack<string>, vector<int>, map<string, int>, etc.
2339
2457
  # This creates a new instance of the type with the specified element type
2340
2458
  if name in TYPE_GENERICS and self._check(TokenType.COMPARE_LT):
2341
2459
  self._advance() # consume <
2342
2460
  element_type = 'dynamic'
2461
+ value_type = None # For map<K, V>
2462
+
2343
2463
  if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
2344
2464
  element_type = self._advance().value
2465
+
2466
+ # Check for second type parameter (map<K, V>)
2467
+ if name == 'map' and self._check(TokenType.COMMA):
2468
+ self._advance() # consume ,
2469
+ if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
2470
+ value_type = self._advance().value
2471
+ else:
2472
+ value_type = 'dynamic'
2473
+
2345
2474
  self._expect(TokenType.COMPARE_GT) # consume >
2475
+
2476
+ # Check for inline initialization: map<K,V>{"key": "value", ...}
2477
+ init_values = None
2478
+ if self._check(TokenType.BLOCK_START):
2479
+ self._advance() # consume {
2480
+ init_values = {}
2481
+
2482
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
2483
+ # Parse key
2484
+ if self._check(TokenType.STRING):
2485
+ key = self._advance().value
2486
+ elif self._check(TokenType.IDENTIFIER):
2487
+ key = self._advance().value
2488
+ else:
2489
+ key = str(self._parse_expression().value) if hasattr(self._parse_expression(), 'value') else 'key'
2490
+
2491
+ # Expect : or =
2492
+ if self._check(TokenType.COLON):
2493
+ self._advance()
2494
+ elif self._check(TokenType.EQUALS):
2495
+ self._advance()
2496
+
2497
+ # Parse value
2498
+ value = self._parse_expression()
2499
+ init_values[key] = value
2500
+
2501
+ # Optional comma
2502
+ if self._check(TokenType.COMMA):
2503
+ self._advance()
2504
+
2505
+ self._expect(TokenType.BLOCK_END) # consume }
2506
+
2346
2507
  return ASTNode('type_instantiation', value={
2347
2508
  'type': name,
2348
- 'element_type': element_type
2509
+ 'element_type': element_type,
2510
+ 'value_type': value_type,
2511
+ 'init_values': init_values
2349
2512
  })
2350
2513
 
2351
2514
  # Check for type-parameterized function call: OpenFind<string>(0)