IncludeCPP 3.9.2__tar.gz → 4.0.1__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 (61) hide show
  1. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/PKG-INFO +2 -1
  2. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/requires.txt +1 -0
  3. {includecpp-3.9.2 → includecpp-4.0.1}/PKG-INFO +2 -1
  4. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/__init__.py +1 -1
  5. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/cli/commands.py +7 -14
  6. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/CSSL_DOCUMENTATION.md +48 -3
  7. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_parser.py +272 -81
  8. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_runtime.py +260 -18
  9. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl_bridge.py +4 -4
  10. {includecpp-3.9.2 → includecpp-4.0.1}/pyproject.toml +2 -1
  11. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  12. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  13. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/entry_points.txt +0 -0
  14. {includecpp-3.9.2 → includecpp-4.0.1}/IncludeCPP.egg-info/top_level.txt +0 -0
  15. {includecpp-3.9.2 → includecpp-4.0.1}/LICENSE +0 -0
  16. {includecpp-3.9.2 → includecpp-4.0.1}/MANIFEST.in +0 -0
  17. {includecpp-3.9.2 → includecpp-4.0.1}/README.md +0 -0
  18. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/__init__.pyi +0 -0
  19. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/__main__.py +0 -0
  20. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/cli/__init__.py +0 -0
  21. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/cli/config_parser.py +0 -0
  22. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/__init__.py +0 -0
  23. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/ai_integration.py +0 -0
  24. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/build_manager.py +0 -0
  25. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cpp_api.py +0 -0
  26. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cpp_api.pyi +0 -0
  27. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cppy_converter.py +0 -0
  28. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/CSSL_DOCUMENTATION_NEW.md +0 -0
  29. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/__init__.py +0 -0
  30. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_builtins.py +0 -0
  31. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_builtins.pyi +0 -0
  32. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_events.py +0 -0
  33. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_modules.py +0 -0
  34. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_syntax.py +0 -0
  35. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl/cssl_types.py +0 -0
  36. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/cssl_bridge.pyi +0 -0
  37. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/error_catalog.py +0 -0
  38. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/error_formatter.py +0 -0
  39. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/exceptions.py +0 -0
  40. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/path_discovery.py +0 -0
  41. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/project_ui.py +0 -0
  42. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/core/settings_ui.py +0 -0
  43. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/generator/__init__.py +0 -0
  44. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/generator/parser.cpp +0 -0
  45. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/generator/parser.h +0 -0
  46. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/generator/type_resolver.cpp +0 -0
  47. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/generator/type_resolver.h +0 -0
  48. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/py.typed +0 -0
  49. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/templates/cpp.proj.template +0 -0
  50. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/__init__.py +0 -0
  51. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/__init__.py +0 -0
  52. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/extension.js +0 -0
  53. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/images/cssl.png +0 -0
  54. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/images/cssl_pl.png +0 -0
  55. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/language-configuration.json +0 -0
  56. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/package.json +0 -0
  57. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/snippets/cssl.snippets.json +0 -0
  58. {includecpp-3.9.2 → includecpp-4.0.1}/includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +0 -0
  59. {includecpp-3.9.2 → includecpp-4.0.1}/requirements.txt +0 -0
  60. {includecpp-3.9.2 → includecpp-4.0.1}/setup.cfg +0 -0
  61. {includecpp-3.9.2 → includecpp-4.0.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.9.2
3
+ Version: 4.0.1
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
@@ -27,6 +27,7 @@ Requires-Dist: pybind11>=2.11.0
27
27
  Requires-Dist: click>=8.0.0
28
28
  Requires-Dist: typing-extensions>=4.0.0
29
29
  Requires-Dist: requests>=2.28.0
30
+ Requires-Dist: colorama>=0.4.0
30
31
  Dynamic: author-email
31
32
  Dynamic: home-page
32
33
  Dynamic: license-file
@@ -2,3 +2,4 @@ pybind11>=2.11.0
2
2
  click>=8.0.0
3
3
  typing-extensions>=4.0.0
4
4
  requests>=2.28.0
5
+ colorama>=0.4.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.9.2
3
+ Version: 4.0.1
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
@@ -27,6 +27,7 @@ Requires-Dist: pybind11>=2.11.0
27
27
  Requires-Dist: click>=8.0.0
28
28
  Requires-Dist: typing-extensions>=4.0.0
29
29
  Requires-Dist: requests>=2.28.0
30
+ Requires-Dist: colorama>=0.4.0
30
31
  Dynamic: author-email
31
32
  Dynamic: home-page
32
33
  Dynamic: license-file
@@ -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.9.2"
5
+ __version__ = "4.0.1"
6
6
  __all__ = ["CppApi", "CSSL"]
7
7
 
8
8
  # Module-level cache for C++ modules
@@ -9,8 +9,12 @@ import re
9
9
  import urllib.request
10
10
  import urllib.error
11
11
  from pathlib import Path
12
+ from colorama import init as colorama_init, Fore, Style
12
13
  from .config_parser import CppProjectConfig
13
14
 
15
+ # Initialize colorama for Windows ANSI color support
16
+ colorama_init()
17
+
14
18
 
15
19
  def _is_experimental_enabled() -> bool:
16
20
  """Check if experimental features (cppy, ai) are enabled."""
@@ -7320,7 +7324,6 @@ def exec_repl(lang, path, import_all):
7320
7324
  code_lines = imports + [''] + lines if imports else lines
7321
7325
 
7322
7326
  click.echo()
7323
- click.secho("--- Output ---", fg='green')
7324
7327
 
7325
7328
  if is_python:
7326
7329
  # Execute Python code
@@ -7443,9 +7446,6 @@ def exec_repl(lang, path, import_all):
7443
7446
  except Exception:
7444
7447
  pass
7445
7448
 
7446
- click.echo()
7447
- click.secho("--------------", fg='green')
7448
-
7449
7449
 
7450
7450
  # ============================================================================
7451
7451
  # CSSL - Hidden Command Group
@@ -7526,19 +7526,12 @@ def _cssl_execute(path, code):
7526
7526
  source = '\n'.join(lines)
7527
7527
 
7528
7528
  # Execute
7529
- click.secho("--- Output ---", fg='green')
7530
7529
  try:
7531
7530
  result = cssl_lang.run(source)
7532
-
7533
- # Output is already printed to stdout during execution via runtime.output()
7534
- # Don't print "Result:" automatically - users should use printl() for output
7535
- # This prevents unwanted output for function calls like: Function();
7536
- pass
7537
-
7538
7531
  except Exception as e:
7539
- click.secho(f"CSSL Error: {e}", fg='red')
7540
-
7541
- click.secho("--------------", fg='green')
7532
+ error_msg = str(e)
7533
+ # Clean display - single CSSL Error: prefix with colorama
7534
+ click.echo(f"{Fore.RED}CSSL Error: {error_msg}{Style.RESET_ALL}")
7542
7535
 
7543
7536
 
7544
7537
  @cssl.command(name='makemodule')
@@ -1,6 +1,6 @@
1
1
  # CSSL - C-Style Scripting Language
2
2
 
3
- > Version 3.9.1 | A modern scripting language with C++-style syntax and unique features like CodeInfusion, BruteInjection, and Python Interop.
3
+ > Version 4.0.0 | A modern scripting language with C++-style syntax and unique features like CodeInfusion, BruteInjection, and Python Interop.
4
4
 
5
5
  ---
6
6
 
@@ -513,10 +513,16 @@ define greet(string name) {
513
513
  | `closed` | Block external CodeInfusion |
514
514
  | `private` | Block all CodeInfusion |
515
515
  | `virtual` | Import-cycle safe |
516
- | `meta` | Source function (must return) |
516
+ | `meta` | Source function (any return type allowed) |
517
517
  | `super` | Force execution (no exceptions) |
518
518
  | `open` | Accept any parameters |
519
519
  | `shuffled` | Return multiple values |
520
+ | `const` | Immutable function (like C++) |
521
+ | `static` | Static method/function |
522
+ | `public` | Explicitly public (default) |
523
+ | `global` | Makes function globally accessible |
524
+
525
+ **Flexible Modifier Order**: Modifiers can appear in any order before the function declaration.
520
526
 
521
527
  ```cssl
522
528
  // Ignore errors
@@ -535,6 +541,45 @@ shuffled string getNames() {
535
541
  }
536
542
 
537
543
  a, b, c = getNames();
544
+
545
+ // Any order of modifiers works
546
+ private string *@Myfunc() {
547
+ println("Hello from global private non-null function!");
548
+ return "Hey";
549
+ }
550
+
551
+ // Const function
552
+ const void immutableFunc() {
553
+ printl("I'm constant");
554
+ }
555
+
556
+ // Global private const
557
+ global private const void @MyGlobalFunc() {
558
+ printl("Complex modifiers in any order");
559
+ }
560
+ ```
561
+
562
+ ### Typed Functions (Enforced Returns)
563
+
564
+ Functions with a return type (like `int`, `string`, `vector<T>`) MUST return that type:
565
+
566
+ ```cssl
567
+ // MUST return string
568
+ string getName() {
569
+ return "Alice"; // OK
570
+ // return 42; // Error! Expected string
571
+ }
572
+
573
+ // MUST return int
574
+ int getAge() {
575
+ return 30; // OK
576
+ }
577
+
578
+ // meta modifier bypasses type checking
579
+ meta datastruct<string> getData() {
580
+ return "just a string"; // OK despite declared type
581
+ return 42; // Also OK - meta allows any type
582
+ }
538
583
  ```
539
584
 
540
585
  ### Non-Null Functions (*)
@@ -1453,4 +1498,4 @@ assert(x > 0, "x must be positive");
1453
1498
 
1454
1499
  ---
1455
1500
 
1456
- *CSSL v3.9.1 - Developed as part of IncludeCPP*
1501
+ *CSSL v4.0.0 - Developed as part of IncludeCPP*
@@ -144,16 +144,25 @@ KEYWORDS = {
144
144
  'structure', # Advanced C++/Py Class
145
145
  'openquote', # SQL openquote container
146
146
  # CSSL Function Modifiers
147
+ 'const', # Immutable function (like C++)
147
148
  'meta', # Source function (must return)
148
149
  'super', # Force execution (no exceptions)
149
150
  'closed', # Protect from external injection
150
151
  'private', # Disable all injections
151
152
  'virtual', # Import cycle safe
152
153
  'sqlbased', # SQL-based function
154
+ 'public', # Explicitly public (default)
155
+ 'static', # Static method/function
153
156
  # CSSL Include Keywords
154
157
  'include', 'get',
155
158
  }
156
159
 
160
+ # Function modifiers that can appear in any order before function name
161
+ FUNCTION_MODIFIERS = {
162
+ 'undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual',
163
+ 'sqlbased', 'const', 'public', 'static', 'global', 'shuffled'
164
+ }
165
+
157
166
  # Type literals that create empty instances
158
167
  TYPE_LITERALS = {'list', 'dict'}
159
168
 
@@ -719,7 +728,7 @@ class CSSLParser:
719
728
 
720
729
  def _is_function_modifier(self, value: str) -> bool:
721
730
  """Check if a keyword is a function modifier"""
722
- return value in ('undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual', 'sqlbased')
731
+ return value in FUNCTION_MODIFIERS
723
732
 
724
733
  def _is_type_keyword(self, value: str) -> bool:
725
734
  """Check if a keyword is a type declaration"""
@@ -730,53 +739,98 @@ class CSSLParser:
730
739
  def _looks_like_function_declaration(self) -> bool:
731
740
  """Check if current position looks like a C-style function declaration.
732
741
 
742
+ Supports flexible ordering of modifiers, types, non-null (*), and global (@):
743
+
733
744
  Patterns:
734
745
  - int funcName(...)
735
746
  - undefined int funcName(...)
736
747
  - vector<string> funcName(...)
737
748
  - undefined void funcName(...)
738
- - private super virtual meta FuncName(...) <- modifiers without return type
749
+ - private super virtual meta FuncName(...)
750
+ - private string *@Myfunc(...)
751
+ - const define myFunc(...)
752
+ - global private const void @Func(...)
753
+ - shuffled *[string] getNumbers(...)
754
+ - datastruct<dynamic> HelloCode(...)
755
+ - datastruct<dynamic> HelloCode() : extends @MyFunc { }
756
+
757
+ Distinguishes functions from variables:
758
+ - datastruct<dynamic> MyVar; <- variable (no () { })
759
+ - datastruct<dynamic> HelloCode() { } <- function (has () { })
739
760
  """
740
761
  saved_pos = self.pos
741
762
  has_modifiers = False
763
+ has_type = False
742
764
 
743
- # Skip modifiers (undefined, open, meta, super, closed, private, virtual)
744
- while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
745
- self._advance()
746
- has_modifiers = True
765
+ try:
766
+ # Skip modifiers in any order (global, private, const, undefined, etc.)
767
+ while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
768
+ self._advance()
769
+ has_modifiers = True
747
770
 
748
- # Check for type keyword (optional if modifiers present)
749
- if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
750
- self._advance()
771
+ # Check for 'define' keyword (special case: const define myFunc())
772
+ if self._check(TokenType.KEYWORD) and self._current().value == 'define':
773
+ self.pos = saved_pos
774
+ return False # Let _parse_define handle this
751
775
 
752
- # Skip generic type parameters <T>
753
- if self._check(TokenType.COMPARE_LT):
754
- depth = 1
776
+ # Check for type keyword (int, string, void, vector, datastruct, etc.)
777
+ if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
755
778
  self._advance()
756
- while depth > 0 and not self._is_at_end():
757
- if self._check(TokenType.COMPARE_LT):
758
- depth += 1
759
- elif self._check(TokenType.COMPARE_GT):
760
- depth -= 1
779
+ has_type = True
780
+
781
+ # Skip generic type parameters <T> or <T, U>
782
+ if self._check(TokenType.COMPARE_LT):
783
+ depth = 1
761
784
  self._advance()
785
+ while depth > 0 and not self._is_at_end():
786
+ if self._check(TokenType.COMPARE_LT):
787
+ depth += 1
788
+ elif self._check(TokenType.COMPARE_GT):
789
+ depth -= 1
790
+ self._advance()
791
+
792
+ # Check for * prefix (non-null) or *[type] (type exclusion)
793
+ if self._check(TokenType.MULTIPLY):
794
+ self._advance()
795
+ # Check for type exclusion: *[string], *[int], etc.
796
+ if self._check(TokenType.BRACKET_START):
797
+ self._advance() # [
798
+ while not self._check(TokenType.BRACKET_END) and not self._is_at_end():
799
+ self._advance()
800
+ if self._check(TokenType.BRACKET_END):
801
+ self._advance() # ]
802
+
803
+ # Check for @ prefix (global function)
804
+ if self._check(TokenType.AT):
805
+ self._advance()
762
806
 
763
- # Check for identifier followed by (
807
+ # Now we should be at the function name (identifier)
764
808
  if self._check(TokenType.IDENTIFIER):
765
809
  self._advance()
766
- is_func = self._check(TokenType.PAREN_START)
767
- self.pos = saved_pos
768
- return is_func
769
810
 
770
- # If we have modifiers and the next token is an identifier followed by (
771
- # This handles: private super virtual meta FuncName()
772
- elif has_modifiers and self._check(TokenType.IDENTIFIER):
773
- self._advance()
774
- is_func = self._check(TokenType.PAREN_START)
811
+ # Check if followed by (
812
+ # IMPORTANT: Only a function declaration if we have modifiers OR type
813
+ # Plain identifier() is a function CALL, not a declaration
814
+ if self._check(TokenType.PAREN_START):
815
+ if has_modifiers or has_type:
816
+ self.pos = saved_pos
817
+ return True
818
+ else:
819
+ # No modifiers/type = function call, not declaration
820
+ self.pos = saved_pos
821
+ return False
822
+
823
+ # If we have a type and identifier but no (, it's a variable
824
+ if has_type and not self._check(TokenType.PAREN_START):
825
+ self.pos = saved_pos
826
+ return False
827
+
775
828
  self.pos = saved_pos
776
- return is_func
829
+ return False
777
830
 
778
- self.pos = saved_pos
779
- return False
831
+ except Exception:
832
+ self.pos = saved_pos
833
+ return False
780
834
 
781
835
  def _looks_like_typed_variable(self) -> bool:
782
836
  """Check if current position looks like a typed variable declaration.
@@ -817,69 +871,109 @@ class CSSLParser:
817
871
  return False
818
872
 
819
873
  def _parse_typed_function(self, is_global: bool = False) -> ASTNode:
820
- """Parse C-style typed function declaration.
874
+ """Parse C-style typed function declaration with flexible modifier ordering.
875
+
876
+ Supports any order of modifiers, types, non-null (*), and global (@):
821
877
 
822
878
  Patterns:
823
- - int Add(int a, int b) { } // Local function
824
- - global int Add(int a, int b) { } // Global function
825
- - int @Add(int a, int b) { } // Global function (alternative)
879
+ - int Add(int a, int b) { }
880
+ - global int Add(int a, int b) { }
881
+ - int @Add(int a, int b) { }
826
882
  - undefined int Func() { }
827
883
  - open void Handler(open Params) { }
828
884
  - vector<string> GetNames() { }
885
+ - private string *@Myfunc() { }
886
+ - const define myFunc() { }
887
+ - global private const void @Func() { }
888
+ - shuffled *[string] getNumbers() { }
889
+ - datastruct<dynamic> HelloCode() { }
890
+ - meta datastruct<string> MyData() { } // meta allows any returns
891
+ - datastruct<dynamic> HelloCode() : extends @MyFunc { }
892
+
893
+ Typed functions (with return type like int, string, void) MUST return that type.
894
+ Functions with 'meta' modifier can return any type regardless of declaration.
895
+ Functions with 'define' are dynamic (any return type allowed).
829
896
  """
830
897
  modifiers = []
831
898
  return_type = None
832
899
  generic_type = None
900
+ non_null = False
901
+ exclude_type = None
902
+ is_const = False
903
+
904
+ # Phase 1: Collect all modifiers, type, non-null, and global indicators
905
+ # These can appear in any order before the function name
906
+
907
+ parsing_prefix = True
908
+ while parsing_prefix and not self._is_at_end():
909
+ # Check for modifiers (global, private, const, undefined, etc.)
910
+ if self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
911
+ mod = self._advance().value
912
+ if mod == 'global':
913
+ is_global = True
914
+ elif mod == 'const':
915
+ is_const = True
916
+ modifiers.append(mod)
917
+ else:
918
+ modifiers.append(mod)
919
+ continue
833
920
 
834
- # Collect modifiers (undefined, open, meta, super, closed, private, virtual)
835
- while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
836
- modifiers.append(self._advance().value)
921
+ # Check for type keyword (int, string, void, vector, datastruct, etc.)
922
+ if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value) and return_type is None:
923
+ return_type = self._advance().value
837
924
 
838
- # Get return type
839
- if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
840
- return_type = self._advance().value
925
+ # Check for generic type <T> or <T, U>
926
+ if self._check(TokenType.COMPARE_LT):
927
+ self._advance() # skip <
928
+ generic_parts = []
929
+ depth = 1
930
+ while depth > 0 and not self._is_at_end():
931
+ if self._check(TokenType.COMPARE_LT):
932
+ depth += 1
933
+ generic_parts.append('<')
934
+ elif self._check(TokenType.COMPARE_GT):
935
+ depth -= 1
936
+ if depth > 0:
937
+ generic_parts.append('>')
938
+ elif self._check(TokenType.COMMA):
939
+ generic_parts.append(',')
940
+ else:
941
+ generic_parts.append(str(self._current().value))
942
+ self._advance()
943
+ generic_type = ''.join(generic_parts)
944
+ continue
841
945
 
842
- # Check for generic type <T>
843
- if self._check(TokenType.COMPARE_LT):
844
- self._advance() # skip <
845
- generic_parts = []
846
- depth = 1
847
- while depth > 0 and not self._is_at_end():
848
- if self._check(TokenType.COMPARE_LT):
849
- depth += 1
850
- generic_parts.append('<')
851
- elif self._check(TokenType.COMPARE_GT):
852
- depth -= 1
853
- if depth > 0:
854
- generic_parts.append('>')
855
- elif self._check(TokenType.COMMA):
856
- generic_parts.append(',')
857
- else:
858
- generic_parts.append(self._current().value)
859
- self._advance()
860
- generic_type = ''.join(generic_parts)
946
+ # Check for * prefix (non-null) or *[type] (type exclusion)
947
+ if self._check(TokenType.MULTIPLY):
948
+ self._advance()
949
+ # Check for type exclusion filter: *[string], *[int], etc.
950
+ if self._check(TokenType.BRACKET_START):
951
+ self._advance() # consume [
952
+ exclude_type = self._advance().value # get type name
953
+ self._expect(TokenType.BRACKET_END)
954
+ else:
955
+ non_null = True
956
+ continue
861
957
 
862
- # Check for * prefix (non-null function) or *[type] (type exclusion)
863
- non_null = False
864
- exclude_type = None
865
- if self._match(TokenType.MULTIPLY):
866
- # Check for type exclusion filter: *[string], *[int], etc.
867
- if self._check(TokenType.BRACKET_START):
868
- self._advance() # consume [
869
- exclude_type = self._advance().value # get type name
870
- self._expect(TokenType.BRACKET_END)
871
- else:
872
- non_null = True
958
+ # Check for @ prefix (global function)
959
+ if self._check(TokenType.AT):
960
+ self._advance()
961
+ is_global = True
962
+ continue
873
963
 
874
- # Check for @ prefix (global function): void @FuncName()
875
- if self._check(TokenType.AT):
876
- self._advance() # consume @
877
- is_global = True
964
+ # If we've reached an identifier, we're at the function name
965
+ if self._check(TokenType.IDENTIFIER):
966
+ parsing_prefix = False
967
+ else:
968
+ # Unknown token in prefix, break out
969
+ parsing_prefix = False
878
970
 
879
- # Get function name
971
+ # Phase 2: Get function name
972
+ if not self._check(TokenType.IDENTIFIER):
973
+ self.error(f"Expected function name, got {self._current().type.name}")
880
974
  name = self._advance().value
881
975
 
882
- # Parse parameters
976
+ # Phase 3: Parse parameters
883
977
  params = []
884
978
  self._expect(TokenType.PAREN_START)
885
979
 
@@ -890,6 +984,10 @@ class CSSLParser:
890
984
  if self._match_keyword('open'):
891
985
  param_info['open'] = True
892
986
 
987
+ # Handle const parameters
988
+ if self._match_keyword('const'):
989
+ param_info['const'] = True
990
+
893
991
  # Handle type annotations (builtin types like int, string, etc.)
894
992
  if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
895
993
  param_info['type'] = self._advance().value
@@ -910,13 +1008,12 @@ class CSSLParser:
910
1008
  elif self._check(TokenType.COMMA):
911
1009
  generic_parts.append(',')
912
1010
  else:
913
- generic_parts.append(self._current().value)
1011
+ generic_parts.append(str(self._current().value))
914
1012
  self._advance()
915
1013
  param_info['generic'] = ''.join(generic_parts)
916
1014
 
917
1015
  # Handle custom class types (identifier followed by another identifier = type + name)
918
1016
  elif self._check(TokenType.IDENTIFIER):
919
- # Look ahead: if next token is also an identifier, current is the type
920
1017
  saved_pos = self.pos
921
1018
  potential_type = self._advance().value
922
1019
 
@@ -936,7 +1033,7 @@ class CSSLParser:
936
1033
  elif self._check(TokenType.COMMA):
937
1034
  generic_parts.append(',')
938
1035
  else:
939
- generic_parts.append(self._current().value)
1036
+ generic_parts.append(str(self._current().value))
940
1037
  self._advance()
941
1038
  param_info['generic'] = ''.join(generic_parts)
942
1039
 
@@ -947,6 +1044,10 @@ class CSSLParser:
947
1044
  # Not a type, restore position - this is just a param name
948
1045
  self.pos = saved_pos
949
1046
 
1047
+ # Handle * prefix for non-null parameters
1048
+ if self._match(TokenType.MULTIPLY):
1049
+ param_info['non_null'] = True
1050
+
950
1051
  # Handle reference operator &
951
1052
  if self._match(TokenType.AMPERSAND):
952
1053
  param_info['ref'] = True
@@ -959,7 +1060,7 @@ class CSSLParser:
959
1060
  else:
960
1061
  params.append(param_name)
961
1062
  elif self._check(TokenType.KEYWORD):
962
- # Parameter name could be a keyword
1063
+ # Parameter name could be a keyword like 'Params'
963
1064
  param_name = self._advance().value
964
1065
  if param_info:
965
1066
  params.append({'name': param_name, **param_info})
@@ -970,16 +1071,106 @@ class CSSLParser:
970
1071
 
971
1072
  self._expect(TokenType.PAREN_END)
972
1073
 
973
- # Parse function body
1074
+ # Phase 4: Check for extends/overwrites and append mode
1075
+ extends_func = None
1076
+ extends_is_python = False
1077
+ extends_class_ref = None
1078
+ extends_method_ref = None
1079
+ overwrites_func = None
1080
+ overwrites_is_python = False
1081
+ overwrites_class_ref = None
1082
+ overwrites_method_ref = None
1083
+ append_mode = False
1084
+ append_ref_class = None
1085
+ append_ref_member = None
1086
+
1087
+ # Check for &ClassName::member or &ClassName.member or &function reference
1088
+ if self._match(TokenType.AMPERSAND):
1089
+ if self._check(TokenType.IDENTIFIER):
1090
+ append_ref_class = self._advance().value
1091
+ elif self._check(TokenType.AT):
1092
+ self._advance()
1093
+ if self._check(TokenType.IDENTIFIER):
1094
+ append_ref_class = '@' + self._advance().value
1095
+ elif self._check(TokenType.SHARED_REF):
1096
+ append_ref_class = f'${self._advance().value}'
1097
+
1098
+ # Check for ::member or .member (support both syntaxes)
1099
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1100
+ if self._check(TokenType.IDENTIFIER) or self._check(TokenType.KEYWORD):
1101
+ append_ref_member = self._advance().value
1102
+
1103
+ # Check for ++ append operator
1104
+ if self._match(TokenType.PLUS_PLUS):
1105
+ append_mode = True
1106
+
1107
+ # Check for : or :: extends/overwrites
1108
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1109
+ while True:
1110
+ if self._match_keyword('extends'):
1111
+ # Parse target: @ModuleName, $PythonObject, Parent::method, Parent.method
1112
+ if self._check(TokenType.AT):
1113
+ self._advance()
1114
+ extends_func = '@' + self._advance().value
1115
+ elif self._check(TokenType.SHARED_REF):
1116
+ extends_is_python = True
1117
+ extends_func = self._advance().value
1118
+ elif self._check(TokenType.IDENTIFIER):
1119
+ first_part = self._advance().value
1120
+ # Support both :: and . for class method access
1121
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1122
+ extends_class_ref = first_part
1123
+ extends_method_ref = self._advance().value
1124
+ else:
1125
+ extends_func = first_part
1126
+ # Skip optional ()
1127
+ if self._match(TokenType.PAREN_START):
1128
+ self._expect(TokenType.PAREN_END)
1129
+ elif self._match_keyword('overwrites'):
1130
+ if self._check(TokenType.AT):
1131
+ self._advance()
1132
+ overwrites_func = '@' + self._advance().value
1133
+ elif self._check(TokenType.SHARED_REF):
1134
+ overwrites_is_python = True
1135
+ overwrites_func = self._advance().value
1136
+ elif self._check(TokenType.IDENTIFIER):
1137
+ first_part = self._advance().value
1138
+ # Support both :: and . for class method access
1139
+ if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.DOT):
1140
+ overwrites_class_ref = first_part
1141
+ overwrites_method_ref = self._advance().value
1142
+ else:
1143
+ overwrites_func = first_part
1144
+ if self._match(TokenType.PAREN_START):
1145
+ self._expect(TokenType.PAREN_END)
1146
+ else:
1147
+ break
1148
+ if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
1149
+ break
1150
+
1151
+ # Phase 5: Parse function body
974
1152
  node = ASTNode('function', value={
975
1153
  'name': name,
976
1154
  'is_global': is_global,
1155
+ 'is_const': is_const,
977
1156
  'params': params,
978
1157
  'return_type': return_type,
979
1158
  'generic_type': generic_type,
980
1159
  'modifiers': modifiers,
981
1160
  'non_null': non_null,
982
- 'exclude_type': exclude_type # *[type] - must NOT return this type
1161
+ 'exclude_type': exclude_type,
1162
+ 'extends': extends_func,
1163
+ 'extends_is_python': extends_is_python,
1164
+ 'extends_class': extends_class_ref,
1165
+ 'extends_method': extends_method_ref,
1166
+ 'overwrites': overwrites_func,
1167
+ 'overwrites_is_python': overwrites_is_python,
1168
+ 'overwrites_class': overwrites_class_ref,
1169
+ 'overwrites_method': overwrites_method_ref,
1170
+ 'append_mode': append_mode,
1171
+ 'append_ref_class': append_ref_class,
1172
+ 'append_ref_member': append_ref_member,
1173
+ 'enforce_return_type': return_type is not None and 'meta' not in modifiers
983
1174
  }, children=[])
984
1175
 
985
1176
  self._expect(TokenType.BLOCK_START)
@@ -68,11 +68,11 @@ class CSSLRuntimeError(Exception):
68
68
  # Build detailed error message
69
69
  error_parts = []
70
70
 
71
- # Main error message
71
+ # Main error message (no "Error:" prefix - CLI handles that)
72
72
  if line:
73
- error_parts.append(f"Error at line {line}: {message}")
73
+ error_parts.append(f"Line {line}: {message}")
74
74
  else:
75
- error_parts.append(f"Error: {message}")
75
+ error_parts.append(message)
76
76
 
77
77
  # Add context if available
78
78
  if context:
@@ -363,11 +363,11 @@ class CSSLRuntime:
363
363
  """Format a detailed error with source context"""
364
364
  error_parts = []
365
365
 
366
- # Main error header
366
+ # Main error header (no "Error:" prefix - CLI handles that)
367
367
  if line and line > 0:
368
- error_parts.append(f"Error at line {line} in {self._current_file}:")
368
+ error_parts.append(f"Line {line} in {self._current_file}:")
369
369
  else:
370
- error_parts.append(f"Error in {self._current_file}:")
370
+ error_parts.append(f"In {self._current_file}:")
371
371
 
372
372
  # Extract message without existing line info
373
373
  clean_msg = message
@@ -1005,13 +1005,29 @@ class CSSLRuntime:
1005
1005
  overwrites_func = func_info.get('overwrites')
1006
1006
  overwrites_is_python = func_info.get('overwrites_is_python', False)
1007
1007
 
1008
+ # Get append/overwrite reference info (&Class::method syntax)
1009
+ append_mode = func_info.get('append_mode', False)
1010
+ append_ref_class = func_info.get('append_ref_class')
1011
+ append_ref_member = func_info.get('append_ref_member')
1012
+
1008
1013
  # Store function extends info for runtime use
1009
1014
  if extends_func:
1010
1015
  node.value['_extends_resolved'] = self._resolve_function_target(
1011
1016
  extends_func, extends_is_python
1012
1017
  )
1013
1018
 
1014
- # Handle overwrites - replace the target function
1019
+ # Handle &Class::method syntax
1020
+ # Without ++ = full replacement
1021
+ # With ++ = append (run original first, then new code)
1022
+ if append_ref_class:
1023
+ if append_mode:
1024
+ # Append mode: wrap original to run original + new
1025
+ self._append_to_target(append_ref_class, append_ref_member, node)
1026
+ else:
1027
+ # Full replacement
1028
+ self._overwrite_target(append_ref_class, append_ref_member, node)
1029
+
1030
+ # Handle overwrites keyword - replace the target function
1015
1031
  if overwrites_func:
1016
1032
  target = self._resolve_function_target(overwrites_func, overwrites_is_python)
1017
1033
  if target is not None:
@@ -1054,6 +1070,142 @@ class CSSLRuntime:
1054
1070
  return self._call_function(func_node, list(args), kwargs)
1055
1071
  return wrapper
1056
1072
 
1073
+ def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
1074
+ """Overwrite a class method or function with the replacement.
1075
+
1076
+ Handles:
1077
+ - &ClassName::method - Overwrite method in CSSL class
1078
+ - &$PyObject.method - Overwrite method in Python shared object
1079
+ - &functionName - Overwrite standalone function
1080
+ """
1081
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1082
+
1083
+ # Handle Python shared objects
1084
+ if ref_class.startswith('$'):
1085
+ var_name = ref_class[1:]
1086
+ ref_obj = _live_objects.get(var_name)
1087
+ if ref_obj is None:
1088
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
1089
+
1090
+ if ref_obj is None:
1091
+ return
1092
+
1093
+ # Unwrap SharedObjectProxy
1094
+ if isinstance(ref_obj, SharedObjectProxy):
1095
+ ref_obj = ref_obj._obj
1096
+
1097
+ # Overwrite Python object method
1098
+ if ref_member and hasattr(ref_obj, ref_member):
1099
+ wrapper = self._create_python_wrapper(replacement_node)
1100
+ try:
1101
+ setattr(ref_obj, ref_member, wrapper)
1102
+ except (AttributeError, TypeError):
1103
+ pass # Can't overwrite (immutable or builtin)
1104
+ return
1105
+
1106
+ # Handle CSSL class method overwrite
1107
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1108
+ if target_class is None:
1109
+ # Maybe it's a standalone function reference (no ::member)
1110
+ if ref_member is None:
1111
+ # &functionName - overwrite the function
1112
+ self.scope.set(ref_class, replacement_node)
1113
+ self.global_scope.set(ref_class, replacement_node)
1114
+ return
1115
+
1116
+ if isinstance(target_class, CSSLClass) and ref_member:
1117
+ # Overwrite method in the class
1118
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1119
+ target_class.methods[ref_member] = replacement_node
1120
+ # Also check members list
1121
+ if hasattr(target_class, 'members'):
1122
+ for i, member in enumerate(target_class.members):
1123
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1124
+ target_class.members[i] = replacement_node
1125
+ break
1126
+
1127
+ def _append_to_target(self, ref_class: str, ref_member: str, append_node: ASTNode):
1128
+ """Append new code to an existing class method or function.
1129
+
1130
+ Creates a wrapper that runs original first, then the appended code.
1131
+ Handles:
1132
+ - &ClassName::method ++ - Append to method in CSSL class
1133
+ - &$PyObject.method ++ - Append to method in Python shared object
1134
+ - &functionName ++ - Append to standalone function
1135
+ """
1136
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
1137
+
1138
+ # Handle Python shared objects
1139
+ if ref_class.startswith('$'):
1140
+ var_name = ref_class[1:]
1141
+ ref_obj = _live_objects.get(var_name)
1142
+ if ref_obj is None:
1143
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
1144
+
1145
+ if ref_obj is None:
1146
+ return
1147
+
1148
+ if isinstance(ref_obj, SharedObjectProxy):
1149
+ ref_obj = ref_obj._obj
1150
+
1151
+ # Create wrapper that calls original + new
1152
+ if ref_member and hasattr(ref_obj, ref_member):
1153
+ original_method = getattr(ref_obj, ref_member)
1154
+ runtime = self
1155
+ def appended_wrapper(*args, **kwargs):
1156
+ # Run original first
1157
+ result = None
1158
+ if callable(original_method):
1159
+ try:
1160
+ result = original_method(*args, **kwargs)
1161
+ except:
1162
+ pass
1163
+ # Then run appended code
1164
+ return runtime._call_function(append_node, list(args), kwargs)
1165
+ try:
1166
+ setattr(ref_obj, ref_member, appended_wrapper)
1167
+ except (AttributeError, TypeError):
1168
+ pass
1169
+ return
1170
+
1171
+ # Handle CSSL class method append
1172
+ target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1173
+ if target_class is None:
1174
+ # Standalone function append
1175
+ if ref_member is None:
1176
+ original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
1177
+ if original_func:
1178
+ # Store original in append_node so it can run first
1179
+ append_node.value['_original_func'] = original_func
1180
+ self.scope.set(ref_class, append_node)
1181
+ self.global_scope.set(ref_class, append_node)
1182
+ return
1183
+
1184
+ if isinstance(target_class, CSSLClass) and ref_member:
1185
+ # Find original method
1186
+ original_method = None
1187
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1188
+ original_method = target_class.methods.get(ref_member)
1189
+
1190
+ if original_method is None and hasattr(target_class, 'members'):
1191
+ for member in target_class.members:
1192
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1193
+ original_method = member
1194
+ break
1195
+
1196
+ # Store original in append_node for runtime execution
1197
+ if original_method:
1198
+ append_node.value['_original_method'] = original_method
1199
+
1200
+ # Replace with append_node (which will call original first via _call_function)
1201
+ if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
1202
+ target_class.methods[ref_member] = append_node
1203
+ if hasattr(target_class, 'members'):
1204
+ for i, member in enumerate(target_class.members):
1205
+ if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
1206
+ target_class.members[i] = append_node
1207
+ break
1208
+
1057
1209
  def _exec_typed_declaration(self, node: ASTNode) -> Any:
1058
1210
  """Execute typed variable declaration: type<T> varName = value;
1059
1211
 
@@ -1352,12 +1504,15 @@ class CSSLRuntime:
1352
1504
  if not self._running:
1353
1505
  break
1354
1506
  self._execute_node(child)
1355
- # Copy all local vars to new scope
1356
- for name, value in temp_scope._vars.items():
1357
- new_scope.set(name, value)
1507
+ except CSSLReturn:
1508
+ # Parent returned - that's fine, we just want the local vars
1509
+ pass
1358
1510
  except:
1359
1511
  pass
1360
1512
  finally:
1513
+ # ALWAYS copy local vars (even if parent returned)
1514
+ for name, value in temp_scope.variables.items():
1515
+ new_scope.set(name, value)
1361
1516
  self.scope = old_scope
1362
1517
 
1363
1518
  # Bind parameters - handle both positional and named arguments
@@ -1420,7 +1575,7 @@ class CSSLRuntime:
1420
1575
 
1421
1576
  # Check exclude_type: *[type] - must NOT return excluded type
1422
1577
  exclude_type = func_info.get('exclude_type')
1423
- if exclude_type:
1578
+ if exclude_type and isinstance(exclude_type, str):
1424
1579
  type_map = {
1425
1580
  'string': str, 'int': int, 'float': float, 'bool': bool,
1426
1581
  'null': type(None), 'none': type(None),
@@ -1431,9 +1586,63 @@ class CSSLRuntime:
1431
1586
  if isinstance(return_value, tuple):
1432
1587
  for val in return_value:
1433
1588
  if excluded_py_type and isinstance(val, excluded_py_type):
1434
- raise CSSLError(f"Type exclusion: function must NOT return '{exclude_type}' values")
1589
+ raise CSSLRuntimeError(f"Type exclusion: function must NOT return '{exclude_type}' values")
1435
1590
  elif excluded_py_type and isinstance(return_value, excluded_py_type):
1436
- raise CSSLError(f"Type exclusion: function must NOT return '{exclude_type}'")
1591
+ raise CSSLRuntimeError(f"Type exclusion: function must NOT return '{exclude_type}'")
1592
+
1593
+ # Enforce return type for typed functions (like C++)
1594
+ # Typed functions MUST return the declared type
1595
+ # Exception: 'meta' modifier allows any return type
1596
+ enforce_return_type = func_info.get('enforce_return_type', False)
1597
+ return_type = func_info.get('return_type')
1598
+
1599
+ if enforce_return_type and return_type and return_type != 'void':
1600
+ # Type mapping from CSSL types to Python types
1601
+ type_validate_map = {
1602
+ 'string': str, 'int': int, 'float': (int, float), 'bool': bool,
1603
+ 'list': list, 'array': list, 'dict': dict, 'json': dict,
1604
+ 'dynamic': object, # Any type
1605
+ 'void': type(None),
1606
+ }
1607
+
1608
+ # Generic container types - accept lists/tuples
1609
+ container_types = {
1610
+ 'vector', 'stack', 'datastruct', 'dataspace',
1611
+ 'shuffled', 'iterator', 'combo', 'openquote', 'map'
1612
+ }
1613
+
1614
+ if return_type in container_types:
1615
+ # Container types accept list, tuple, dict depending on type
1616
+ if return_type == 'map':
1617
+ expected = dict
1618
+ elif return_type == 'shuffled':
1619
+ expected = (list, tuple)
1620
+ else:
1621
+ expected = (list, tuple, object)
1622
+
1623
+ if not isinstance(return_value, expected):
1624
+ func_name = func_info.get('name', 'unknown')
1625
+ actual_type = type(return_value).__name__
1626
+ raise CSSLRuntimeError(
1627
+ f"Type error in '{func_name}': declared return type '{return_type}' "
1628
+ f"but returned '{actual_type}'. Typed functions must return declared type."
1629
+ )
1630
+ elif return_type in type_validate_map:
1631
+ expected = type_validate_map[return_type]
1632
+ if expected != object and return_value is not None:
1633
+ if not isinstance(return_value, expected):
1634
+ func_name = func_info.get('name', 'unknown')
1635
+ actual_type = type(return_value).__name__
1636
+ raise CSSLRuntimeError(
1637
+ f"Type error in '{func_name}': declared return type '{return_type}' "
1638
+ f"but returned '{actual_type}'. Typed functions must return declared type."
1639
+ )
1640
+
1641
+ # Check non_null: function must return a value (not None)
1642
+ non_null = func_info.get('non_null', False)
1643
+ if non_null and return_value is None:
1644
+ func_name = func_info.get('name', 'unknown')
1645
+ raise CSSLRuntimeError(f"Non-null function '{func_name}' returned null/None")
1437
1646
 
1438
1647
  return return_value
1439
1648
  except Exception as e:
@@ -3022,7 +3231,7 @@ class CSSLRuntime:
3022
3231
  'json': dict,
3023
3232
  }
3024
3233
 
3025
- excluded_py_type = type_map.get(exclude_type.lower())
3234
+ excluded_py_type = type_map.get(exclude_type.lower() if isinstance(exclude_type, str) else exclude_type)
3026
3235
  if excluded_py_type and isinstance(value, excluded_py_type):
3027
3236
  raise self._format_error(
3028
3237
  node.line if hasattr(node, 'line') else 0,
@@ -3574,12 +3783,34 @@ class CSSLRuntime:
3574
3783
 
3575
3784
  # Resolve the class/instance reference
3576
3785
  if ref_class.startswith('$'):
3577
- # Dynamic instance reference: &$instanceVar::member
3786
+ # Dynamic instance reference: &$instanceVar::member or &$PyObject.method
3578
3787
  var_name = ref_class[1:]
3579
- ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3788
+
3789
+ # First check in _live_objects for Python shared objects
3790
+ from ..cssl_bridge import _live_objects, SharedObjectProxy
3791
+ ref_obj = _live_objects.get(var_name)
3792
+ if ref_obj is None:
3793
+ ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
3794
+
3580
3795
  if ref_obj is None:
3581
3796
  return # Instance not found, skip silently
3582
3797
 
3798
+ # Handle Python shared objects
3799
+ if isinstance(ref_obj, SharedObjectProxy):
3800
+ ref_obj = ref_obj._obj
3801
+
3802
+ # If it's a Python object (not CSSL), call the method directly
3803
+ if not isinstance(ref_obj, (CSSLInstance, CSSLClass)):
3804
+ if ref_member and hasattr(ref_obj, ref_member):
3805
+ method = getattr(ref_obj, ref_member)
3806
+ if callable(method):
3807
+ try:
3808
+ method(*args, **kwargs)
3809
+ except TypeError:
3810
+ # Try without args
3811
+ method()
3812
+ return
3813
+
3583
3814
  if isinstance(ref_obj, CSSLInstance):
3584
3815
  # Get the class definition from the instance
3585
3816
  target_class = ref_obj.class_def
@@ -3799,9 +4030,19 @@ class CSSLRuntime:
3799
4030
  self.scope = new_scope
3800
4031
  self._current_instance = instance
3801
4032
 
4033
+ original_return = None
3802
4034
  try:
4035
+ # Handle append mode via _append_to_target (stored original)
4036
+ original_method = func_info.get('_original_method')
4037
+ if original_method:
4038
+ # Execute original method first - capture return for fallback
4039
+ try:
4040
+ original_return = self._call_method(instance, original_method, args, kwargs)
4041
+ except CSSLReturn as ret:
4042
+ original_return = ret.value
4043
+
3803
4044
  # Handle append mode (++) - execute referenced parent method first
3804
- if append_mode and append_ref_class:
4045
+ elif append_mode and append_ref_class:
3805
4046
  self._execute_append_reference(
3806
4047
  instance, append_ref_class, append_ref_member,
3807
4048
  args, kwargs, {}, is_constructor=False
@@ -3822,7 +4063,8 @@ class CSSLRuntime:
3822
4063
  self.scope = old_scope
3823
4064
  self._current_instance = old_instance
3824
4065
 
3825
- return None
4066
+ # If no return in appended code, use original's return
4067
+ return original_return
3826
4068
 
3827
4069
  def _eval_member_access(self, node: ASTNode) -> Any:
3828
4070
  """Evaluate member access"""
@@ -455,12 +455,12 @@ class CsslLang:
455
455
 
456
456
  return result
457
457
  except Exception as e:
458
- # Format error message nicely
458
+ # Format error message nicely - don't add prefixes, let CLI handle that
459
459
  error_msg = str(e)
460
- # Don't double-wrap error messages
460
+ # Strip any existing CSSL Error: prefix to avoid duplication
461
461
  if error_msg.startswith("CSSL Error:"):
462
- raise RuntimeError(error_msg) from e
463
- raise RuntimeError(f"CSSL Error:\n{error_msg}") from e
462
+ error_msg = error_msg[11:].strip()
463
+ raise RuntimeError(error_msg) from e
464
464
 
465
465
  def exec(self, path_or_code: str, *args) -> Any:
466
466
  """
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "IncludeCPP"
7
- version = "3.9.2"
7
+ version = "4.0.1"
8
8
  description = "Professional C++ Python bindings with type-generic templates, pystubs and native threading"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "click>=8.0.0",
32
32
  "typing-extensions>=4.0.0",
33
33
  "requests>=2.28.0",
34
+ "colorama>=0.4.0",
34
35
  ]
35
36
 
36
37
  [project.urls]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes