IncludeCPP 3.9.1__tar.gz → 3.9.2__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.1 → includecpp-3.9.2}/IncludeCPP.egg-info/PKG-INFO +1 -1
  2. {includecpp-3.9.1 → includecpp-3.9.2}/PKG-INFO +1 -1
  3. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/__init__.py +1 -1
  4. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/CSSL_DOCUMENTATION.md +112 -4
  5. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_parser.py +68 -25
  6. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_runtime.py +257 -28
  7. {includecpp-3.9.1 → includecpp-3.9.2}/pyproject.toml +1 -1
  8. {includecpp-3.9.1 → includecpp-3.9.2}/IncludeCPP.egg-info/SOURCES.txt +0 -0
  9. {includecpp-3.9.1 → includecpp-3.9.2}/IncludeCPP.egg-info/dependency_links.txt +0 -0
  10. {includecpp-3.9.1 → includecpp-3.9.2}/IncludeCPP.egg-info/entry_points.txt +0 -0
  11. {includecpp-3.9.1 → includecpp-3.9.2}/IncludeCPP.egg-info/requires.txt +0 -0
  12. {includecpp-3.9.1 → includecpp-3.9.2}/IncludeCPP.egg-info/top_level.txt +0 -0
  13. {includecpp-3.9.1 → includecpp-3.9.2}/LICENSE +0 -0
  14. {includecpp-3.9.1 → includecpp-3.9.2}/MANIFEST.in +0 -0
  15. {includecpp-3.9.1 → includecpp-3.9.2}/README.md +0 -0
  16. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/__init__.pyi +0 -0
  17. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/__main__.py +0 -0
  18. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/cli/__init__.py +0 -0
  19. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/cli/commands.py +0 -0
  20. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/cli/config_parser.py +0 -0
  21. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/__init__.py +0 -0
  22. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/ai_integration.py +0 -0
  23. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/build_manager.py +0 -0
  24. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cpp_api.py +0 -0
  25. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cpp_api.pyi +0 -0
  26. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cppy_converter.py +0 -0
  27. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/CSSL_DOCUMENTATION_NEW.md +0 -0
  28. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/__init__.py +0 -0
  29. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_builtins.py +0 -0
  30. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_builtins.pyi +0 -0
  31. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_events.py +0 -0
  32. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_modules.py +0 -0
  33. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_syntax.py +0 -0
  34. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl/cssl_types.py +0 -0
  35. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl_bridge.py +0 -0
  36. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/cssl_bridge.pyi +0 -0
  37. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/error_catalog.py +0 -0
  38. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/error_formatter.py +0 -0
  39. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/exceptions.py +0 -0
  40. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/path_discovery.py +0 -0
  41. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/project_ui.py +0 -0
  42. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/core/settings_ui.py +0 -0
  43. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/generator/__init__.py +0 -0
  44. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/generator/parser.cpp +0 -0
  45. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/generator/parser.h +0 -0
  46. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/generator/type_resolver.cpp +0 -0
  47. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/generator/type_resolver.h +0 -0
  48. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/py.typed +0 -0
  49. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/templates/cpp.proj.template +0 -0
  50. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/__init__.py +0 -0
  51. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/__init__.py +0 -0
  52. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/extension.js +0 -0
  53. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/images/cssl.png +0 -0
  54. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/images/cssl_pl.png +0 -0
  55. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/language-configuration.json +0 -0
  56. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/package.json +0 -0
  57. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/snippets/cssl.snippets.json +0 -0
  58. {includecpp-3.9.1 → includecpp-3.9.2}/includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +0 -0
  59. {includecpp-3.9.1 → includecpp-3.9.2}/requirements.txt +0 -0
  60. {includecpp-3.9.1 → includecpp-3.9.2}/setup.cfg +0 -0
  61. {includecpp-3.9.1 → includecpp-3.9.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: IncludeCPP
3
- Version: 3.9.1
3
+ Version: 3.9.2
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.9.1
3
+ Version: 3.9.2
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.9.1"
5
+ __version__ = "3.9.2"
6
6
  __all__ = ["CppApi", "CSSL"]
7
7
 
8
8
  # Module-level cache for C++ modules
@@ -168,6 +168,42 @@ isnull(null); // true
168
168
 
169
169
  ## Variables & Globals
170
170
 
171
+ ### Scope Behavior
172
+
173
+ Variables are **local by default**. Each function/class has its own scope - variables defined inside a function only exist within that function.
174
+
175
+ ```cssl
176
+ define myFunction() {
177
+ string name = "Alice"; // Local to myFunction
178
+ printl(name); // Works
179
+ }
180
+
181
+ myFunction();
182
+ // printl(name); // Error! 'name' doesn't exist here
183
+
184
+ // Same variable name in different scopes = different variables
185
+ string name = "Bob";
186
+
187
+ define otherFunction() {
188
+ string name = "Charlie"; // Different variable, local to otherFunction
189
+ printl(name); // "Charlie"
190
+ }
191
+
192
+ otherFunction();
193
+ printl(name); // "Bob" - outer scope unchanged
194
+ ```
195
+
196
+ ### Everything is Local by Default
197
+
198
+ All elements (variables, functions, classes) are **local by default** and must be explicitly marked global.
199
+
200
+ | Element | Local (default) | Global |
201
+ |---------|-----------------|--------|
202
+ | **Variables** | `string x = "text";` | `global x = "text";` or `@x = "text";` |
203
+ | **Functions** | `define myFunc() { }` | `global define myFunc() { }` or `define @myFunc() { }` |
204
+ | **Typed functions** | `void myFunc() { }` | `global void myFunc() { }` or `void @myFunc() { }` |
205
+ | **Classes** | `class MyClass { }` | `global class MyClass { }` or `class @MyClass { }` |
206
+
171
207
  ### Local Variables
172
208
 
173
209
  ```cssl
@@ -179,6 +215,8 @@ bool active = true;
179
215
 
180
216
  ### Global Variables
181
217
 
218
+ Use `global` keyword or `@`/`r@` prefix to create/access global variables:
219
+
182
220
  ```cssl
183
221
  // Declaration with 'global' keyword
184
222
  global myGlobal = "visible everywhere";
@@ -186,11 +224,60 @@ global myGlobal = "visible everywhere";
186
224
  // Access with '@' prefix
187
225
  printl(@myGlobal);
188
226
 
189
- // Alternative: r@ syntax
190
- r@anotherGlobal = "also global";
191
- printl(@anotherGlobal);
227
+ // Direct global assignment with @ or r@
228
+ @anotherGlobal = "also global";
229
+ r@yetAnother = "and this too";
230
+
231
+ // Always read globals with @
232
+ printl(@x);
233
+ ```
234
+
235
+ ### Global Functions
236
+
237
+ ```cssl
238
+ // Global function with 'global' keyword
239
+ global define helper() {
240
+ printl("I'm global!");
241
+ }
242
+
243
+ // Alternative: @ prefix syntax
244
+ define @anotherHelper() {
245
+ printl("Also global!");
246
+ }
247
+
248
+ // Global typed function
249
+ global void @utilityFunc(string msg) {
250
+ printl(msg);
251
+ }
252
+ ```
253
+
254
+ ### Global Classes
255
+
256
+ ```cssl
257
+ // Global class with 'global' keyword
258
+ global class SharedClass {
259
+ string value;
260
+ }
261
+
262
+ // Alternative: @ prefix syntax
263
+ class @AnotherShared {
264
+ int count;
265
+ }
266
+
267
+ // Instantiate global class with @ prefix
268
+ instance = new @SharedClass();
192
269
  ```
193
270
 
271
+ ### Prefix Reference
272
+
273
+ | Prefix | Name | Usage |
274
+ |--------|------|-------|
275
+ | `@` | Global/Module | Read global vars, access modules: `@myVar`, `@os.getcwd()` |
276
+ | `r@` | Global Ref | Assign to global scope: `r@name = value` |
277
+ | `$` | Shared | Access Python objects: `$counter.value` |
278
+ | `%` | Captured | Capture value at registration: `%version` |
279
+ | `s@` | Self-Ref | Access global structs: `s@Backend.config` |
280
+
194
281
  ### Shared Objects ($)
195
282
 
196
283
  Access Python objects shared via `CSSL.share()`:
@@ -215,6 +302,20 @@ version = "2.0";
215
302
  printl(savedVersion); // Still "1.0"
216
303
  ```
217
304
 
305
+ ### Class Member Access (this->)
306
+
307
+ Inside classes, use `this->` to access instance members:
308
+
309
+ ```cssl
310
+ class Person {
311
+ string name;
312
+
313
+ void setName(string n) {
314
+ this->name = n; // Instance member
315
+ }
316
+ }
317
+ ```
318
+
218
319
  ---
219
320
 
220
321
  ## Operators
@@ -1332,7 +1433,9 @@ assert(x > 0, "x must be positive");
1332
1433
 
1333
1434
  | Syntax | Description |
1334
1435
  |--------|-------------|
1335
- | `@name` | Global variable |
1436
+ | `@name` | Global variable access |
1437
+ | `@name = value` | Global variable assignment |
1438
+ | `r@name = value` | Global reference assignment |
1336
1439
  | `$name` | Shared Python object |
1337
1440
  | `%name` | Captured value |
1338
1441
  | `this->` | Class member access |
@@ -1342,6 +1445,11 @@ assert(x > 0, "x must be positive");
1342
1445
  | `&FunctionName ++` | Append to function |
1343
1446
  | `*func()` | Non-null function |
1344
1447
  | `*[type]func()` | Type exclusion filter |
1448
+ | `global class Name` | Global class declaration |
1449
+ | `class @Name` | Global class (alt syntax) |
1450
+ | `global define func()` | Global function declaration |
1451
+ | `define @func()` | Global function (alt syntax) |
1452
+ | `new @ClassName()` | Instantiate global class |
1345
1453
 
1346
1454
  ---
1347
1455
 
@@ -816,11 +816,13 @@ class CSSLParser:
816
816
  self.pos = saved_pos
817
817
  return False
818
818
 
819
- def _parse_typed_function(self) -> ASTNode:
819
+ def _parse_typed_function(self, is_global: bool = False) -> ASTNode:
820
820
  """Parse C-style typed function declaration.
821
821
 
822
822
  Patterns:
823
- - int Add(int a, int b) { }
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)
824
826
  - undefined int Func() { }
825
827
  - open void Handler(open Params) { }
826
828
  - vector<string> GetNames() { }
@@ -869,6 +871,11 @@ class CSSLParser:
869
871
  else:
870
872
  non_null = True
871
873
 
874
+ # Check for @ prefix (global function): void @FuncName()
875
+ if self._check(TokenType.AT):
876
+ self._advance() # consume @
877
+ is_global = True
878
+
872
879
  # Get function name
873
880
  name = self._advance().value
874
881
 
@@ -966,6 +973,7 @@ class CSSLParser:
966
973
  # Parse function body
967
974
  node = ASTNode('function', value={
968
975
  'name': name,
976
+ 'is_global': is_global,
969
977
  'params': params,
970
978
  'return_type': return_type,
971
979
  'generic_type': generic_type,
@@ -1131,19 +1139,30 @@ class CSSLParser:
1131
1139
  root.children.append(self._parse_package_includes())
1132
1140
  # Handle global declarations
1133
1141
  elif self._match_keyword('global'):
1134
- stmt = self._parse_expression_statement()
1135
- if stmt:
1136
- # Wrap in global_assignment to mark as global variable
1137
- global_stmt = ASTNode('global_assignment', value=stmt)
1138
- root.children.append(global_stmt)
1142
+ # Check if followed by class or define (global class/function)
1143
+ if self._match_keyword('class'):
1144
+ root.children.append(self._parse_class(is_global=True))
1145
+ elif self._match_keyword('define'):
1146
+ root.children.append(self._parse_define(is_global=True))
1147
+ elif self._looks_like_function_declaration():
1148
+ # global void MyFunc() { } or global int MyFunc() { }
1149
+ root.children.append(self._parse_typed_function(is_global=True))
1150
+ else:
1151
+ stmt = self._parse_expression_statement()
1152
+ if stmt:
1153
+ # Wrap in global_assignment to mark as global variable
1154
+ global_stmt = ASTNode('global_assignment', value=stmt)
1155
+ root.children.append(global_stmt)
1139
1156
  elif self._check(TokenType.GLOBAL_REF):
1140
1157
  stmt = self._parse_expression_statement()
1141
1158
  if stmt:
1142
1159
  # Wrap in global_assignment to mark as global variable (same as 'global' keyword)
1143
1160
  global_stmt = ASTNode('global_assignment', value=stmt)
1144
1161
  root.children.append(global_stmt)
1145
- # Handle statements
1146
- elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF):
1162
+ # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1163
+ elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1164
+ self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
1165
+ self._check(TokenType.KEYWORD)):
1147
1166
  stmt = self._parse_expression_statement()
1148
1167
  if stmt:
1149
1168
  root.children.append(stmt)
@@ -1380,20 +1399,14 @@ class CSSLParser:
1380
1399
  self._expect(TokenType.BLOCK_END)
1381
1400
  return node
1382
1401
 
1383
- def _parse_class(self) -> ASTNode:
1402
+ def _parse_class(self, is_global: bool = False) -> ASTNode:
1384
1403
  """Parse class declaration with members and methods.
1385
1404
 
1386
1405
  Syntax:
1387
- class ClassName {
1388
- string name;
1389
- int age;
1390
-
1391
- void ClassName(string n) { this->name = n; }
1392
-
1393
- void sayHello() {
1394
- printl("Hello " + this->name);
1395
- }
1396
- }
1406
+ class ClassName { ... } // Local class
1407
+ global class ClassName { ... } // Global class
1408
+ class @ClassName { ... } // Global class (alternative)
1409
+ class *ClassName { ... } // Non-null class
1397
1410
 
1398
1411
  Non-null class (all methods return non-null):
1399
1412
  class *MyClass { ... }
@@ -1403,6 +1416,11 @@ class CSSLParser:
1403
1416
  if self._match(TokenType.MULTIPLY):
1404
1417
  non_null = True
1405
1418
 
1419
+ # Check for @ prefix (global class): class @ClassName
1420
+ if self._check(TokenType.AT):
1421
+ self._advance() # consume @
1422
+ is_global = True
1423
+
1406
1424
  class_name = self._advance().value
1407
1425
 
1408
1426
  # Check for class-level constructor parameters: class MyClass (int x, string y) { ... }
@@ -1459,6 +1477,7 @@ class CSSLParser:
1459
1477
 
1460
1478
  node = ASTNode('class', value={
1461
1479
  'name': class_name,
1480
+ 'is_global': is_global,
1462
1481
  'non_null': non_null,
1463
1482
  'class_params': class_params,
1464
1483
  'extends': extends_class,
@@ -1693,12 +1712,14 @@ class CSSLParser:
1693
1712
 
1694
1713
  return params
1695
1714
 
1696
- def _parse_define(self) -> ASTNode:
1715
+ def _parse_define(self, is_global: bool = False) -> ASTNode:
1697
1716
  """Parse define function declaration.
1698
1717
 
1699
1718
  Syntax:
1700
- define MyFunc(args) { }
1701
- define *MyFunc(args) { } // Non-null: must never return None
1719
+ define MyFunc(args) { } // Local function
1720
+ global define MyFunc(args) { } // Global function
1721
+ define @MyFunc(args) { } // Global function (alternative)
1722
+ define *MyFunc(args) { } // Non-null: must never return None
1702
1723
  define MyFunc : extends OtherFunc() { } // Inherit local vars
1703
1724
  define MyFunc : overwrites OtherFunc() { } // Replace OtherFunc
1704
1725
  define MyFunc :: extends Parent::Method :: overwrites Parent::Method() { } // Method-level inheritance
@@ -1716,6 +1737,11 @@ class CSSLParser:
1716
1737
  else:
1717
1738
  non_null = True
1718
1739
 
1740
+ # Check for @ prefix (global function): define @FuncName
1741
+ if self._check(TokenType.AT):
1742
+ self._advance() # consume @
1743
+ is_global = True
1744
+
1719
1745
  name = self._advance().value
1720
1746
 
1721
1747
  # Check for extends/overwrites: define func : extends/overwrites target() { }
@@ -1859,6 +1885,7 @@ class CSSLParser:
1859
1885
 
1860
1886
  node = ASTNode('function', value={
1861
1887
  'name': name,
1888
+ 'is_global': is_global,
1862
1889
  'params': params,
1863
1890
  'non_null': non_null,
1864
1891
  'exclude_type': exclude_type, # *[type] - must NOT return this type
@@ -2791,16 +2818,21 @@ class CSSLParser:
2791
2818
  # Just 'this' keyword alone - return as identifier for now
2792
2819
  return ASTNode('identifier', value='this')
2793
2820
 
2794
- # Handle 'new ClassName(args)' instantiation
2821
+ # Handle 'new ClassName(args)' or 'new @ClassName(args)' instantiation
2795
2822
  if self._check(TokenType.KEYWORD) and self._current().value == 'new':
2796
2823
  self._advance() # consume 'new'
2824
+ # Check for @ prefix (global class reference)
2825
+ is_global_ref = False
2826
+ if self._check(TokenType.AT):
2827
+ self._advance() # consume @
2828
+ is_global_ref = True
2797
2829
  class_name = self._advance().value # get class name
2798
2830
  args = []
2799
2831
  kwargs = {}
2800
2832
  if self._match(TokenType.PAREN_START):
2801
2833
  args, kwargs = self._parse_call_arguments()
2802
2834
  self._expect(TokenType.PAREN_END)
2803
- node = ASTNode('new', value={'class': class_name, 'args': args, 'kwargs': kwargs})
2835
+ node = ASTNode('new', value={'class': class_name, 'args': args, 'kwargs': kwargs, 'is_global_ref': is_global_ref})
2804
2836
  # Continue to check for member access, calls on the new object
2805
2837
  while True:
2806
2838
  if self._match(TokenType.DOT):
@@ -2819,7 +2851,18 @@ class CSSLParser:
2819
2851
  return node
2820
2852
 
2821
2853
  if self._match(TokenType.AT):
2854
+ # Check for @* (global non-null reference): @*name
2855
+ is_non_null = False
2856
+ if self._check(TokenType.MULTIPLY):
2857
+ self._advance() # consume *
2858
+ is_non_null = True
2859
+
2822
2860
  node = self._parse_module_reference()
2861
+
2862
+ # Wrap in non_null_assert if @* was used
2863
+ if is_non_null:
2864
+ node = ASTNode('non_null_assert', value={'operand': node, 'is_global': True})
2865
+
2823
2866
  # Continue to check for calls, indexing, member access on module refs
2824
2867
  while True:
2825
2868
  if self._match(TokenType.PAREN_START):
@@ -99,6 +99,89 @@ ERROR_HINTS = {
99
99
  }
100
100
 
101
101
 
102
+ def _find_similar_names(name: str, candidates: list, max_distance: int = 2) -> list:
103
+ """Find similar names for 'did you mean' suggestions using Levenshtein distance."""
104
+ if not candidates:
105
+ return []
106
+
107
+ def levenshtein(s1: str, s2: str) -> int:
108
+ if len(s1) < len(s2):
109
+ s1, s2 = s2, s1
110
+ if len(s2) == 0:
111
+ return len(s1)
112
+ prev_row = range(len(s2) + 1)
113
+ for i, c1 in enumerate(s1):
114
+ curr_row = [i + 1]
115
+ for j, c2 in enumerate(s2):
116
+ insertions = prev_row[j + 1] + 1
117
+ deletions = curr_row[j] + 1
118
+ substitutions = prev_row[j] + (c1.lower() != c2.lower())
119
+ curr_row.append(min(insertions, deletions, substitutions))
120
+ prev_row = curr_row
121
+ return prev_row[-1]
122
+
123
+ similar = []
124
+ name_lower = name.lower()
125
+ for candidate in candidates:
126
+ if candidate.startswith('_'):
127
+ continue
128
+ dist = levenshtein(name, candidate)
129
+ # Also check case-insensitive exact match
130
+ if name_lower == candidate.lower() and name != candidate:
131
+ similar.insert(0, candidate) # Exact case mismatch goes first
132
+ elif dist <= max_distance:
133
+ similar.append(candidate)
134
+
135
+ return similar[:3] # Return top 3 suggestions
136
+
137
+
138
+ def _get_available_classes(scope: 'Scope', global_scope: 'Scope', promoted_globals: dict) -> list:
139
+ """Get list of all available class names."""
140
+ classes = []
141
+ # Check current scope chain
142
+ current = scope
143
+ while current:
144
+ for name, val in current.variables.items():
145
+ if isinstance(val, CSSLClass) and name not in classes:
146
+ classes.append(name)
147
+ current = current.parent
148
+ # Check global scope
149
+ for name, val in global_scope.variables.items():
150
+ if isinstance(val, CSSLClass) and name not in classes:
151
+ classes.append(name)
152
+ # Check promoted globals
153
+ for name, val in promoted_globals.items():
154
+ if isinstance(val, CSSLClass) and name not in classes:
155
+ classes.append(name)
156
+ return classes
157
+
158
+
159
+ def _get_available_functions(scope: 'Scope', global_scope: 'Scope', builtins) -> list:
160
+ """Get list of all available function names."""
161
+ functions = []
162
+ # Check current scope chain
163
+ current = scope
164
+ while current:
165
+ for name, val in current.variables.items():
166
+ if callable(val) or (isinstance(val, ASTNode) and val.type == 'function'):
167
+ if name not in functions:
168
+ functions.append(name)
169
+ current = current.parent
170
+ # Check global scope
171
+ for name, val in global_scope.variables.items():
172
+ if callable(val) or (isinstance(val, ASTNode) and val.type == 'function'):
173
+ if name not in functions:
174
+ functions.append(name)
175
+ # Check builtins
176
+ if builtins:
177
+ for name in dir(builtins):
178
+ if name.startswith('builtin_'):
179
+ func_name = name[8:] # Remove 'builtin_' prefix
180
+ if func_name not in functions:
181
+ functions.append(func_name)
182
+ return functions
183
+
184
+
102
185
  class CSSLBreak(Exception):
103
186
  """Break statement"""
104
187
  pass
@@ -716,9 +799,12 @@ class CSSLRuntime:
716
799
  Parses class members and methods, creating a CSSLClass object
717
800
  that can be instantiated with 'new'.
718
801
  Supports inheritance via 'extends' keyword and method overwriting via 'overwrites'.
802
+
803
+ Classes are local by default. Use 'global class' or 'class @Name' for global classes.
719
804
  """
720
805
  class_info = node.value
721
806
  class_name = class_info.get('name')
807
+ is_global = class_info.get('is_global', False)
722
808
  extends_class_name = class_info.get('extends')
723
809
  extends_is_python = class_info.get('extends_is_python', False)
724
810
  overwrites_class_name = class_info.get('overwrites')
@@ -745,7 +831,22 @@ class CSSLRuntime:
745
831
  parent_class = self.global_scope.get(extends_class_name)
746
832
 
747
833
  if parent_class is None:
748
- raise ValueError(f"Cannot extend unknown class '{extends_class_name}'")
834
+ # Build detailed error for extends
835
+ available_classes = _get_available_classes(self.scope, self.global_scope, self._promoted_globals)
836
+ similar = _find_similar_names(extends_class_name, available_classes)
837
+
838
+ if similar:
839
+ hint = f"Did you mean: {', '.join(similar)}?"
840
+ elif extends_is_python:
841
+ hint = f"Python object '${extends_class_name}' not found. Use share() to share Python objects first."
842
+ else:
843
+ hint = f"Define class '{extends_class_name}' before this class, or use 'extends $PyObject' for Python objects"
844
+
845
+ raise self._format_error(
846
+ node.line,
847
+ f"Cannot extend unknown class '{extends_class_name}'",
848
+ hint
849
+ )
749
850
 
750
851
  # Auto-wrap Python objects for inheritance
751
852
  from .cssl_builtins import CSSLizedPythonObject
@@ -803,9 +904,11 @@ class CSSLRuntime:
803
904
  class_def.class_params = class_params # Class-level constructor parameters
804
905
  class_def.extends_args = extends_args # Arguments to pass to parent constructor
805
906
 
806
- # Register class in scope
907
+ # Register class in scope (local by default, global if marked)
807
908
  self.scope.set(class_name, class_def)
808
- self.global_scope.set(class_name, class_def)
909
+ if is_global:
910
+ self.global_scope.set(class_name, class_def)
911
+ self._promoted_globals[class_name] = class_def
809
912
 
810
913
  # Handle class overwrites - replace methods in target class
811
914
  if overwrites_class_name:
@@ -888,12 +991,15 @@ class CSSLRuntime:
888
991
  """Execute function definition - registers it and handles extends/overwrites.
889
992
 
890
993
  Syntax:
891
- define func() { ... }
892
- define func : extends otherFunc() { ... } - Inherit local vars
893
- define func : overwrites otherFunc() { ... } - Replace otherFunc
994
+ define func() { ... } - Local function
995
+ global define func() { ... } - Global function
996
+ define @func() { ... } - Global function (alternative)
997
+ define func : extends otherFunc() { ... } - Inherit local vars
998
+ define func : overwrites otherFunc() { ... } - Replace otherFunc
894
999
  """
895
1000
  func_info = node.value
896
1001
  func_name = func_info.get('name')
1002
+ is_global = func_info.get('is_global', False)
897
1003
  extends_func = func_info.get('extends')
898
1004
  extends_is_python = func_info.get('extends_is_python', False)
899
1005
  overwrites_func = func_info.get('overwrites')
@@ -922,8 +1028,11 @@ class CSSLRuntime:
922
1028
  self.scope.set(overwrites_func, node)
923
1029
  self.global_scope.set(overwrites_func, node)
924
1030
 
925
- # Register the function
1031
+ # Register the function (local by default, global if marked)
926
1032
  self.scope.set(func_name, node)
1033
+ if is_global:
1034
+ self.global_scope.set(func_name, node)
1035
+ self._promoted_globals[func_name] = node
927
1036
  return None
928
1037
 
929
1038
  def _resolve_function_target(self, name: str, is_python: bool) -> Any:
@@ -2734,7 +2843,21 @@ class CSSLRuntime:
2734
2843
  scoped_val = self.global_scope.get(f'${name}')
2735
2844
  if scoped_val is not None:
2736
2845
  return scoped_val
2737
- raise CSSLRuntimeError(f"Shared object '${name}' not found. Use share() to share objects.")
2846
+ # List available shared objects for helpful error
2847
+ available_shared = list(_live_objects.keys())
2848
+ similar = _find_similar_names(name, available_shared)
2849
+ if similar:
2850
+ hint = f"Did you mean: ${', $'.join(similar)}?"
2851
+ elif available_shared:
2852
+ hint = f"Available shared objects: ${', $'.join(available_shared[:5])}"
2853
+ else:
2854
+ hint = "Use share(name, object) from Python to share objects first."
2855
+
2856
+ raise self._format_error(
2857
+ node.line if hasattr(node, 'line') else 0,
2858
+ f"Shared object '${name}' not found",
2859
+ hint
2860
+ )
2738
2861
 
2739
2862
  if node.type == 'captured_ref':
2740
2863
  # %<name> captured reference - use value captured at infusion registration time
@@ -2761,7 +2884,13 @@ class CSSLRuntime:
2761
2884
  value = self._original_functions.get(name)
2762
2885
  if value is not None:
2763
2886
  return value
2764
- raise CSSLRuntimeError(f"Captured reference '%{name}' not found.")
2887
+ # Build helpful error for captured reference
2888
+ hint = f"Variable '{name}' must exist when the infusion is registered. Check that '%{name}' is defined before the <<== operator."
2889
+ raise self._format_error(
2890
+ node.line if hasattr(node, 'line') else 0,
2891
+ f"Captured reference '%{name}' not found",
2892
+ hint
2893
+ )
2765
2894
 
2766
2895
  if node.type == 'instance_ref':
2767
2896
  # instance<"name"> - get shared instance by name
@@ -2855,7 +2984,22 @@ class CSSLRuntime:
2855
2984
  operand = node.value.get('operand')
2856
2985
  value = self._evaluate(operand)
2857
2986
  if value is None:
2858
- raise CSSLError(f"Non-null assertion failed: value is null/None")
2987
+ # Get name of the operand for better error message
2988
+ operand_name = "unknown"
2989
+ if isinstance(operand, ASTNode):
2990
+ if operand.type == 'identifier':
2991
+ operand_name = operand.value
2992
+ elif operand.type == 'shared_ref':
2993
+ operand_name = f"${operand.value}"
2994
+ elif operand.type == 'module_ref':
2995
+ operand_name = f"@{operand.value}"
2996
+ elif operand.type == 'global_ref':
2997
+ operand_name = f"r@{operand.value}"
2998
+ raise self._format_error(
2999
+ node.line if hasattr(node, 'line') else 0,
3000
+ f"Non-null assertion failed: '{operand_name}' is null/None",
3001
+ f"The value accessed via '*{operand_name}' must not be null. Check that it is defined and initialized."
3002
+ )
2859
3003
  return value
2860
3004
 
2861
3005
  if node.type == 'type_exclude_assert':
@@ -2880,7 +3024,11 @@ class CSSLRuntime:
2880
3024
 
2881
3025
  excluded_py_type = type_map.get(exclude_type.lower())
2882
3026
  if excluded_py_type and isinstance(value, excluded_py_type):
2883
- raise CSSLError(f"Type exclusion assertion failed: value is of excluded type '{exclude_type}'")
3027
+ raise self._format_error(
3028
+ node.line if hasattr(node, 'line') else 0,
3029
+ f"Type exclusion assertion failed: value is of excluded type '{exclude_type}'",
3030
+ f"The expression was marked *[{exclude_type}] meaning it must NOT return {exclude_type}, but it did."
3031
+ )
2884
3032
  return value
2885
3033
 
2886
3034
  if node.type == 'call':
@@ -3160,12 +3308,33 @@ class CSSLRuntime:
3160
3308
  return self._call_function(callee, args, kwargs)
3161
3309
 
3162
3310
  callee_name = callee_node.value if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value') else str(callee_node)
3163
- raise CSSLRuntimeError(
3164
- f"Cannot call '{callee_name}' - it is not a function",
3165
- node.line,
3166
- context=f"Type: {type(callee).__name__}",
3167
- hint=ERROR_HINTS['undefined_function']
3168
- )
3311
+
3312
+ # Build detailed error with suggestions
3313
+ available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
3314
+ similar = _find_similar_names(callee_name, available_funcs)
3315
+
3316
+ if callee is None:
3317
+ # Function not found at all
3318
+ if similar:
3319
+ hint = f"Did you mean: {', '.join(similar)}?"
3320
+ else:
3321
+ hint = f"Function '{callee_name}' is not defined. Define it with: define {callee_name}() {{ }}"
3322
+ raise self._format_error(
3323
+ node.line,
3324
+ f"Function '{callee_name}' not found",
3325
+ hint
3326
+ )
3327
+ else:
3328
+ # Found something but it's not callable
3329
+ if similar:
3330
+ hint = f"'{callee_name}' is a {type(callee).__name__}, not a function. Did you mean: {', '.join(similar)}?"
3331
+ else:
3332
+ hint = f"'{callee_name}' is a {type(callee).__name__}. Functions must be defined with 'define' keyword."
3333
+ raise self._format_error(
3334
+ node.line,
3335
+ f"Cannot call '{callee_name}' - it is not a function",
3336
+ hint
3337
+ )
3169
3338
 
3170
3339
  def _eval_typed_call(self, node: ASTNode) -> Any:
3171
3340
  """Evaluate typed function call like OpenFind<string>(0) or OpenFind<dynamic, "name">"""
@@ -3231,26 +3400,53 @@ class CSSLRuntime:
3231
3400
  )
3232
3401
 
3233
3402
  def _eval_new(self, node: ASTNode) -> CSSLInstance:
3234
- """Evaluate 'new ClassName(args)' expression.
3403
+ """Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
3235
3404
 
3236
3405
  Creates a new instance of a CSSL class and calls its constructor.
3237
3406
  Supports multiple constructors (constr keyword), class parameters,
3238
3407
  and automatic parent constructor calling.
3408
+
3409
+ With '@' prefix (new @ClassName), looks only in global scope.
3239
3410
  """
3240
3411
  class_name = node.value.get('class')
3412
+ is_global_ref = node.value.get('is_global_ref', False)
3241
3413
  args = [self._evaluate(arg) for arg in node.value.get('args', [])]
3242
3414
  kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
3243
3415
 
3244
3416
  # Get class definition from scope
3245
- class_def = self.scope.get(class_name)
3246
- if class_def is None:
3247
- class_def = self.global_scope.get(class_name)
3417
+ if is_global_ref:
3418
+ # With @ prefix, only look in global scope
3419
+ class_def = self._promoted_globals.get(class_name)
3420
+ if class_def is None:
3421
+ class_def = self.global_scope.get(class_name)
3422
+ else:
3423
+ # Normal lookup: local scope first, then global
3424
+ class_def = self.scope.get(class_name)
3425
+ if class_def is None:
3426
+ class_def = self.global_scope.get(class_name)
3248
3427
 
3249
3428
  if class_def is None:
3250
- raise CSSLRuntimeError(
3251
- f"Class '{class_name}' not found",
3429
+ # Build detailed error with suggestions
3430
+ source_line = self._get_source_line(node.line)
3431
+ available_classes = _get_available_classes(self.scope, self.global_scope, self._promoted_globals)
3432
+ similar = _find_similar_names(class_name, available_classes)
3433
+
3434
+ # Check if class exists in global scope (user forgot @)
3435
+ global_class = self._promoted_globals.get(class_name) or self.global_scope.get(class_name)
3436
+ if global_class and isinstance(global_class, CSSLClass) and not is_global_ref:
3437
+ hint = f"Class '{class_name}' exists in global scope. Use: new @{class_name}()"
3438
+ elif similar:
3439
+ hint = f"Did you mean: {', '.join(similar)}?"
3440
+ elif available_classes:
3441
+ hint = f"Available classes: {', '.join(available_classes[:5])}"
3442
+ else:
3443
+ hint = "Define the class before instantiation, or use 'global class' / 'class @Name' for global classes"
3444
+
3445
+ context = f"in expression: {source_line.strip()}" if source_line else None
3446
+ raise self._format_error(
3252
3447
  node.line,
3253
- hint="Make sure the class is defined before instantiation"
3448
+ f"Class '{class_name}' not found",
3449
+ hint
3254
3450
  )
3255
3451
 
3256
3452
  if not isinstance(class_def, CSSLClass):
@@ -3542,9 +3738,24 @@ class CSSLRuntime:
3542
3738
  return lambda *args, **kwargs: python_method(*args, **kwargs)
3543
3739
  return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
3544
3740
 
3545
- raise CSSLRuntimeError(
3546
- f"'{instance._class.name}' has no member or method '{member}'",
3547
- node.line if hasattr(node, 'line') else 0
3741
+ # Build helpful error with available members
3742
+ class_name = instance._class.name
3743
+ available_members = list(instance._members.keys()) if hasattr(instance, '_members') else []
3744
+ available_methods = list(instance._methods.keys()) if hasattr(instance, '_methods') else []
3745
+ all_available = available_members + available_methods
3746
+ similar = _find_similar_names(member, all_available)
3747
+
3748
+ if similar:
3749
+ hint = f"Did you mean: {', '.join(similar)}?"
3750
+ elif all_available:
3751
+ hint = f"Available: {', '.join(all_available[:5])}"
3752
+ else:
3753
+ hint = f"Class '{class_name}' has no accessible members. Check class definition."
3754
+
3755
+ raise self._format_error(
3756
+ node.line if hasattr(node, 'line') else 0,
3757
+ f"'{class_name}' has no member or method '{member}'",
3758
+ hint
3548
3759
  )
3549
3760
 
3550
3761
  def _call_method(self, instance: CSSLInstance, method_node: ASTNode, args: list, kwargs: dict = None) -> Any:
@@ -3639,7 +3850,25 @@ class CSSLRuntime:
3639
3850
  python_method = method_node[1]
3640
3851
  return lambda *args, **kwargs: python_method(*args, **kwargs)
3641
3852
  return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
3642
- raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")
3853
+ # Build helpful error with available members
3854
+ class_name = obj._class.name
3855
+ available_members = list(obj._members.keys()) if hasattr(obj, '_members') else []
3856
+ available_methods = list(obj._methods.keys()) if hasattr(obj, '_methods') else []
3857
+ all_available = available_members + available_methods
3858
+ similar = _find_similar_names(member, all_available)
3859
+
3860
+ if similar:
3861
+ hint = f"Did you mean: {', '.join(similar)}?"
3862
+ elif all_available:
3863
+ hint = f"Available: {', '.join(all_available[:5])}"
3864
+ else:
3865
+ hint = f"Class '{class_name}' has no accessible members."
3866
+
3867
+ raise self._format_error(
3868
+ node.line,
3869
+ f"'{class_name}' has no member or method '{member}'",
3870
+ hint
3871
+ )
3643
3872
 
3644
3873
  # === STRING METHODS ===
3645
3874
  if isinstance(obj, str):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "IncludeCPP"
7
- version = "3.9.1"
7
+ version = "3.9.2"
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"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes