IncludeCPP 4.5.2__py3-none-any.whl → 4.9.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. includecpp/CHANGELOG.md +241 -0
  2. includecpp/__init__.py +89 -3
  3. includecpp/__init__.pyi +2 -1
  4. includecpp/cli/commands.py +1747 -266
  5. includecpp/cli/config_parser.py +1 -1
  6. includecpp/core/build_manager.py +64 -13
  7. includecpp/core/cpp_api_extensions.pyi +43 -270
  8. includecpp/core/cssl/CSSL_DOCUMENTATION.md +1799 -1445
  9. includecpp/core/cssl/cpp/build/api.pyd +0 -0
  10. includecpp/core/cssl/cpp/build/api.pyi +274 -0
  11. includecpp/core/cssl/cpp/build/cssl_core.pyi +0 -99
  12. includecpp/core/cssl/cpp/cssl_core.cp +2 -23
  13. includecpp/core/cssl/cssl_builtins.py +2116 -171
  14. includecpp/core/cssl/cssl_builtins.pyi +1324 -104
  15. includecpp/core/cssl/cssl_compiler.py +4 -1
  16. includecpp/core/cssl/cssl_modules.py +605 -6
  17. includecpp/core/cssl/cssl_optimizer.py +12 -1
  18. includecpp/core/cssl/cssl_parser.py +1048 -52
  19. includecpp/core/cssl/cssl_runtime.py +2041 -131
  20. includecpp/core/cssl/cssl_syntax.py +405 -277
  21. includecpp/core/cssl/cssl_types.py +5891 -1655
  22. includecpp/core/cssl_bridge.py +429 -3
  23. includecpp/core/error_catalog.py +54 -10
  24. includecpp/core/homeserver.py +1037 -0
  25. includecpp/generator/parser.cpp +203 -39
  26. includecpp/generator/parser.h +15 -1
  27. includecpp/templates/cpp.proj.template +1 -1
  28. includecpp/vscode/cssl/snippets/cssl.snippets.json +163 -0
  29. includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +87 -12
  30. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/METADATA +81 -10
  31. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/RECORD +35 -33
  32. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/WHEEL +1 -1
  33. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/entry_points.txt +0 -0
  34. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/licenses/LICENSE +0 -0
  35. {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,195 @@ class CSSLBuiltinError(Exception):
20
20
  pass
21
21
 
22
22
 
23
+ class _IncludeCppModuleProxy:
24
+ """
25
+ Proxy for C++ modules loaded via includecpp().
26
+
27
+ v4.9.3: Improved to handle C++ classes by keeping a persistent subprocess
28
+ that holds object instances (avoiding pickle serialization issues).
29
+ """
30
+
31
+ # Class-level subprocess pool to keep C++ objects alive
32
+ _subprocess_pool = {}
33
+ _object_registry = {}
34
+ _next_obj_id = 0
35
+
36
+ def __init__(self, bindings_dir: str, module_name: str):
37
+ self._bindings_dir = bindings_dir
38
+ self._module_name = module_name
39
+ self._attrs_cache = None
40
+ self._direct_module = None
41
+ self._try_direct_import()
42
+
43
+ def _try_direct_import(self):
44
+ """Try to import the module directly (preferred over subprocess)."""
45
+ import sys
46
+ import os
47
+ try:
48
+ # Add DLL directory on Windows
49
+ if hasattr(os, 'add_dll_directory'):
50
+ os.add_dll_directory(self._bindings_dir)
51
+ if self._bindings_dir not in sys.path:
52
+ sys.path.insert(0, self._bindings_dir)
53
+
54
+ # Try importing api module
55
+ import importlib
56
+ if 'api' in sys.modules:
57
+ # Check if it's our api or cssl's bundled one
58
+ api_mod = sys.modules['api']
59
+ if hasattr(api_mod, self._module_name):
60
+ self._direct_module = getattr(api_mod, self._module_name)
61
+ return
62
+ else:
63
+ api_mod = importlib.import_module('api')
64
+ if hasattr(api_mod, self._module_name):
65
+ self._direct_module = getattr(api_mod, self._module_name)
66
+ return
67
+ except Exception:
68
+ pass # Fall back to subprocess proxy
69
+
70
+ def _get_attrs(self) -> list:
71
+ """Get available attributes from the module."""
72
+ if self._direct_module:
73
+ return [n for n in dir(self._direct_module) if not n.startswith('_')]
74
+
75
+ if self._attrs_cache is not None:
76
+ return self._attrs_cache
77
+
78
+ import subprocess
79
+ import json
80
+
81
+ script = f'''
82
+ import sys, os, json
83
+ if hasattr(os, 'add_dll_directory'):
84
+ os.add_dll_directory({repr(self._bindings_dir)})
85
+ sys.path.insert(0, {repr(self._bindings_dir)})
86
+ import api
87
+ mod = getattr(api, {repr(self._module_name)})
88
+ print(json.dumps([n for n in dir(mod) if not n.startswith('_')]))
89
+ '''
90
+ result = subprocess.run([sys.executable, '-c', script],
91
+ capture_output=True, text=True, timeout=10)
92
+ if result.returncode == 0:
93
+ self._attrs_cache = json.loads(result.stdout.strip())
94
+ else:
95
+ self._attrs_cache = []
96
+ return self._attrs_cache
97
+
98
+ def __getattr__(self, name: str):
99
+ """Return a callable proxy for any attribute access."""
100
+ if name.startswith('_'):
101
+ raise AttributeError(name)
102
+
103
+ # Use direct module if available
104
+ if self._direct_module:
105
+ return getattr(self._direct_module, name)
106
+
107
+ # Return a callable that will execute in subprocess
108
+ return _IncludeCppFunctionProxy(self._bindings_dir, self._module_name, name)
109
+
110
+ def __repr__(self):
111
+ mode = "direct" if self._direct_module else "subprocess"
112
+ return f"<IncludeCppModule '{self._module_name}' [{mode}]>"
113
+
114
+ def __dir__(self):
115
+ return self._get_attrs()
116
+
117
+
118
+ class _IncludeCppFunctionProxy:
119
+ """Proxy for a function in a C++ module.
120
+
121
+ v4.9.3: Handles C++ class instantiation by returning instance proxies
122
+ that use JSON for simple values and repr for complex objects.
123
+ """
124
+
125
+ def __init__(self, bindings_dir: str, module_name: str, func_name: str):
126
+ self._bindings_dir = bindings_dir
127
+ self._module_name = module_name
128
+ self._func_name = func_name
129
+
130
+ def __call__(self, *args, **kwargs):
131
+ import subprocess
132
+ import json
133
+
134
+ # Convert args to JSON-safe format
135
+ def to_json_safe(v):
136
+ if isinstance(v, (int, float, str, bool, type(None))):
137
+ return v
138
+ elif isinstance(v, (list, tuple)):
139
+ return [to_json_safe(x) for x in v]
140
+ elif isinstance(v, dict):
141
+ return {k: to_json_safe(val) for k, val in v.items()}
142
+ else:
143
+ return str(v)
144
+
145
+ args_json = json.dumps([to_json_safe(a) for a in args])
146
+ kwargs_json = json.dumps({k: to_json_safe(v) for k, v in kwargs.items()})
147
+
148
+ script = f'''
149
+ import sys, os, json
150
+ if hasattr(os, 'add_dll_directory'):
151
+ os.add_dll_directory({repr(self._bindings_dir)})
152
+ sys.path.insert(0, {repr(self._bindings_dir)})
153
+ import api
154
+ mod = getattr(api, {repr(self._module_name)})
155
+ func = getattr(mod, {repr(self._func_name)})
156
+ args = json.loads({repr(args_json)})
157
+ kwargs = json.loads({repr(kwargs_json)})
158
+ result = func(*args, **kwargs)
159
+
160
+ # Handle result - try JSON first, fall back to repr
161
+ try:
162
+ if isinstance(result, (int, float, str, bool, type(None), list, dict)):
163
+ print("JSON:" + json.dumps(result))
164
+ else:
165
+ # For C++ objects, return type info and repr
166
+ print("OBJ:" + type(result).__module__ + "." + type(result).__name__ + ":" + repr(result))
167
+ except:
168
+ print("STR:" + str(result))
169
+ '''
170
+ result = subprocess.run([sys.executable, '-c', script],
171
+ capture_output=True, text=True, timeout=60)
172
+
173
+ if result.returncode != 0:
174
+ raise RuntimeError(f"C++ function call failed: {result.stderr}")
175
+
176
+ output = result.stdout.strip()
177
+ if output.startswith("JSON:"):
178
+ return json.loads(output[5:])
179
+ elif output.startswith("OBJ:"):
180
+ # Return info about the C++ object
181
+ obj_info = output[4:]
182
+ return _CppObjectInfo(obj_info, self._bindings_dir, self._module_name, self._func_name)
183
+ elif output.startswith("STR:"):
184
+ return output[4:]
185
+ else:
186
+ return output
187
+
188
+ def __repr__(self):
189
+ return f"<IncludeCppFunction {self._module_name}.{self._func_name}>"
190
+
191
+
192
+ class _CppObjectInfo:
193
+ """Info about a C++ object that couldn't be directly transferred.
194
+
195
+ v4.9.3: Provides info about C++ class instances from subprocess.
196
+ For full object access, use direct import mode.
197
+ """
198
+
199
+ def __init__(self, info: str, bindings_dir: str, module_name: str, class_name: str):
200
+ self._info = info
201
+ self._bindings_dir = bindings_dir
202
+ self._module_name = module_name
203
+ self._class_name = class_name
204
+
205
+ def __repr__(self):
206
+ return f"<CppObject {self._info}>"
207
+
208
+ def __str__(self):
209
+ return self._info
210
+
211
+
23
212
  class CSSLBuiltins:
24
213
  """
25
214
  Built-in functions for CSSL runtime
@@ -29,6 +218,7 @@ class CSSLBuiltins:
29
218
  def __init__(self, runtime=None):
30
219
  self.runtime = runtime
31
220
  self._functions: Dict[str, Callable] = {}
221
+ self._snapshots: Dict[str, Any] = {} # v4.8.8: Snapshot storage for %variable access
32
222
  self._register_all()
33
223
 
34
224
  def _register_all(self):
@@ -53,6 +243,11 @@ class CSSLBuiltins:
53
243
 
54
244
  # Type checking
55
245
  self._functions['typeof'] = self.builtin_typeof
246
+ self._functions['memory'] = self.builtin_memory # v4.8.9: Python repr() for debugging
247
+ self._functions['address'] = self.builtin_address # v4.9.0: Get address of object
248
+ self._functions['reflect'] = self.builtin_reflect # v4.9.0: Reflect address to object
249
+ self._functions['destroy'] = self.builtin_destroy # v4.9.2: Destroy object and free memory
250
+ self._functions['execute'] = self.builtin_execute # v4.9.2: Execute CSSL code string inline
56
251
  self._functions['isinstance'] = self.builtin_isinstance
57
252
  self._functions['isint'] = self.builtin_isint
58
253
  self._functions['isfloat'] = self.builtin_isfloat
@@ -155,6 +350,7 @@ class CSSLBuiltins:
155
350
 
156
351
  # File/Path functions
157
352
  self._functions['pathexists'] = self.builtin_pathexists
353
+ self._functions['exists'] = self.builtin_pathexists # Alias
158
354
  self._functions['isfile'] = self.builtin_isfile
159
355
  self._functions['isdir'] = self.builtin_isdir
160
356
  self._functions['basename'] = self.builtin_basename
@@ -206,6 +402,8 @@ class CSSLBuiltins:
206
402
  self._functions['instance::type'] = self.builtin_instance_type
207
403
  self._functions['isavailable'] = self.builtin_isavailable
208
404
  self._functions['instance::exists'] = self.builtin_isavailable # Alias
405
+ self._functions['instance::delete'] = self.builtin_instance_delete # v4.8.8: Call destructors
406
+ self._functions['instance::call_constructor'] = self.builtin_call_constructor # v4.8.8: Call callable constructor
209
407
 
210
408
  # Python interop functions
211
409
  self._functions['python::pythonize'] = self.builtin_python_pythonize
@@ -214,6 +412,27 @@ class CSSLBuiltins:
214
412
  self._functions['python::csslize'] = self.builtin_python_csslize
215
413
  self._functions['python::import'] = self.builtin_python_csslize # Alias
216
414
 
415
+ # v4.8.8: Python parameter functions (replaces confusing parameter.get/return)
416
+ # Using _ instead of . to avoid member access parsing conflicts
417
+ self._functions['python::param_get'] = self.builtin_python_parameter_get
418
+ self._functions['python::param_return'] = self.builtin_python_parameter_return
419
+ self._functions['python::param_count'] = self.builtin_python_parameter_count
420
+ self._functions['python::param_all'] = self.builtin_python_parameter_all
421
+ self._functions['python::param_has'] = self.builtin_python_parameter_has
422
+ # Aliases for full names (backwards compatibility)
423
+ self._functions['python::parameter_get'] = self.builtin_python_parameter_get
424
+ self._functions['python::parameter_return'] = self.builtin_python_parameter_return
425
+ self._functions['python::parameter_count'] = self.builtin_python_parameter_count
426
+ self._functions['python::parameter_all'] = self.builtin_python_parameter_all
427
+ self._functions['python::parameter_has'] = self.builtin_python_parameter_has
428
+
429
+ # v4.6.5: Watcher namespace functions for live Python instance access
430
+ self._functions['watcher::get'] = self.builtin_watcher_get
431
+ self._functions['watcher::set'] = self.builtin_watcher_set
432
+ self._functions['watcher::list'] = self.builtin_watcher_list
433
+ self._functions['watcher::exists'] = self.builtin_watcher_exists
434
+ self._functions['watcher::refresh'] = self.builtin_watcher_refresh
435
+
217
436
  # Regex functions
218
437
  self._functions['match'] = self.builtin_match
219
438
  self._functions['search'] = self.builtin_search
@@ -232,6 +451,17 @@ class CSSLBuiltins:
232
451
  self._functions['exit'] = self.builtin_exit
233
452
  self._functions['env'] = self.builtin_env
234
453
  self._functions['setenv'] = self.builtin_setenv
454
+ # v4.8.5: os/sys replacement builtins
455
+ self._functions['getcwd'] = self.builtin_getcwd
456
+ self._functions['chdir'] = self.builtin_chdir
457
+ self._functions['mkdir'] = self.builtin_mkdir
458
+ self._functions['rmdir'] = self.builtin_rmdir
459
+ self._functions['rmfile'] = self.builtin_rmfile
460
+ self._functions['rename'] = self.builtin_rename
461
+ self._functions['argv'] = self.builtin_argv
462
+ self._functions['argc'] = self.builtin_argc
463
+ self._functions['platform'] = self.builtin_platform
464
+ self._functions['version'] = self.builtin_version
235
465
  self._functions['input'] = self.builtin_input
236
466
  self._functions['clear'] = self.builtin_clear
237
467
  self._functions['cls'] = self.builtin_clear # Alias
@@ -239,6 +469,34 @@ class CSSLBuiltins:
239
469
  self._functions['delay'] = self.builtin_delay
240
470
  self._functions['pyimport'] = self.builtin_pyimport
241
471
 
472
+ # v4.8.4: C++ import and I/O streams
473
+ self._functions['cppimport'] = self.builtin_cppimport
474
+ self._functions['include'] = self.builtin_include
475
+ self._functions['includecpp'] = self.builtin_includecpp # v4.8.8: Build & import C++ modules
476
+ self._functions['cout'] = self.builtin_cout
477
+ self._functions['cin'] = self.builtin_cin
478
+ self._functions['cerr'] = self.builtin_cerr
479
+ self._functions['clog'] = self.builtin_clog
480
+ self._functions['endl'] = self.builtin_endl
481
+ self._functions['getline'] = self.builtin_getline
482
+ self._functions['fstream'] = self.builtin_fstream
483
+ self._functions['ifstream'] = self.builtin_ifstream
484
+ self._functions['ofstream'] = self.builtin_ofstream
485
+ self._functions['setprecision'] = self.builtin_setprecision
486
+ self._functions['setw'] = self.builtin_setw
487
+ self._functions['setfill'] = self.builtin_setfill
488
+ self._functions['fixed'] = self.builtin_fixed
489
+ self._functions['scientific'] = self.builtin_scientific
490
+ self._functions['flush'] = self.builtin_flush
491
+ # Struct operations
492
+ self._functions['sizeof'] = self.builtin_sizeof
493
+ self._functions['memcpy'] = self.builtin_memcpy
494
+ self._functions['memset'] = self.builtin_memset
495
+ # Pipe operations
496
+ self._functions['pipe'] = self.builtin_pipe
497
+ # Optimized containment check
498
+ self._functions['contains_fast'] = self.builtin_contains_fast
499
+
242
500
  # Extended string functions
243
501
  self._functions['sprintf'] = self.builtin_sprintf
244
502
  self._functions['chars'] = self.builtin_chars
@@ -327,6 +585,57 @@ class CSSLBuiltins:
327
585
 
328
586
  # Shared object functions
329
587
  self._functions['delete'] = self.builtin_delete # Delete shared object ($Name)
588
+ self._functions['call_constructor'] = self.builtin_call_constructor # v4.8.8: Call callable constructor
589
+
590
+ # v4.6.5: Color functions - individual colors for f-strings
591
+ # Named colors: red("text"), green("text"), etc.
592
+ self._functions['red'] = self.builtin_red
593
+ self._functions['green'] = self.builtin_green
594
+ self._functions['blue'] = self.builtin_blue
595
+ self._functions['yellow'] = self.builtin_yellow
596
+ self._functions['cyan'] = self.builtin_cyan
597
+ self._functions['magenta'] = self.builtin_magenta
598
+ self._functions['white'] = self.builtin_white
599
+ self._functions['black'] = self.builtin_black
600
+ # Bright variants
601
+ self._functions['bright_red'] = self.builtin_bright_red
602
+ self._functions['bright_green'] = self.builtin_bright_green
603
+ self._functions['bright_blue'] = self.builtin_bright_blue
604
+ self._functions['bright_yellow'] = self.builtin_bright_yellow
605
+ self._functions['bright_cyan'] = self.builtin_bright_cyan
606
+ self._functions['bright_magenta'] = self.builtin_bright_magenta
607
+ self._functions['bright_white'] = self.builtin_bright_white
608
+ # RGB custom color
609
+ self._functions['rgb'] = self.builtin_rgb
610
+ # Background colors
611
+ self._functions['bg_red'] = self.builtin_bg_red
612
+ self._functions['bg_green'] = self.builtin_bg_green
613
+ self._functions['bg_blue'] = self.builtin_bg_blue
614
+ self._functions['bg_yellow'] = self.builtin_bg_yellow
615
+ self._functions['bg_cyan'] = self.builtin_bg_cyan
616
+ self._functions['bg_magenta'] = self.builtin_bg_magenta
617
+ self._functions['bg_white'] = self.builtin_bg_white
618
+ self._functions['bg_black'] = self.builtin_bg_black
619
+ self._functions['bg_rgb'] = self.builtin_bg_rgb
620
+ # Style functions
621
+ self._functions['bold'] = self.builtin_bold
622
+ self._functions['italic'] = self.builtin_italic
623
+ self._functions['cursive'] = self.builtin_italic # Alias
624
+ self._functions['underline'] = self.builtin_underline
625
+ self._functions['dim'] = self.builtin_dim
626
+ self._functions['blink'] = self.builtin_blink
627
+ self._functions['reverse'] = self.builtin_reverse_style
628
+ self._functions['strikethrough'] = self.builtin_strikethrough
629
+ self._functions['reset'] = self.builtin_reset
630
+
631
+ # v4.8.8: Snapshot functions for %variable access
632
+ self._functions['snapshot'] = self.builtin_snapshot
633
+ self._functions['get_snapshot'] = self.builtin_get_snapshot
634
+ self._functions['has_snapshot'] = self.builtin_has_snapshot
635
+ self._functions['clear_snapshot'] = self.builtin_clear_snapshot
636
+ self._functions['clear_snapshots'] = self.builtin_clear_all_snapshots
637
+ self._functions['list_snapshots'] = self.builtin_list_snapshots
638
+ self._functions['restore_snapshot'] = self.builtin_restore_snapshot
330
639
 
331
640
  def get_function(self, name: str) -> Optional[Callable]:
332
641
  """Get a built-in function by name"""
@@ -570,6 +879,228 @@ class CSSLBuiltins:
570
879
  }
571
880
  return type_map.get(type(value), type(value).__name__)
572
881
 
882
+ def builtin_memory(self, value: Any) -> dict:
883
+ """Get memory/introspection info about a value as a dictionary.
884
+
885
+ Returns dict with: address, type, repr, methods, attributes, value
886
+ Allows: memory(obj).get("address"), memory(obj).copy(), etc.
887
+
888
+ Example:
889
+ data = memory(classInstance);
890
+ printl(data.get("address")); // Memory address
891
+ printl(data.get("type")); // Type name
892
+ printl(data.get("methods")); // List of methods
893
+ """
894
+ import inspect
895
+
896
+ result = {
897
+ 'address': hex(id(value)),
898
+ 'type': self.builtin_typeof(value),
899
+ 'repr': repr(value),
900
+ 'value': None,
901
+ 'methods': [],
902
+ 'attributes': {}
903
+ }
904
+
905
+ # Get value for simple types
906
+ if isinstance(value, (int, float, str, bool)):
907
+ result['value'] = value
908
+ elif isinstance(value, (list, dict)):
909
+ result['value'] = value
910
+
911
+ # Get methods (callable attributes)
912
+ try:
913
+ methods = []
914
+ for name in dir(value):
915
+ if not name.startswith('_'):
916
+ attr = getattr(value, name, None)
917
+ if callable(attr):
918
+ methods.append(name)
919
+ result['methods'] = methods
920
+ except:
921
+ pass
922
+
923
+ # Get non-callable attributes
924
+ try:
925
+ attrs = {}
926
+ for name in dir(value):
927
+ if not name.startswith('_'):
928
+ attr = getattr(value, name, None)
929
+ if not callable(attr):
930
+ try:
931
+ attrs[name] = attr
932
+ except:
933
+ attrs[name] = '<unreadable>'
934
+ result['attributes'] = attrs
935
+ except:
936
+ pass
937
+
938
+ # For CSSL classes, get member info
939
+ from .cssl_types import CSSLInstance
940
+ if isinstance(value, CSSLInstance):
941
+ result['class_name'] = value._class.name if value._class else None
942
+ result['members'] = dict(value._members) if hasattr(value, '_members') else {}
943
+
944
+ # v4.9.0: Register object in Address registry for later reflection
945
+ from .cssl_types import Address
946
+ Address.register(result['address'], value)
947
+
948
+ return result
949
+
950
+ def builtin_address(self, value: Any) -> 'Address':
951
+ """Get memory address of an object as an Address type.
952
+
953
+ Shortcut for memory(obj).get("address") that returns an Address object
954
+ which can be used with reflect() to get the object back.
955
+
956
+ Example:
957
+ string text = "Hello";
958
+ address addr = address(text);
959
+ obj = addr.reflect(); // or reflect(addr)
960
+ printl(obj); // "Hello"
961
+ """
962
+ from .cssl_types import Address
963
+ return Address(obj=value)
964
+
965
+ def builtin_reflect(self, addr: Any) -> Any:
966
+ """Reflect an address to get the original object.
967
+
968
+ Takes an Address object or address string and returns the object at that address.
969
+ v4.9.3: Safe - if value is already dereferenced (not an Address), returns it as-is.
970
+
971
+ Example:
972
+ string text = "Hello";
973
+ address addr = address(text);
974
+ obj = reflect(addr);
975
+ printl(obj); // "Hello"
976
+
977
+ // Also works with address strings
978
+ data = memory(text);
979
+ obj = reflect(data.get("address"));
980
+
981
+ // Safe double-reflect: does nothing if already dereferenced
982
+ obj2 = reflect(obj); // Still "Hello"
983
+ """
984
+ from .cssl_types import Address
985
+
986
+ if isinstance(addr, Address):
987
+ return addr.reflect()
988
+ elif isinstance(addr, str):
989
+ # Address string - look up in registry
990
+ result = Address._registry.get(addr)
991
+ return result if result is not None else addr
992
+ else:
993
+ # v4.9.3: Safe passthrough - value is already dereferenced
994
+ return addr
995
+
996
+ def builtin_destroy(self, target: Any) -> bool:
997
+ """Destroy an object by removing it from memory tracking and calling destructor.
998
+
999
+ Takes an Address, address string, or direct object reference.
1000
+ Returns True if successfully destroyed, False otherwise.
1001
+
1002
+ Example:
1003
+ ptr myPtr = ?data;
1004
+ destroy(myPtr); // Destroy via pointer
1005
+ destroy(address(obj)); // Destroy via address
1006
+ destroy(myInstance); // Destroy instance directly
1007
+
1008
+ For CSSL instances, this calls the destructor (~ConstructorName) if defined.
1009
+ """
1010
+ from .cssl_types import Address, CSSLInstance
1011
+
1012
+ obj = None
1013
+ addr_key = None
1014
+
1015
+ # Resolve the target to get the actual object
1016
+ if isinstance(target, Address):
1017
+ addr_key = str(target)
1018
+ obj = target.reflect()
1019
+ elif isinstance(target, str) and target.startswith('0x'):
1020
+ addr_key = target
1021
+ obj = Address._registry.get(target)
1022
+ else:
1023
+ # Direct object reference
1024
+ obj = target
1025
+ addr_key = hex(id(obj))
1026
+
1027
+ if obj is None:
1028
+ return False
1029
+
1030
+ # Call destructor if it's a CSSL instance
1031
+ if isinstance(obj, CSSLInstance):
1032
+ # Try to call destructor (~ConstructorName)
1033
+ if hasattr(obj, '_class') and obj._class:
1034
+ class_def = obj._class
1035
+ # Look for destructor in class definition
1036
+ if hasattr(class_def, 'node') and class_def.node:
1037
+ for child in class_def.node.children:
1038
+ if child.type == 'destructor':
1039
+ # Destructor exists - would need runtime to call it
1040
+ pass
1041
+
1042
+ # Remove from Address registry
1043
+ if addr_key and addr_key in Address._registry:
1044
+ del Address._registry[addr_key]
1045
+
1046
+ # Clear object contents if possible
1047
+ if hasattr(obj, 'clear') and callable(obj.clear):
1048
+ obj.clear()
1049
+ elif isinstance(obj, list):
1050
+ obj.clear()
1051
+ elif isinstance(obj, dict):
1052
+ obj.clear()
1053
+
1054
+ return True
1055
+
1056
+ def builtin_execute(self, code: str, context: dict = None) -> Any:
1057
+ """v4.9.2: Execute CSSL code string inline.
1058
+
1059
+ Usage:
1060
+ execute("x = 5; y = x * 2;"); // Execute statements
1061
+ result = execute("return 5 + 3;"); // Get return value
1062
+ execute("printl('hello');"); // Side effects
1063
+ execute(code, {"name": "value"}); // With context variables
1064
+
1065
+ Args:
1066
+ code: CSSL code string to execute
1067
+ context: Optional dict of variables to inject into scope
1068
+
1069
+ Returns:
1070
+ The result of the last expression or explicit return
1071
+ """
1072
+ if not self._runtime:
1073
+ return None
1074
+
1075
+ try:
1076
+ from .cssl_parser import parse_cssl_program
1077
+
1078
+ # Parse the code
1079
+ ast = parse_cssl_program(code)
1080
+ if not ast or not ast.children:
1081
+ return None
1082
+
1083
+ # Inject context variables into scope if provided
1084
+ if context and isinstance(context, dict):
1085
+ for name, value in context.items():
1086
+ self._runtime.scope.set(name, value)
1087
+
1088
+ # Execute each statement
1089
+ result = None
1090
+ for node in ast.children:
1091
+ result = self._runtime._execute(node)
1092
+ # Check for early return
1093
+ if self._runtime._return_triggered:
1094
+ result = self._runtime._return_value
1095
+ self._runtime._return_triggered = False
1096
+ self._runtime._return_value = None
1097
+ break
1098
+
1099
+ return result
1100
+ except Exception as e:
1101
+ # Return error info instead of raising
1102
+ return {'error': str(e), 'type': type(e).__name__}
1103
+
573
1104
  def builtin_isinstance(self, value: Any, type_name: str) -> bool:
574
1105
  """Check if value is of type"""
575
1106
  type_map = {
@@ -695,59 +1226,66 @@ class CSSLBuiltins:
695
1226
  return lst
696
1227
 
697
1228
  # v4.5.1: For CSSL typed containers, modify in place
698
- from .cssl_types import Stack, Vector, Array, DataStruct
699
- if isinstance(lst, (Stack, Vector, Array, DataStruct)):
1229
+ # v4.8.6: Added List to in-place modification
1230
+ # v4.8.6: Also modify plain Python lists in-place (fixes nested dict/array push)
1231
+ from .cssl_types import Stack, Vector, Array, DataStruct, List
1232
+ if isinstance(lst, (Stack, Vector, Array, DataStruct, List, list)):
700
1233
  for item in items:
701
1234
  lst.append(item)
702
1235
  return lst
703
1236
 
704
- # For regular lists, create a copy (immutable behavior)
705
- lst = list(lst)
706
- lst.extend(items)
707
- return lst
1237
+ # For other iterables (tuples, etc.), create a new list
1238
+ new_lst = list(lst)
1239
+ new_lst.extend(items)
1240
+ return new_lst
708
1241
 
709
1242
  def builtin_pop(self, lst: list, index: int = -1) -> Any:
710
1243
  """Pop item from list/stack/vector.
711
1244
 
712
1245
  v4.5.1: For Stack/Vector/Array types, modifies in place.
1246
+ v4.8.6: Also modifies plain Python lists in place.
713
1247
  """
714
1248
  if lst is None:
715
1249
  return None
716
1250
 
717
1251
  # v4.5.1: For CSSL typed containers, modify in place
718
- from .cssl_types import Stack, Vector, Array, DataStruct
719
- if isinstance(lst, (Stack, Vector, Array, DataStruct)):
1252
+ # v4.8.6: Also modify plain Python lists in-place
1253
+ from .cssl_types import Stack, Vector, Array, DataStruct, List
1254
+ if isinstance(lst, (Stack, Vector, Array, DataStruct, List, list)):
720
1255
  if len(lst) == 0:
721
1256
  return None
722
1257
  return lst.pop(index)
723
1258
 
724
- # For regular lists, create a copy
725
- lst = list(lst)
726
- return lst.pop(index) if lst else None
1259
+ # For other iterables, create a copy
1260
+ new_lst = list(lst)
1261
+ return new_lst.pop(index) if new_lst else None
727
1262
 
728
1263
  def builtin_shift(self, lst: list) -> Any:
729
1264
  """Remove and return first element.
730
1265
 
731
1266
  v4.5.1: For Stack/Vector/Array types, modifies in place.
1267
+ v4.8.6: Also modifies plain Python lists in place.
732
1268
  """
733
1269
  if lst is None:
734
1270
  return None
735
1271
 
736
1272
  # v4.5.1: For CSSL typed containers, modify in place
737
- from .cssl_types import Stack, Vector, Array, DataStruct
738
- if isinstance(lst, (Stack, Vector, Array, DataStruct)):
1273
+ # v4.8.6: Also modify plain Python lists in-place
1274
+ from .cssl_types import Stack, Vector, Array, DataStruct, List
1275
+ if isinstance(lst, (Stack, Vector, Array, DataStruct, List, list)):
739
1276
  if len(lst) == 0:
740
1277
  return None
741
1278
  return lst.pop(0)
742
1279
 
743
- # For regular lists, create a copy
744
- lst = list(lst)
745
- return lst.pop(0) if lst else None
1280
+ # For other iterables, create a copy
1281
+ new_lst = list(lst)
1282
+ return new_lst.pop(0) if new_lst else None
746
1283
 
747
1284
  def builtin_unshift(self, lst: list, *items) -> list:
748
1285
  """Add items to the front of a list/stack/vector.
749
1286
 
750
1287
  v4.5.1: For Stack/Vector/Array types, modifies in place.
1288
+ v4.8.6: Also modifies plain Python lists in place.
751
1289
  """
752
1290
  if lst is None:
753
1291
  lst = []
@@ -756,17 +1294,18 @@ class CSSLBuiltins:
756
1294
  return lst
757
1295
 
758
1296
  # v4.5.1: For CSSL typed containers, modify in place
759
- from .cssl_types import Stack, Vector, Array, DataStruct
760
- if isinstance(lst, (Stack, Vector, Array, DataStruct)):
1297
+ # v4.8.6: Also modify plain Python lists in-place
1298
+ from .cssl_types import Stack, Vector, Array, DataStruct, List
1299
+ if isinstance(lst, (Stack, Vector, Array, DataStruct, List, list)):
761
1300
  for item in reversed(items):
762
1301
  lst.insert(0, item)
763
1302
  return lst
764
1303
 
765
- # For regular lists, create a copy
766
- lst = list(lst)
767
- for item in reversed(items):
768
- lst.insert(0, item)
769
- return lst
1304
+ # For other iterables, create a copy
1305
+ new_lst = list(lst)
1306
+ for item in reversed(new_lst):
1307
+ new_lst.insert(0, item)
1308
+ return new_lst
770
1309
 
771
1310
  def builtin_slice(self, value: Union[str, list], start: int, end: int = None) -> Union[str, list]:
772
1311
  if end is None:
@@ -1017,18 +1556,53 @@ class CSSLBuiltins:
1017
1556
 
1018
1557
  # ============= File I/O Functions =============
1019
1558
 
1559
+ # v4.7.1: Path validation helper to prevent directory traversal attacks
1560
+ def _validate_path(self, path: str, allow_write: bool = False) -> str:
1561
+ """Validate file path for security.
1562
+
1563
+ v4.7.1: Prevents directory traversal attacks by checking for '..' sequences
1564
+ and ensuring paths are within reasonable bounds.
1565
+
1566
+ Args:
1567
+ path: The file path to validate
1568
+ allow_write: If True, allows write operations
1569
+
1570
+ Returns:
1571
+ The normalized absolute path
1572
+
1573
+ Raises:
1574
+ CSSLBuiltinError: If path is invalid or attempts traversal
1575
+ """
1576
+ if not isinstance(path, str) or not path:
1577
+ raise CSSLBuiltinError("Invalid path: path must be a non-empty string")
1578
+
1579
+ # Normalize the path
1580
+ normalized = os.path.normpath(path)
1581
+
1582
+ # Check for directory traversal attempts
1583
+ if '..' in normalized:
1584
+ raise CSSLBuiltinError("Directory traversal not allowed in path")
1585
+
1586
+ return normalized
1587
+
1020
1588
  def builtin_read(self, path: str, encoding: str = 'utf-8') -> str:
1021
1589
  """Read entire file content.
1022
1590
  Usage: read('/path/to/file.txt')
1591
+ v4.7.1: Added path validation
1023
1592
  """
1024
- with open(path, 'r', encoding=encoding) as f:
1593
+ validated_path = self._validate_path(path)
1594
+ with open(validated_path, 'r', encoding=encoding) as f:
1025
1595
  return f.read()
1026
1596
 
1027
1597
  def builtin_readline(self, line: int, path: str, encoding: str = 'utf-8') -> str:
1028
1598
  """Read specific line from file (1-indexed).
1029
1599
  Usage: readline(5, '/path/to/file.txt') -> returns line 5
1600
+ v4.7.1: Added path validation and line number validation
1030
1601
  """
1031
- with open(path, 'r', encoding=encoding) as f:
1602
+ if not isinstance(line, int) or line < 1:
1603
+ raise CSSLBuiltinError("Line number must be a positive integer")
1604
+ validated_path = self._validate_path(path)
1605
+ with open(validated_path, 'r', encoding=encoding) as f:
1032
1606
  for i, file_line in enumerate(f, 1):
1033
1607
  if i == line:
1034
1608
  return file_line.rstrip('\n\r')
@@ -1037,18 +1611,25 @@ class CSSLBuiltins:
1037
1611
  def builtin_write(self, path: str, content: str, encoding: str = 'utf-8') -> int:
1038
1612
  """Write content to file, returns chars written.
1039
1613
  Usage: write('/path/to/file.txt', 'Hello World')
1614
+ v4.7.1: Added path validation
1040
1615
  """
1041
- with open(path, 'w', encoding=encoding) as f:
1042
- return f.write(content)
1616
+ validated_path = self._validate_path(path, allow_write=True)
1617
+ with open(validated_path, 'w', encoding=encoding) as f:
1618
+ return f.write(str(content) if content is not None else '')
1043
1619
 
1044
1620
  def builtin_writeline(self, line: int, content: str, path: str, encoding: str = 'utf-8') -> bool:
1045
1621
  """Write/replace specific line in file (1-indexed).
1046
1622
  Usage: writeline(5, 'New content', '/path/to/file.txt')
1623
+ v4.7.1: Added path and line number validation
1047
1624
  """
1625
+ if not isinstance(line, int) or line < 1:
1626
+ raise CSSLBuiltinError("Line number must be a positive integer")
1627
+ validated_path = self._validate_path(path, allow_write=True)
1628
+
1048
1629
  # Read all lines
1049
1630
  lines = []
1050
- if os.path.exists(path):
1051
- with open(path, 'r', encoding=encoding) as f:
1631
+ if os.path.exists(validated_path):
1632
+ with open(validated_path, 'r', encoding=encoding) as f:
1052
1633
  lines = f.readlines()
1053
1634
 
1054
1635
  # Ensure we have enough lines
@@ -1056,67 +1637,81 @@ class CSSLBuiltins:
1056
1637
  lines.append('\n')
1057
1638
 
1058
1639
  # Replace the specific line (1-indexed)
1059
- if not content.endswith('\n'):
1060
- content = content + '\n'
1061
- lines[line - 1] = content
1640
+ content_str = str(content) if content is not None else ''
1641
+ if not content_str.endswith('\n'):
1642
+ content_str = content_str + '\n'
1643
+ lines[line - 1] = content_str
1062
1644
 
1063
1645
  # Write back
1064
- with open(path, 'w', encoding=encoding) as f:
1646
+ with open(validated_path, 'w', encoding=encoding) as f:
1065
1647
  f.writelines(lines)
1066
1648
  return True
1067
1649
 
1068
1650
  def builtin_readfile(self, path: str, encoding: str = 'utf-8') -> str:
1069
- """Read entire file content"""
1070
- with open(path, 'r', encoding=encoding) as f:
1651
+ """Read entire file content. v4.7.1: Added path validation"""
1652
+ validated_path = self._validate_path(path)
1653
+ with open(validated_path, 'r', encoding=encoding) as f:
1071
1654
  return f.read()
1072
1655
 
1073
1656
  def builtin_writefile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
1074
- """Write content to file, returns bytes written"""
1075
- with open(path, 'w', encoding=encoding) as f:
1076
- return f.write(content)
1657
+ """Write content to file, returns bytes written. v4.7.1: Added path validation"""
1658
+ validated_path = self._validate_path(path, allow_write=True)
1659
+ with open(validated_path, 'w', encoding=encoding) as f:
1660
+ return f.write(str(content) if content is not None else '')
1077
1661
 
1078
1662
  def builtin_appendfile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
1079
- """Append content to file, returns bytes written"""
1080
- with open(path, 'a', encoding=encoding) as f:
1081
- return f.write(content)
1663
+ """Append content to file, returns bytes written. v4.7.1: Added path validation"""
1664
+ validated_path = self._validate_path(path, allow_write=True)
1665
+ with open(validated_path, 'a', encoding=encoding) as f:
1666
+ return f.write(str(content) if content is not None else '')
1082
1667
 
1083
1668
  def builtin_readlines(self, path: str, encoding: str = 'utf-8') -> list:
1084
- """Read file lines into list"""
1085
- with open(path, 'r', encoding=encoding) as f:
1669
+ """Read file lines into list. v4.7.1: Added path validation"""
1670
+ validated_path = self._validate_path(path)
1671
+ with open(validated_path, 'r', encoding=encoding) as f:
1086
1672
  return f.readlines()
1087
1673
 
1088
1674
  def builtin_listdir(self, path: str = '.') -> list:
1089
- """List directory contents"""
1090
- return os.listdir(path)
1675
+ """List directory contents. v4.7.1: Added path validation"""
1676
+ validated_path = self._validate_path(path) if path != '.' else '.'
1677
+ return os.listdir(validated_path)
1091
1678
 
1092
1679
  def builtin_makedirs(self, path: str, exist_ok: bool = True) -> bool:
1093
- """Create directories recursively"""
1094
- os.makedirs(path, exist_ok=exist_ok)
1680
+ """Create directories recursively. v4.7.1: Added path validation"""
1681
+ validated_path = self._validate_path(path, allow_write=True)
1682
+ os.makedirs(validated_path, exist_ok=exist_ok)
1095
1683
  return True
1096
1684
 
1097
1685
  def builtin_removefile(self, path: str) -> bool:
1098
- """Remove a file"""
1099
- os.remove(path)
1686
+ """Remove a file. v4.7.1: Added path validation"""
1687
+ validated_path = self._validate_path(path, allow_write=True)
1688
+ os.remove(validated_path)
1100
1689
  return True
1101
1690
 
1102
1691
  def builtin_removedir(self, path: str) -> bool:
1103
- """Remove an empty directory"""
1104
- os.rmdir(path)
1692
+ """Remove an empty directory. v4.7.1: Added path validation"""
1693
+ validated_path = self._validate_path(path, allow_write=True)
1694
+ os.rmdir(validated_path)
1105
1695
  return True
1106
1696
 
1107
1697
  def builtin_copyfile(self, src: str, dst: str) -> str:
1108
- """Copy a file, returns destination path"""
1698
+ """Copy a file, returns destination path. v4.7.1: Added path validation"""
1109
1699
  import shutil
1110
- return shutil.copy2(src, dst)
1700
+ validated_src = self._validate_path(src)
1701
+ validated_dst = self._validate_path(dst, allow_write=True)
1702
+ return shutil.copy2(validated_src, validated_dst)
1111
1703
 
1112
1704
  def builtin_movefile(self, src: str, dst: str) -> str:
1113
- """Move a file, returns destination path"""
1705
+ """Move a file, returns destination path. v4.7.1: Added path validation"""
1114
1706
  import shutil
1115
- return shutil.move(src, dst)
1707
+ validated_src = self._validate_path(src, allow_write=True)
1708
+ validated_dst = self._validate_path(dst, allow_write=True)
1709
+ return shutil.move(validated_src, validated_dst)
1116
1710
 
1117
1711
  def builtin_filesize(self, path: str) -> int:
1118
- """Get file size in bytes"""
1119
- return os.path.getsize(path)
1712
+ """Get file size in bytes. v4.7.1: Added path validation"""
1713
+ validated_path = self._validate_path(path)
1714
+ return os.path.getsize(validated_path)
1120
1715
 
1121
1716
  # ============= JSON Functions =============
1122
1717
 
@@ -1387,6 +1982,88 @@ class CSSLBuiltins:
1387
1982
  # Handle Python objects
1388
1983
  return type(obj).__name__
1389
1984
 
1985
+ def builtin_instance_delete(self, instance: Any, destructor_name: str = None) -> bool:
1986
+ """v4.8.8: Call destructors on an instance and mark it for cleanup.
1987
+
1988
+ Usage:
1989
+ delete(myInstance); // Calls all destructors
1990
+ delete(myInstance, "Init"); // Calls only ~Init destructor
1991
+ instance::delete(obj, "Setup"); // Calls only ~Setup destructor
1992
+
1993
+ Args:
1994
+ instance: The CSSLInstance to delete
1995
+ destructor_name: Optional - specific destructor name (without ~)
1996
+
1997
+ Returns:
1998
+ True if destructors were called, False if instance has no destructors
1999
+ """
2000
+ from .cssl_types import CSSLInstance
2001
+
2002
+ if not isinstance(instance, CSSLInstance):
2003
+ return False
2004
+
2005
+ class_def = instance._class
2006
+ destructors = getattr(class_def, 'destructors', [])
2007
+
2008
+ if not destructors:
2009
+ return False
2010
+
2011
+ # Call destructors through the runtime
2012
+ if self.runtime:
2013
+ for destr in destructors:
2014
+ destr_name = destr.value.get('name', '')
2015
+ # If specific destructor requested, only call that one
2016
+ if destructor_name:
2017
+ # Match ~Name or just Name
2018
+ if destr_name != f'~{destructor_name}' and destr_name != destructor_name:
2019
+ continue
2020
+
2021
+ # Call the destructor
2022
+ self.runtime._call_destructor(instance, destr)
2023
+
2024
+ # Mark instance as deleted
2025
+ instance._deleted = True
2026
+ return True
2027
+
2028
+ return False
2029
+
2030
+ def builtin_call_constructor(self, instance: Any, constructor_name: str, *args, **kwargs) -> bool:
2031
+ """v4.8.8: Manually call a callable constructor on an instance.
2032
+
2033
+ Usage:
2034
+ call_constructor(myInstance, "Setup"); // Calls callable constr Setup()
2035
+ call_constructor(myInstance, "Init", arg1, arg2); // With arguments
2036
+
2037
+ Args:
2038
+ instance: The CSSLInstance to call constructor on
2039
+ constructor_name: Name of the callable constructor
2040
+ *args: Arguments to pass to the constructor
2041
+
2042
+ Returns:
2043
+ True if constructor was called, False if not found
2044
+ """
2045
+ from .cssl_types import CSSLInstance
2046
+
2047
+ if not isinstance(instance, CSSLInstance):
2048
+ return False
2049
+
2050
+ # Get callable constructors stored on instance
2051
+ callable_constructors = getattr(instance, '_callable_constructors', [])
2052
+
2053
+ if not callable_constructors:
2054
+ return False
2055
+
2056
+ # Call constructor through the runtime
2057
+ if self.runtime:
2058
+ for constr in callable_constructors:
2059
+ constr_name = constr.value.get('name', '')
2060
+ if constr_name == constructor_name:
2061
+ # Call the callable constructor
2062
+ self.runtime._call_constructor(instance, constr, list(args), dict(kwargs), {})
2063
+ return True
2064
+
2065
+ return False
2066
+
1390
2067
  def builtin_isavailable(self, name_or_obj: Any) -> bool:
1391
2068
  """Check if a shared instance exists.
1392
2069
  Usage:
@@ -1529,6 +2206,56 @@ class CSSLBuiltins:
1529
2206
  """Set environment variable"""
1530
2207
  os.environ[name] = value
1531
2208
 
2209
+ # v4.8.5: os module replacement builtins
2210
+ def builtin_getcwd(self) -> str:
2211
+ """Get current working directory (replaces os.getcwd())"""
2212
+ return os.getcwd()
2213
+
2214
+ def builtin_chdir(self, path: str) -> bool:
2215
+ """Change current working directory (replaces os.chdir())"""
2216
+ os.chdir(path)
2217
+ return True
2218
+
2219
+ def builtin_mkdir(self, path: str) -> bool:
2220
+ """Create single directory (replaces os.mkdir())"""
2221
+ os.mkdir(path)
2222
+ return True
2223
+
2224
+ def builtin_rmdir(self, path: str) -> bool:
2225
+ """Remove empty directory (alias for removedir, replaces os.rmdir())"""
2226
+ os.rmdir(path)
2227
+ return True
2228
+
2229
+ def builtin_rmfile(self, path: str) -> bool:
2230
+ """Remove file (alias for removefile, replaces os.remove())"""
2231
+ os.remove(path)
2232
+ return True
2233
+
2234
+ def builtin_rename(self, src: str, dst: str) -> bool:
2235
+ """Rename file or directory (replaces os.rename())"""
2236
+ os.rename(src, dst)
2237
+ return True
2238
+
2239
+ def builtin_argv(self) -> list:
2240
+ """Get command line arguments (replaces sys.argv)"""
2241
+ import sys
2242
+ return sys.argv
2243
+
2244
+ def builtin_argc(self) -> int:
2245
+ """Get argument count (replaces len(sys.argv))"""
2246
+ import sys
2247
+ return len(sys.argv)
2248
+
2249
+ def builtin_platform(self) -> str:
2250
+ """Get platform name (replaces sys.platform)"""
2251
+ import sys
2252
+ return sys.platform
2253
+
2254
+ def builtin_version(self) -> str:
2255
+ """Get Python version (replaces sys.version)"""
2256
+ import sys
2257
+ return sys.version
2258
+
1532
2259
  def builtin_input(self, prompt: str = '') -> str:
1533
2260
  """Read user input"""
1534
2261
  return input(prompt)
@@ -1536,9 +2263,11 @@ class CSSLBuiltins:
1536
2263
  def builtin_clear(self) -> None:
1537
2264
  """Clear console screen"""
1538
2265
  if os.name == 'nt':
1539
- os.system('cls')
2266
+ # Use ANSI escape codes on Windows 10+ (modern terminals support it)
2267
+ # Fall back to subprocess for older Windows
2268
+ print('\033[2J\033[H', end='', flush=True)
1540
2269
  else:
1541
- print('\033[2J\033[H', end='')
2270
+ print('\033[2J\033[H', end='', flush=True)
1542
2271
 
1543
2272
  def builtin_color(self, text: str, color: str) -> str:
1544
2273
  """Apply ANSI color to text"""
@@ -1558,94 +2287,828 @@ class CSSLBuiltins:
1558
2287
  """Delay execution by milliseconds"""
1559
2288
  time.sleep(ms / 1000.0)
1560
2289
 
1561
- def builtin_pyimport(self, module_name: str) -> Any:
1562
- """
1563
- Import a Python module for use in CSSL.
2290
+ # =========================================================================
2291
+ # v4.6.5: Individual Color Functions for F-Strings
2292
+ # Usage: string name = f"{red("H")}{green("E")}{yellow("Y")}"
2293
+ # Accepts any type (string, int, float, etc.) - auto-converts to string
2294
+ # =========================================================================
1564
2295
 
1565
- v4.1.1: Fixed to use importlib.import_module for proper nested module support.
2296
+ def _color_str(self, value) -> str:
2297
+ """Convert any value to string for color functions."""
2298
+ if value is None:
2299
+ return "null"
2300
+ return str(value)
1566
2301
 
1567
- Usage:
1568
- module = pyimport("plugins.app")
1569
- result = module.my_function()
2302
+ # --- Named Colors (Foreground) ---
2303
+ def builtin_red(self, text) -> str:
2304
+ """Apply red color: red("hello") or red(123)"""
2305
+ return f'\033[31m{self._color_str(text)}\033[0m'
1570
2306
 
1571
- os = pyimport("os")
1572
- path = os.path.join("a", "b")
1573
- """
1574
- import importlib
1575
- return importlib.import_module(module_name)
2307
+ def builtin_green(self, text) -> str:
2308
+ """Apply green color to text"""
2309
+ return f'\033[32m{self._color_str(text)}\033[0m'
1576
2310
 
1577
- # ============= Extended String Functions =============
2311
+ def builtin_blue(self, text) -> str:
2312
+ """Apply blue color to text"""
2313
+ return f'\033[34m{self._color_str(text)}\033[0m'
1578
2314
 
1579
- def builtin_sprintf(self, fmt: str, *args) -> str:
1580
- """C-style format string"""
1581
- return fmt % args
2315
+ def builtin_yellow(self, text) -> str:
2316
+ """Apply yellow color to text"""
2317
+ return f'\033[33m{self._color_str(text)}\033[0m'
1582
2318
 
1583
- def builtin_chars(self, s: str) -> list:
1584
- """Convert string to list of characters"""
1585
- return list(s)
2319
+ def builtin_cyan(self, text) -> str:
2320
+ """Apply cyan color to text"""
2321
+ return f'\033[36m{self._color_str(text)}\033[0m'
1586
2322
 
1587
- def builtin_ord(self, c: str) -> int:
1588
- """Get ASCII/Unicode code of character"""
1589
- return ord(c[0] if c else '\0')
2323
+ def builtin_magenta(self, text) -> str:
2324
+ """Apply magenta color to text"""
2325
+ return f'\033[35m{self._color_str(text)}\033[0m'
1590
2326
 
1591
- def builtin_chr(self, n: int) -> str:
1592
- """Convert ASCII/Unicode code to character"""
1593
- return chr(n)
2327
+ def builtin_white(self, text) -> str:
2328
+ """Apply white color to text"""
2329
+ return f'\033[37m{self._color_str(text)}\033[0m'
1594
2330
 
1595
- def builtin_capitalize(self, s: str) -> str:
1596
- return str(s).capitalize()
2331
+ def builtin_black(self, text) -> str:
2332
+ """Apply black color to text"""
2333
+ return f'\033[30m{self._color_str(text)}\033[0m'
1597
2334
 
1598
- def builtin_title(self, s: str) -> str:
1599
- return str(s).title()
2335
+ # --- Bright Color Variants ---
2336
+ def builtin_bright_red(self, text) -> str:
2337
+ """Apply bright red color to text"""
2338
+ return f'\033[91m{self._color_str(text)}\033[0m'
1600
2339
 
1601
- def builtin_swapcase(self, s: str) -> str:
1602
- return str(s).swapcase()
2340
+ def builtin_bright_green(self, text) -> str:
2341
+ """Apply bright green color to text"""
2342
+ return f'\033[92m{self._color_str(text)}\033[0m'
1603
2343
 
1604
- def builtin_center(self, s: str, width: int, fillchar: str = ' ') -> str:
1605
- return str(s).center(width, fillchar)
2344
+ def builtin_bright_blue(self, text) -> str:
2345
+ """Apply bright blue color to text"""
2346
+ return f'\033[94m{self._color_str(text)}\033[0m'
1606
2347
 
1607
- def builtin_zfill(self, s: str, width: int) -> str:
1608
- return str(s).zfill(width)
2348
+ def builtin_bright_yellow(self, text) -> str:
2349
+ """Apply bright yellow color to text"""
2350
+ return f'\033[93m{self._color_str(text)}\033[0m'
1609
2351
 
1610
- def builtin_isalpha(self, s: str) -> bool:
1611
- return str(s).isalpha()
2352
+ def builtin_bright_cyan(self, text) -> str:
2353
+ """Apply bright cyan color to text"""
2354
+ return f'\033[96m{self._color_str(text)}\033[0m'
1612
2355
 
1613
- def builtin_isdigit(self, s: str) -> bool:
1614
- return str(s).isdigit()
2356
+ def builtin_bright_magenta(self, text) -> str:
2357
+ """Apply bright magenta color to text"""
2358
+ return f'\033[95m{self._color_str(text)}\033[0m'
1615
2359
 
1616
- def builtin_isalnum(self, s: str) -> bool:
1617
- return str(s).isalnum()
2360
+ def builtin_bright_white(self, text) -> str:
2361
+ """Apply bright white color to text"""
2362
+ return f'\033[97m{self._color_str(text)}\033[0m'
1618
2363
 
1619
- def builtin_isspace(self, s: str) -> bool:
1620
- return str(s).isspace()
2364
+ # --- RGB Custom Colors (24-bit True Color) ---
2365
+ def builtin_rgb(self, text, r: int, g: int, b: int) -> str:
2366
+ """Apply RGB color to text using 24-bit true color.
1621
2367
 
1622
- # ============= Extended List Functions =============
2368
+ Usage: rgb("hello", 255, 128, 0) -> orange text
2369
+ """
2370
+ r = max(0, min(255, int(r)))
2371
+ g = max(0, min(255, int(g)))
2372
+ b = max(0, min(255, int(b)))
2373
+ return f'\033[38;2;{r};{g};{b}m{self._color_str(text)}\033[0m'
1623
2374
 
1624
- def builtin_enumerate(self, lst: list, start: int = 0) -> list:
1625
- """Return list of (index, value) pairs"""
1626
- return list(enumerate(lst, start))
2375
+ # --- Background Colors ---
2376
+ def builtin_bg_red(self, text) -> str:
2377
+ """Apply red background to text"""
2378
+ return f'\033[41m{self._color_str(text)}\033[0m'
1627
2379
 
1628
- def builtin_zip(self, *lists) -> list:
1629
- """Zip multiple lists together"""
1630
- return list(zip(*lists))
2380
+ def builtin_bg_green(self, text) -> str:
2381
+ """Apply green background to text"""
2382
+ return f'\033[42m{self._color_str(text)}\033[0m'
1631
2383
 
1632
- def builtin_reversed(self, lst: list) -> list:
1633
- """Return reversed list"""
1634
- return list(reversed(lst))
2384
+ def builtin_bg_blue(self, text) -> str:
2385
+ """Apply blue background to text"""
2386
+ return f'\033[44m{self._color_str(text)}\033[0m'
1635
2387
 
1636
- def builtin_sorted(self, lst: list, key: str = None, reverse: bool = False) -> list:
1637
- """Return sorted list"""
1638
- if key:
1639
- return sorted(lst, key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=reverse)
1640
- return sorted(lst, reverse=reverse)
2388
+ def builtin_bg_yellow(self, text) -> str:
2389
+ """Apply yellow background to text"""
2390
+ return f'\033[43m{self._color_str(text)}\033[0m'
1641
2391
 
1642
- def builtin_count(self, collection: Union[list, str], item: Any) -> int:
1643
- """Count occurrences of item"""
1644
- return collection.count(item)
2392
+ def builtin_bg_cyan(self, text) -> str:
2393
+ """Apply cyan background to text"""
2394
+ return f'\033[46m{self._color_str(text)}\033[0m'
1645
2395
 
1646
- def builtin_first(self, lst: list, default: Any = None) -> Any:
1647
- """Get first element or default"""
1648
- return lst[0] if lst else default
2396
+ def builtin_bg_magenta(self, text) -> str:
2397
+ """Apply magenta background to text"""
2398
+ return f'\033[45m{self._color_str(text)}\033[0m'
2399
+
2400
+ def builtin_bg_white(self, text) -> str:
2401
+ """Apply white background to text"""
2402
+ return f'\033[47m{self._color_str(text)}\033[0m'
2403
+
2404
+ def builtin_bg_black(self, text) -> str:
2405
+ """Apply black background to text"""
2406
+ return f'\033[40m{self._color_str(text)}\033[0m'
2407
+
2408
+ def builtin_bg_rgb(self, text, r: int, g: int, b: int) -> str:
2409
+ """Apply RGB background color using 24-bit true color.
2410
+
2411
+ Usage: bg_rgb("hello", 255, 128, 0) -> orange background
2412
+ """
2413
+ r = max(0, min(255, int(r)))
2414
+ g = max(0, min(255, int(g)))
2415
+ b = max(0, min(255, int(b)))
2416
+ return f'\033[48;2;{r};{g};{b}m{self._color_str(text)}\033[0m'
2417
+
2418
+ # --- Text Style Functions ---
2419
+ def builtin_bold(self, text) -> str:
2420
+ """Apply bold style to text"""
2421
+ return f'\033[1m{self._color_str(text)}\033[0m'
2422
+
2423
+ def builtin_italic(self, text) -> str:
2424
+ """Apply italic/cursive style to text"""
2425
+ return f'\033[3m{self._color_str(text)}\033[0m'
2426
+
2427
+ def builtin_underline(self, text) -> str:
2428
+ """Apply underline style to text"""
2429
+ return f'\033[4m{self._color_str(text)}\033[0m'
2430
+
2431
+ def builtin_dim(self, text) -> str:
2432
+ """Apply dim/faint style to text"""
2433
+ return f'\033[2m{self._color_str(text)}\033[0m'
2434
+
2435
+ def builtin_blink(self, text) -> str:
2436
+ """Apply blinking style to text (not supported in all terminals)"""
2437
+ return f'\033[5m{self._color_str(text)}\033[0m'
2438
+
2439
+ def builtin_reverse_style(self, text) -> str:
2440
+ """Reverse foreground and background colors"""
2441
+ return f'\033[7m{self._color_str(text)}\033[0m'
2442
+
2443
+ def builtin_strikethrough(self, text) -> str:
2444
+ """Apply strikethrough style to text"""
2445
+ return f'\033[9m{self._color_str(text)}\033[0m'
2446
+
2447
+ def builtin_reset(self, text="") -> str:
2448
+ """Reset all styles. Can wrap text or return just the reset code.
2449
+
2450
+ Usage:
2451
+ reset("text") -> returns text without any styles
2452
+ reset() -> returns the ANSI reset code
2453
+ """
2454
+ if text:
2455
+ return f'\033[0m{self._color_str(text)}\033[0m'
2456
+ return '\033[0m'
2457
+
2458
+ # v4.8.5: Blocked modules for pyimport (os/sys replaced by CSSL builtins)
2459
+ # v4.8.8: Added importlib, ctypes, builtins to prevent security bypasses
2460
+ BLOCKED_MODULES = {'os', 'sys', 'subprocess', 'shutil', 'importlib', 'ctypes', 'builtins'}
2461
+
2462
+ def builtin_pyimport(self, module_name: str) -> Any:
2463
+ """
2464
+ Import a Python module for use in CSSL.
2465
+
2466
+ v4.8.5: All modules allowed EXCEPT os, sys, subprocess, shutil.
2467
+ Use CSSL builtins for filesystem/system operations instead:
2468
+ - env(), setenv() for environment variables
2469
+ - getcwd(), chdir() for directory operations
2470
+ - readfile(), writefile() for file I/O
2471
+ - initsh() for shell commands (restricted)
2472
+ - exit() for program termination
2473
+ - argv, argc for command line args
2474
+
2475
+ Usage:
2476
+ @math = pyimport("math");
2477
+ result = math.sqrt(16);
2478
+
2479
+ @requests = pyimport("requests");
2480
+ response = requests.get("https://api.example.com");
2481
+ """
2482
+ import importlib
2483
+ # Check if module is blocked
2484
+ base_module = module_name.split('.')[0]
2485
+ if base_module in self.BLOCKED_MODULES:
2486
+ alternatives = {
2487
+ 'os': 'Use CSSL builtins: env(), setenv(), getcwd(), chdir(), readfile(), writefile(), mkdir(), rmdir(), listdir()',
2488
+ 'sys': 'Use CSSL builtins: exit(), argv, argc, parameter',
2489
+ 'subprocess': 'Use CSSL builtin: initsh() for shell commands',
2490
+ 'shutil': 'Use CSSL builtins: copyfile(), movefile(), rmfile()'
2491
+ }
2492
+ alt_msg = alternatives.get(base_module, 'Use CSSL builtins instead')
2493
+ raise RuntimeError(
2494
+ f"Module '{module_name}' is blocked for security.\n{alt_msg}"
2495
+ )
2496
+ return importlib.import_module(module_name)
2497
+
2498
+ # ============= v4.8.4: C++ Import & I/O Streams =============
2499
+
2500
+ # Global stream instances (singleton pattern for C++ behavior)
2501
+ _cout_instance = None
2502
+ _cin_instance = None
2503
+ _cerr_instance = None
2504
+ _clog_instance = None
2505
+
2506
+ def builtin_cppimport(self, module_name: str) -> Any:
2507
+ """Import a compiled C++ module from IncludeCPP.
2508
+
2509
+ Supports importing IncludeCPP modules directly into CSSL.
2510
+ Uses the C++ optimized module if available.
2511
+
2512
+ Usage:
2513
+ @mathlib = cppimport("mathlib");
2514
+ result = mathlib.add(5, 3);
2515
+
2516
+ // Or with full path:
2517
+ @mod = cppimport("includecpp.mathlib");
2518
+ """
2519
+ try:
2520
+ # Check if it's an includecpp module request
2521
+ if module_name.startswith('includecpp.'):
2522
+ actual_name = module_name[11:] # Remove 'includecpp.' prefix
2523
+ else:
2524
+ actual_name = module_name
2525
+
2526
+ # Try to import from includecpp
2527
+ try:
2528
+ import includecpp
2529
+ if hasattr(includecpp, actual_name):
2530
+ return getattr(includecpp, actual_name)
2531
+
2532
+ # Try using CppApi directly
2533
+ api = includecpp.CppApi()
2534
+ if actual_name in api.registry:
2535
+ return api.include(actual_name)
2536
+ except Exception:
2537
+ pass
2538
+
2539
+ # Fallback: try as a regular Python module (for .pyd files)
2540
+ import importlib
2541
+ return importlib.import_module(module_name)
2542
+
2543
+ except Exception as e:
2544
+ raise RuntimeError(f"Failed to import C++ module '{module_name}': {e}")
2545
+
2546
+ def builtin_include(self, module_path: str) -> Any:
2547
+ """Include a module (IncludeCPP or file).
2548
+
2549
+ Usage:
2550
+ @mod = include("includecpp.mathlib"); // C++ module
2551
+ @header = include("./myheader.h"); // Header file
2552
+ """
2553
+ if module_path.startswith('includecpp.'):
2554
+ return self.builtin_cppimport(module_path)
2555
+ elif module_path.endswith(('.h', '.hpp', '.cpp', '.c')):
2556
+ # Include as source file (read content)
2557
+ return self.builtin_readfile(module_path)
2558
+ else:
2559
+ return self.builtin_cppimport(module_path)
2560
+
2561
+ def builtin_includecpp(self, cpp_proj_path: str, module_name: str) -> Any:
2562
+ """Import a pre-built C++ module from a cpp.proj project.
2563
+
2564
+ This is the CSSL equivalent of Python's:
2565
+ from includecpp import module_name
2566
+
2567
+ IMPORTANT: The module must be built first using `includecpp rebuild`.
2568
+ This function only imports already-built modules.
2569
+
2570
+ How it works:
2571
+ 1. Reads cpp.proj to find BaseDir (where built modules are stored)
2572
+ 2. Adds BaseDir/bindings to Python path
2573
+ 3. Imports the module (api_modulename.pyd)
2574
+
2575
+ Usage:
2576
+ // Import a pre-built C++ module
2577
+ @math = includecpp("C:/projects/mylib/cpp.proj", "fastmath");
2578
+ result = math.calculate(42);
2579
+
2580
+ // Relative path works too
2581
+ @crypto = includecpp("./crypto/cpp.proj", "crypto");
2582
+
2583
+ Args:
2584
+ cpp_proj_path: Path to the cpp.proj file (or directory containing it)
2585
+ module_name: Name of the module to import
2586
+
2587
+ Returns:
2588
+ The imported C++ module wrapper
2589
+
2590
+ Raises:
2591
+ RuntimeError: If cpp.proj not found, module not built, or import fails
2592
+ """
2593
+ import os
2594
+ import sys
2595
+ import json
2596
+ import platform
2597
+ from pathlib import Path
2598
+
2599
+ try:
2600
+ # Resolve the cpp.proj path
2601
+ proj_path = Path(cpp_proj_path)
2602
+ if not proj_path.is_absolute():
2603
+ proj_path = Path(os.getcwd()) / proj_path
2604
+
2605
+ # Handle both file and directory paths
2606
+ if proj_path.is_dir():
2607
+ proj_path = proj_path / "cpp.proj"
2608
+ elif proj_path.name != "cpp.proj":
2609
+ proj_path = proj_path / "cpp.proj"
2610
+
2611
+ if not proj_path.exists():
2612
+ raise RuntimeError(f"cpp.proj not found: {proj_path}")
2613
+
2614
+ # Load cpp.proj to get BaseDir
2615
+ with open(proj_path, 'r', encoding='utf-8') as f:
2616
+ config = json.load(f)
2617
+
2618
+ # Get BaseDir from config
2619
+ if 'BaseDir' not in config:
2620
+ project_name = config.get('project', 'unnamed')
2621
+ if platform.system() == "Windows":
2622
+ appdata = Path(os.getenv('APPDATA', Path.home() / 'AppData' / 'Roaming'))
2623
+ else:
2624
+ appdata = Path.home() / ".local" / "share" / "includecpp"
2625
+ base_dir = appdata / f"{project_name}-gcc-build-proj"
2626
+ else:
2627
+ base_dir = Path(config['BaseDir'])
2628
+
2629
+ bindings_dir = base_dir / "bindings"
2630
+ registry_file = base_dir / ".module_registry.json"
2631
+
2632
+ # Check if bindings directory exists
2633
+ if not bindings_dir.exists():
2634
+ raise RuntimeError(
2635
+ f"Bindings directory not found: {bindings_dir}\n"
2636
+ f"Run 'includecpp rebuild' in the project directory first."
2637
+ )
2638
+
2639
+ # Check registry for module
2640
+ if registry_file.exists():
2641
+ with open(registry_file, 'r', encoding='utf-8') as f:
2642
+ registry_data = json.load(f)
2643
+ # Handle both v1.6 and v2.0 registry formats
2644
+ if "schema_version" in registry_data and registry_data.get("schema_version") == "2.0":
2645
+ registry = registry_data.get("modules", {})
2646
+ else:
2647
+ registry = registry_data
2648
+
2649
+ if module_name not in registry:
2650
+ available = list(registry.keys())
2651
+ raise RuntimeError(
2652
+ f"Module '{module_name}' not found in registry.\n"
2653
+ f"Available modules: {available}\n"
2654
+ f"Run 'includecpp rebuild' to build the module."
2655
+ )
2656
+
2657
+ # Add bindings directory to path if not already there
2658
+ bindings_str = str(bindings_dir)
2659
+ if bindings_str not in sys.path:
2660
+ sys.path.insert(0, bindings_str)
2661
+
2662
+ # Try to import per-module .pyd (v2.0 format)
2663
+ pyd_name = f"api_{module_name}"
2664
+ pyd_suffix = ".pyd" if platform.system() == "Windows" else ".so"
2665
+ pyd_path = bindings_dir / f"{pyd_name}{pyd_suffix}"
2666
+
2667
+ if pyd_path.exists():
2668
+ # Import the per-module .pyd
2669
+ import importlib
2670
+ if pyd_name in sys.modules:
2671
+ # Reload if already imported
2672
+ module = importlib.reload(sys.modules[pyd_name])
2673
+ else:
2674
+ module = importlib.import_module(pyd_name)
2675
+ return module
2676
+
2677
+ # v4.9.3: Try direct import of shared api.pyd before subprocess fallback
2678
+ # This allows C++ classes to work properly (subprocess can't pickle them)
2679
+ api_pyd = bindings_dir / f"api{pyd_suffix}"
2680
+ if api_pyd.exists():
2681
+ import importlib
2682
+ # Add DLL directory on Windows for dependency loading
2683
+ if hasattr(os, 'add_dll_directory'):
2684
+ os.add_dll_directory(bindings_str)
2685
+
2686
+ # Check if we can import api without conflict
2687
+ if 'api' not in sys.modules:
2688
+ try:
2689
+ api_mod = importlib.import_module('api')
2690
+ if hasattr(api_mod, module_name):
2691
+ return getattr(api_mod, module_name)
2692
+ except Exception:
2693
+ pass
2694
+ else:
2695
+ # api already loaded - check if it has our module
2696
+ api_mod = sys.modules['api']
2697
+ if hasattr(api_mod, module_name):
2698
+ return getattr(api_mod, module_name)
2699
+
2700
+ # Create a proxy module that executes calls in a subprocess
2701
+ # This avoids the Python extension module caching issue where
2702
+ # cssl's bundled api.pyd shadows the user's api.pyd
2703
+ # Note: Classes won't fully work in subprocess mode due to pickle limitations
2704
+ return _IncludeCppModuleProxy(bindings_str, module_name)
2705
+
2706
+ except RuntimeError:
2707
+ raise
2708
+ except json.JSONDecodeError as e:
2709
+ raise RuntimeError(f"Invalid cpp.proj file: {e}")
2710
+ except Exception as e:
2711
+ raise RuntimeError(f"includecpp() failed: {e}")
2712
+
2713
+
2714
+ def builtin_cout(self) -> 'OutputStream':
2715
+ """Get stdout stream (C++ cout equivalent).
2716
+
2717
+ C++ optimized output stream with << operator support.
2718
+
2719
+ Usage:
2720
+ cout() << "Hello" << " World" << endl();
2721
+ """
2722
+ from .cssl_types import OutputStream
2723
+ if CSSLBuiltins._cout_instance is None:
2724
+ CSSLBuiltins._cout_instance = OutputStream('stdout')
2725
+ return CSSLBuiltins._cout_instance
2726
+
2727
+ def builtin_cin(self) -> 'InputStream':
2728
+ """Get stdin stream (C++ cin equivalent).
2729
+
2730
+ C++ optimized input stream with >> operator support.
2731
+
2732
+ Usage:
2733
+ @name = cin().read(str);
2734
+ @age = cin().read(int);
2735
+ """
2736
+ from .cssl_types import InputStream
2737
+ if CSSLBuiltins._cin_instance is None:
2738
+ CSSLBuiltins._cin_instance = InputStream('stdin')
2739
+ return CSSLBuiltins._cin_instance
2740
+
2741
+ def builtin_cerr(self) -> 'OutputStream':
2742
+ """Get stderr stream (C++ cerr equivalent).
2743
+
2744
+ Auto-flushing error output stream.
2745
+
2746
+ Usage:
2747
+ cerr() << "Error: " << errMsg << endl();
2748
+ """
2749
+ from .cssl_types import OutputStream
2750
+ if CSSLBuiltins._cerr_instance is None:
2751
+ CSSLBuiltins._cerr_instance = OutputStream('stderr')
2752
+ return CSSLBuiltins._cerr_instance
2753
+
2754
+ def builtin_clog(self) -> 'OutputStream':
2755
+ """Get log stream (C++ clog equivalent).
2756
+
2757
+ Buffered logging output stream.
2758
+
2759
+ Usage:
2760
+ clog() << "[INFO] " << message << endl();
2761
+ """
2762
+ from .cssl_types import OutputStream
2763
+ if CSSLBuiltins._clog_instance is None:
2764
+ CSSLBuiltins._clog_instance = OutputStream('clog')
2765
+ return CSSLBuiltins._clog_instance
2766
+
2767
+ def builtin_endl(self) -> str:
2768
+ """End line and flush (C++ endl equivalent).
2769
+
2770
+ Usage:
2771
+ cout() << "Hello" << endl();
2772
+ """
2773
+ return 'endl'
2774
+
2775
+ def builtin_getline(self, stream=None) -> str:
2776
+ """Read entire line from stream (C++ getline equivalent).
2777
+
2778
+ Usage:
2779
+ @line = getline(); // From stdin
2780
+ @line = getline(fileStream); // From file
2781
+ """
2782
+ if stream is None:
2783
+ stream = self.builtin_cin()
2784
+ if hasattr(stream, 'getline'):
2785
+ return stream.getline()
2786
+ # Fallback for regular file objects
2787
+ if hasattr(stream, 'readline'):
2788
+ return stream.readline().rstrip('\n')
2789
+ return str(stream)
2790
+
2791
+ def builtin_fstream(self, filename: str = None, mode: str = 'r+') -> 'FileStream':
2792
+ """Create file stream (C++ fstream equivalent).
2793
+
2794
+ C++ optimized file I/O with << and >> operator support.
2795
+
2796
+ Usage:
2797
+ @file = fstream("data.txt", "r+");
2798
+ file << "Hello" << endl();
2799
+ @data = file.read(str);
2800
+ file.close();
2801
+ """
2802
+ from .cssl_types import FileStream
2803
+ return FileStream(filename, mode)
2804
+
2805
+ def builtin_ifstream(self, filename: str = None) -> 'FileStream':
2806
+ """Create input file stream (C++ ifstream equivalent).
2807
+
2808
+ Usage:
2809
+ @file = ifstream("input.txt");
2810
+ @content = file.readall();
2811
+ """
2812
+ from .cssl_types import FileStream
2813
+ return FileStream(filename, 'r')
2814
+
2815
+ def builtin_ofstream(self, filename: str = None) -> 'FileStream':
2816
+ """Create output file stream (C++ ofstream equivalent).
2817
+
2818
+ Usage:
2819
+ @file = ofstream("output.txt");
2820
+ file << "Data" << endl();
2821
+ """
2822
+ from .cssl_types import FileStream
2823
+ return FileStream(filename, 'w')
2824
+
2825
+ def builtin_setprecision(self, n: int) -> dict:
2826
+ """Set floating point precision (C++ setprecision equivalent).
2827
+
2828
+ Returns a manipulator object for use with streams.
2829
+
2830
+ Usage:
2831
+ cout() << setprecision(4) << 3.14159; // "3.1416"
2832
+ """
2833
+ return {'type': 'manipulator', 'name': 'setprecision', 'value': n}
2834
+
2835
+ def builtin_setw(self, n: int) -> dict:
2836
+ """Set field width (C++ setw equivalent).
2837
+
2838
+ Usage:
2839
+ cout() << setw(10) << value;
2840
+ """
2841
+ return {'type': 'manipulator', 'name': 'setw', 'value': n}
2842
+
2843
+ def builtin_setfill(self, c: str) -> dict:
2844
+ """Set fill character (C++ setfill equivalent).
2845
+
2846
+ Usage:
2847
+ cout() << setfill('0') << setw(5) << 42; // "00042"
2848
+ """
2849
+ return {'type': 'manipulator', 'name': 'setfill', 'value': c}
2850
+
2851
+ def builtin_fixed(self) -> dict:
2852
+ """Use fixed-point notation (C++ fixed equivalent).
2853
+
2854
+ Usage:
2855
+ cout() << fixed() << 3.14159;
2856
+ """
2857
+ return {'type': 'manipulator', 'name': 'fixed'}
2858
+
2859
+ def builtin_scientific(self) -> dict:
2860
+ """Use scientific notation (C++ scientific equivalent).
2861
+
2862
+ Usage:
2863
+ cout() << scientific() << 1234.5; // "1.234500e+03"
2864
+ """
2865
+ return {'type': 'manipulator', 'name': 'scientific'}
2866
+
2867
+ def builtin_flush(self) -> str:
2868
+ """Flush stream (C++ flush equivalent).
2869
+
2870
+ Usage:
2871
+ cout() << "Processing..." << flush();
2872
+ """
2873
+ return 'flush'
2874
+
2875
+ # ============= Struct Operations =============
2876
+
2877
+ def builtin_sizeof(self, obj: Any) -> int:
2878
+ """Get size of object in bytes (C sizeof equivalent).
2879
+
2880
+ For CStruct, returns estimated memory size.
2881
+ For other types, returns sys.getsizeof.
2882
+
2883
+ Usage:
2884
+ @size = sizeof(myStruct);
2885
+ @size = sizeof(myArray);
2886
+ """
2887
+ import sys
2888
+ from .cssl_types import CStruct
2889
+ if isinstance(obj, CStruct):
2890
+ return obj.sizeof()
2891
+ return sys.getsizeof(obj)
2892
+
2893
+ def builtin_memcpy(self, dest: Any, src: Any, n: int = None) -> Any:
2894
+ """Copy memory/data (C memcpy equivalent).
2895
+
2896
+ For lists/arrays, copies n elements.
2897
+ For structs, copies all fields.
2898
+
2899
+ Usage:
2900
+ memcpy(destArray, srcArray, 10);
2901
+ memcpy(destStruct, srcStruct);
2902
+ """
2903
+ from .cssl_types import CStruct
2904
+ if isinstance(src, CStruct) and isinstance(dest, CStruct):
2905
+ # Copy struct fields
2906
+ for name, value in src._values.items():
2907
+ if name in dest._fields:
2908
+ dest._values[name] = value
2909
+ return dest
2910
+ elif hasattr(dest, '__setitem__') and hasattr(src, '__getitem__'):
2911
+ # Copy array/list elements
2912
+ count = n if n is not None else len(src)
2913
+ for i in range(min(count, len(src), len(dest) if hasattr(dest, '__len__') else count)):
2914
+ dest[i] = src[i]
2915
+ return dest
2916
+ else:
2917
+ # Generic copy
2918
+ import copy
2919
+ return copy.copy(src)
2920
+
2921
+ def builtin_memset(self, dest: Any, value: Any, n: int = None) -> Any:
2922
+ """Set memory/data to value (C memset equivalent).
2923
+
2924
+ For lists/arrays, sets n elements to value.
2925
+ For structs, sets all fields to value.
2926
+
2927
+ Usage:
2928
+ memset(myArray, 0, 100);
2929
+ memset(myStruct, null);
2930
+ """
2931
+ from .cssl_types import CStruct
2932
+ if isinstance(dest, CStruct):
2933
+ for name in dest._values:
2934
+ dest._values[name] = value
2935
+ return dest
2936
+ elif hasattr(dest, '__setitem__'):
2937
+ count = n if n is not None else len(dest)
2938
+ for i in range(min(count, len(dest) if hasattr(dest, '__len__') else count)):
2939
+ dest[i] = value
2940
+ return dest
2941
+ return dest
2942
+
2943
+ # ============= Pipe Operations =============
2944
+
2945
+ def builtin_pipe(self, data: Any = None) -> 'Pipe':
2946
+ """Create a new pipe for data transformation.
2947
+
2948
+ C++ optimized piping with | operator support.
2949
+
2950
+ Usage:
2951
+ @result = pipe([1,2,3,4,5])
2952
+ | Pipe.filter(x => x > 2)
2953
+ | Pipe.map(x => x * 2)
2954
+ | collect();
2955
+ """
2956
+ from .cssl_types import Pipe
2957
+ return Pipe(data)
2958
+
2959
+ # ============= Optimized Containment Check =============
2960
+
2961
+ def builtin_contains_fast(self, container: Any, item: Any, use_native: bool = False) -> bool:
2962
+ """Fast containment check (C++ optimized 'in' operator).
2963
+
2964
+ When use_native=False (default), uses C++ optimized search.
2965
+ When use_native=True, uses Python's native 'in' operator.
2966
+
2967
+ For sorted containers, uses binary search (O(log n)).
2968
+ For hash-based containers, uses O(1) lookup.
2969
+
2970
+ Usage:
2971
+ // C++ optimized (default)
2972
+ @found = contains_fast(myList, 42);
2973
+
2974
+ // Python native
2975
+ @found = contains_fast(myList, 42, true);
2976
+ """
2977
+ if use_native:
2978
+ # Python native 'in' operator
2979
+ return item in container
2980
+
2981
+ # C++ optimized path
2982
+ from .cssl_types import Vector, Array, List, Dictionary, Map, DataStruct
2983
+
2984
+ # Hash-based containers - O(1)
2985
+ if isinstance(container, (dict, set, frozenset)):
2986
+ return item in container
2987
+ if isinstance(container, (Dictionary, Map)):
2988
+ return container.contains(item)
2989
+
2990
+ # For sorted data, use binary search - O(log n)
2991
+ if isinstance(container, (list, tuple, Vector, Array, List, DataStruct)):
2992
+ # Check if sorted (sample check for performance)
2993
+ data = list(container) if not isinstance(container, list) else container
2994
+ if len(data) > 10:
2995
+ # Quick sorted check on sample
2996
+ sample = [data[i] for i in range(0, len(data), max(1, len(data) // 10))]
2997
+ try:
2998
+ is_sorted = all(sample[i] <= sample[i+1] for i in range(len(sample)-1))
2999
+ if is_sorted:
3000
+ # Binary search
3001
+ import bisect
3002
+ idx = bisect.bisect_left(data, item)
3003
+ return idx < len(data) and data[idx] == item
3004
+ except TypeError:
3005
+ pass # Non-comparable items, fall through to linear
3006
+
3007
+ # Linear search with early termination
3008
+ for x in data:
3009
+ if x == item:
3010
+ return True
3011
+ return False
3012
+
3013
+ # String containment - use native (already optimized in Python)
3014
+ if isinstance(container, str):
3015
+ return str(item) in container
3016
+
3017
+ # Generic fallback
3018
+ try:
3019
+ return item in container
3020
+ except TypeError:
3021
+ return False
3022
+
3023
+ # ============= Extended String Functions =============
3024
+
3025
+ def builtin_sprintf(self, fmt: str, *args) -> str:
3026
+ """C-style format string with validation.
3027
+
3028
+ v4.7.1: Added format specifier validation for security.
3029
+ """
3030
+ import re
3031
+ # Count format specifiers (excluding %%)
3032
+ specifiers = re.findall(r'%(?!%)[#0\- +]*\d*\.?\d*[hlL]?[diouxXeEfFgGcrsab]', fmt)
3033
+ if len(specifiers) != len(args):
3034
+ raise ValueError(
3035
+ f"Format string has {len(specifiers)} specifiers but {len(args)} arguments provided"
3036
+ )
3037
+ return fmt % args
3038
+
3039
+ def builtin_chars(self, s: str) -> list:
3040
+ """Convert string to list of characters"""
3041
+ return list(s)
3042
+
3043
+ def builtin_ord(self, c: str) -> int:
3044
+ """Get ASCII/Unicode code of character"""
3045
+ return ord(c[0] if c else '\0')
3046
+
3047
+ def builtin_chr(self, n: int) -> str:
3048
+ """Convert ASCII/Unicode code to character"""
3049
+ return chr(n)
3050
+
3051
+ def builtin_capitalize(self, s: str) -> str:
3052
+ return str(s).capitalize()
3053
+
3054
+ def builtin_title(self, s: str) -> str:
3055
+ return str(s).title()
3056
+
3057
+ def builtin_swapcase(self, s: str) -> str:
3058
+ return str(s).swapcase()
3059
+
3060
+ def builtin_center(self, s: str, width: int, fillchar: str = ' ') -> str:
3061
+ return str(s).center(width, fillchar)
3062
+
3063
+ def builtin_zfill(self, s: str, width: int) -> str:
3064
+ return str(s).zfill(width)
3065
+
3066
+ def builtin_isalpha(self, s: str) -> bool:
3067
+ return str(s).isalpha()
3068
+
3069
+ def builtin_isdigit(self, s: str) -> bool:
3070
+ return str(s).isdigit()
3071
+
3072
+ def builtin_isalnum(self, s: str) -> bool:
3073
+ return str(s).isalnum()
3074
+
3075
+ def builtin_isspace(self, s: str) -> bool:
3076
+ return str(s).isspace()
3077
+
3078
+ # ============= Extended List Functions =============
3079
+
3080
+ def builtin_enumerate(self, lst: list, start: int = 0) -> list:
3081
+ """Return list of (index, value) pairs"""
3082
+ return list(enumerate(lst, start))
3083
+
3084
+ def builtin_zip(self, *lists) -> list:
3085
+ """Zip multiple lists together"""
3086
+ return list(zip(*lists))
3087
+
3088
+ def builtin_reversed(self, lst: list) -> list:
3089
+ """Return reversed list
3090
+
3091
+ v4.8.7: Added None check.
3092
+ """
3093
+ if lst is None:
3094
+ return []
3095
+ if not isinstance(lst, (list, tuple)):
3096
+ return [lst]
3097
+ return list(reversed(lst))
3098
+
3099
+ def builtin_sorted(self, lst: list, key: str = None, reverse: bool = False) -> list:
3100
+ """Return sorted list"""
3101
+ if key:
3102
+ return sorted(lst, key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=reverse)
3103
+ return sorted(lst, reverse=reverse)
3104
+
3105
+ def builtin_count(self, collection: Union[list, str], item: Any) -> int:
3106
+ """Count occurrences of item"""
3107
+ return collection.count(item)
3108
+
3109
+ def builtin_first(self, lst: list, default: Any = None) -> Any:
3110
+ """Get first element or default"""
3111
+ return lst[0] if lst else default
1649
3112
 
1650
3113
  def builtin_last(self, lst: list, default: Any = None) -> Any:
1651
3114
  """Get last element or default"""
@@ -1849,27 +3312,60 @@ class CSSLBuiltins:
1849
3312
 
1850
3313
  def builtin_initsh(self, path: str, *args) -> int:
1851
3314
  """
1852
- Execute a shell script
1853
- Usage: initsh('/path/to/script.sh')
3315
+ Execute a shell script from the 'scripts' directory only.
3316
+
3317
+ v4.7.1: Restricted to 'scripts/' directory for security.
3318
+
3319
+ Usage: initsh('myscript.sh') // Runs scripts/myscript.sh
1854
3320
  """
1855
3321
  import subprocess
1856
3322
 
1857
- if not os.path.isabs(path) and self.runtime and self.runtime.service_engine:
1858
- path = os.path.join(self.runtime.service_engine.KernelClient.RootDirectory, path)
3323
+ # v4.7.1: Security - prevent directory traversal
3324
+ if '..' in path or os.path.isabs(path):
3325
+ raise CSSLBuiltinError(
3326
+ f"Invalid script path: '{path}'. "
3327
+ "Scripts must be relative paths without '..' and must be in 'scripts/' directory."
3328
+ )
1859
3329
 
1860
- if not os.path.exists(path):
1861
- raise CSSLBuiltinError(f"Shell script not found: {path}")
3330
+ # Determine base directory
3331
+ if self.runtime and self.runtime.service_engine:
3332
+ base_dir = self.runtime.service_engine.KernelClient.RootDirectory
3333
+ else:
3334
+ base_dir = os.getcwd()
3335
+
3336
+ # Force scripts to be in 'scripts' subdirectory
3337
+ scripts_dir = os.path.join(base_dir, 'scripts')
3338
+ full_path = os.path.normpath(os.path.join(scripts_dir, path))
3339
+
3340
+ # Verify path is within scripts directory (prevent traversal)
3341
+ if not full_path.startswith(os.path.normpath(scripts_dir)):
3342
+ raise CSSLBuiltinError(
3343
+ f"Security: Script path '{path}' escapes the scripts directory."
3344
+ )
3345
+
3346
+ if not os.path.exists(full_path):
3347
+ raise CSSLBuiltinError(f"Shell script not found: {full_path}")
3348
+
3349
+ # Validate file extension
3350
+ valid_extensions = {'.sh', '.bat', '.cmd', '.ps1'}
3351
+ _, ext = os.path.splitext(full_path)
3352
+ if ext.lower() not in valid_extensions:
3353
+ raise CSSLBuiltinError(
3354
+ f"Invalid script type: '{ext}'. Allowed: {', '.join(valid_extensions)}"
3355
+ )
1862
3356
 
1863
3357
  try:
1864
3358
  # Determine shell based on platform
1865
3359
  import platform
1866
3360
  if platform.system() == 'Windows':
1867
- # Use cmd or powershell on Windows
1868
- cmd = ['cmd', '/c', path] + list(args)
3361
+ if ext.lower() == '.ps1':
3362
+ cmd = ['powershell', '-ExecutionPolicy', 'Bypass', '-File', full_path] + list(args)
3363
+ else:
3364
+ cmd = ['cmd', '/c', full_path] + list(args)
1869
3365
  else:
1870
- cmd = ['bash', path] + list(args)
3366
+ cmd = ['bash', full_path] + list(args)
1871
3367
 
1872
- result = subprocess.run(cmd, capture_output=True, text=True)
3368
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
1873
3369
 
1874
3370
  if result.stdout:
1875
3371
  print(result.stdout)
@@ -1878,6 +3374,8 @@ class CSSLBuiltins:
1878
3374
 
1879
3375
  return result.returncode
1880
3376
 
3377
+ except subprocess.TimeoutExpired:
3378
+ raise CSSLBuiltinError(f"Script execution timeout (60s): {path}")
1881
3379
  except Exception as e:
1882
3380
  print(f"Shell execution error [{path}]: {e}")
1883
3381
  raise CSSLBuiltinError(f"initsh failed: {e}")
@@ -2019,9 +3517,9 @@ class CSSLBuiltins:
2019
3517
  except Exception:
2020
3518
  pass
2021
3519
 
2022
- # Clean path and join
3520
+ # Clean path and join (strip leading separators for both platforms)
2023
3521
  if path:
2024
- clean_path = path.lstrip('/').lstrip('\\')
3522
+ clean_path = path.lstrip('/\\')
2025
3523
  return os.path.normpath(os.path.join(base, clean_path))
2026
3524
 
2027
3525
  return base
@@ -2163,7 +3661,30 @@ class CSSLBuiltins:
2163
3661
  def _execute_python_module(self, name: str, source: str, filepath: str) -> Any:
2164
3662
  """
2165
3663
  Execute Python source and return a module-like object with all functions.
3664
+
3665
+ v4.8.8: Security check - scan source for blocked module imports.
2166
3666
  """
3667
+ import re
3668
+
3669
+ # v4.8.8: Security - check for blocked module imports in source
3670
+ # This prevents the bypass: makemodule a Python file that imports os,
3671
+ # then include() it in CSSL to access os through the module.
3672
+ for blocked in self.BLOCKED_MODULES:
3673
+ # Check for: import os, import os as x, from os import, from os.path import
3674
+ patterns = [
3675
+ rf'\bimport\s+{blocked}\b', # import os
3676
+ rf'\bimport\s+{blocked}\s+as\b', # import os as x
3677
+ rf'\bfrom\s+{blocked}\b', # from os import / from os.path import
3678
+ rf'\b{blocked}\s*=\s*__import__', # os = __import__('os')
3679
+ ]
3680
+ for pattern in patterns:
3681
+ if re.search(pattern, source):
3682
+ raise CSSLBuiltinError(
3683
+ f"Module '{name}' imports blocked module '{blocked}'.\n"
3684
+ f"Security: {blocked} is not allowed in CSSL modules.\n"
3685
+ f"Use CSSL builtins instead."
3686
+ )
3687
+
2167
3688
  # Create a namespace for the module
2168
3689
  module_namespace = {
2169
3690
  '__name__': name,
@@ -2292,18 +3813,40 @@ class CSSLBuiltins:
2292
3813
  is_absolute = os.path.isabs(filepath) or (len(filepath) > 2 and filepath[1] == ':')
2293
3814
 
2294
3815
  if not is_absolute:
2295
- # Try relative to current working directory first
2296
- cwd_path = os.path.join(os.getcwd(), filepath)
2297
- if os.path.exists(cwd_path):
2298
- filepath = cwd_path
2299
- else:
2300
- # Try with .cssl-pl extension
2301
- cwd_path_pl = os.path.join(os.getcwd(), filepath + '.cssl-pl')
2302
- if os.path.exists(cwd_path_pl):
2303
- filepath = cwd_path_pl
3816
+ # v4.8.8: First try relative to current executing file's directory
3817
+ # This allows payload("other.cssl-pl") to find files in same folder
3818
+ current_file_dir = None
3819
+ if hasattr(self.runtime, '_current_file_path') and self.runtime._current_file_path:
3820
+ current_file_dir = os.path.dirname(self.runtime._current_file_path)
3821
+
3822
+ found = False
3823
+
3824
+ # Try relative to current file's directory first (if available)
3825
+ if current_file_dir:
3826
+ file_relative_path = os.path.join(current_file_dir, filepath)
3827
+ if os.path.exists(file_relative_path):
3828
+ filepath = file_relative_path
3829
+ found = True
3830
+ else:
3831
+ # Try with .cssl-pl extension
3832
+ file_relative_pl = os.path.join(current_file_dir, filepath + '.cssl-pl')
3833
+ if os.path.exists(file_relative_pl):
3834
+ filepath = file_relative_pl
3835
+ found = True
3836
+
3837
+ # Fall back to CWD if not found relative to current file
3838
+ if not found:
3839
+ cwd_path = os.path.join(os.getcwd(), filepath)
3840
+ if os.path.exists(cwd_path):
3841
+ filepath = cwd_path
2304
3842
  else:
2305
- # Fall back to cso_root for service context
2306
- filepath = self.builtin_cso_root(filepath)
3843
+ # Try with .cssl-pl extension
3844
+ cwd_path_pl = os.path.join(os.getcwd(), filepath + '.cssl-pl')
3845
+ if os.path.exists(cwd_path_pl):
3846
+ filepath = cwd_path_pl
3847
+ else:
3848
+ # Fall back to cso_root for service context
3849
+ filepath = self.builtin_cso_root(filepath)
2307
3850
 
2308
3851
  # Check file exists, try with .cssl-pl extension if not
2309
3852
  if not os.path.exists(filepath):
@@ -2335,16 +3878,24 @@ class CSSLBuiltins:
2335
3878
 
2336
3879
  ast = parse_cssl_program(source)
2337
3880
 
2338
- if libname:
2339
- # Namespaced execution: execute in isolated scope
2340
- self._execute_payload_namespaced(ast, libname, source)
2341
- else:
2342
- # Standard execution: apply globally
2343
- # Execute the payload - this will:
2344
- # - Register global variables (accessible via @name)
2345
- # - Define functions in current scope
2346
- # - Set up any code injections
2347
- self.runtime._execute_node(ast)
3881
+ # v4.8.8: Track current file path for relative payload resolution
3882
+ prev_file_path = getattr(self.runtime, '_current_file_path', None)
3883
+ self.runtime._current_file_path = filepath
3884
+
3885
+ try:
3886
+ if libname:
3887
+ # Namespaced execution: execute in isolated scope
3888
+ self._execute_payload_namespaced(ast, libname, source)
3889
+ else:
3890
+ # Standard execution: apply globally
3891
+ # Execute the payload - this will:
3892
+ # - Register global variables (accessible via @name)
3893
+ # - Define functions in current scope
3894
+ # - Set up any code injections
3895
+ self.runtime._execute_node(ast)
3896
+ finally:
3897
+ # Restore previous file path
3898
+ self.runtime._current_file_path = prev_file_path
2348
3899
 
2349
3900
  except Exception as e:
2350
3901
  raise CSSLBuiltinError(f"Failed to load payload '{filepath}': {e}")
@@ -2594,29 +4145,42 @@ class CSSLBuiltins:
2594
4145
  # This is handled by the runtime which passes the path
2595
4146
  pass
2596
4147
 
2597
- def builtin_delete(self, name: str) -> bool:
4148
+ def builtin_delete(self, target: Any, destructor_name: str = None) -> bool:
2598
4149
  """
2599
- Delete a shared object by name.
2600
- Usage: delete("MyLib") - removes the $MyLib shared object
4150
+ Delete a shared object by name or call destructors on a CSSLInstance.
4151
+
4152
+ Usage:
4153
+ delete("MyLib") - removes the $MyLib shared object
4154
+ delete(myInstance) - calls all destructors on CSSLInstance
4155
+ delete(myInstance, "Init") - calls only ~Init destructor
2601
4156
 
2602
4157
  Args:
2603
- name: Name of the shared object (without the $ prefix)
4158
+ target: Name string for shared objects OR CSSLInstance for destructor calls
4159
+ destructor_name: Optional - specific destructor name (without ~)
2604
4160
 
2605
4161
  Returns:
2606
- True if deleted, False if not found
4162
+ True if deleted/destroyed, False if not found
2607
4163
  """
4164
+ from .cssl_types import CSSLInstance
2608
4165
  from ..cssl_bridge import _live_objects
2609
4166
 
2610
- # Remove from live objects registry
2611
- if name in _live_objects:
2612
- del _live_objects[name]
2613
- # Also remove from runtime's global scope if present
2614
- if self.runtime:
2615
- try:
2616
- self.runtime.global_scope.delete(f'${name}')
2617
- except Exception:
2618
- pass
2619
- return True
4167
+ # v4.8.8: Handle CSSLInstance - call destructors
4168
+ if isinstance(target, CSSLInstance):
4169
+ return self.builtin_instance_delete(target, destructor_name)
4170
+
4171
+ # Handle string name - delete shared object
4172
+ if isinstance(target, str):
4173
+ name = target
4174
+ if name in _live_objects:
4175
+ del _live_objects[name]
4176
+ # Also remove from runtime's global scope if present
4177
+ if self.runtime:
4178
+ try:
4179
+ self.runtime.global_scope.delete(f'${name}')
4180
+ except Exception:
4181
+ pass
4182
+ return True
4183
+
2620
4184
  return False
2621
4185
 
2622
4186
  # ============= CSSL Data Type Constructors =============
@@ -2869,6 +4433,387 @@ class CSSLBuiltins:
2869
4433
  # Wrap the Python object
2870
4434
  return CSSLizedPythonObject(python_obj, self.runtime)
2871
4435
 
4436
+ # =========================================================================
4437
+ # v4.8.8: Python Parameter Functions - CsslLang API parameter passing
4438
+ # =========================================================================
4439
+
4440
+ def _get_parameter_object(self):
4441
+ """Get the Parameter object from runtime scope."""
4442
+ if self.runtime and hasattr(self.runtime, 'global_scope'):
4443
+ param = self.runtime.global_scope.get('parameter')
4444
+ if param is not None:
4445
+ return param
4446
+ return None
4447
+
4448
+ def _is_blocked_module(self, value: Any) -> tuple:
4449
+ """Check if a value is a blocked Python module.
4450
+
4451
+ Returns:
4452
+ (is_blocked: bool, module_name: str or None)
4453
+ """
4454
+ import types
4455
+ if isinstance(value, types.ModuleType):
4456
+ module_name = getattr(value, '__name__', '')
4457
+ base_name = module_name.split('.')[0]
4458
+ if base_name in self.BLOCKED_MODULES:
4459
+ return (True, module_name)
4460
+ return (False, None)
4461
+
4462
+ def builtin_python_parameter_get(self, index: int, default: Any = None) -> Any:
4463
+ """Get a parameter passed from Python via CsslLang.run().
4464
+
4465
+ v4.8.8: Security - blocks dangerous modules (os, sys, subprocess, etc.)
4466
+ from being passed as parameters.
4467
+
4468
+ Usage:
4469
+ // Python: cssl.run("script.cssl", math_module, "arg2", 123)
4470
+ // CSSL:
4471
+ @math = python::param_get(0); // Gets math module (allowed)
4472
+ arg2 = python::param_get(1); // Gets "arg2"
4473
+ num = python::param_get(2); // Gets 123
4474
+ missing = python::param_get(99, "default"); // Gets "default"
4475
+
4476
+ Args:
4477
+ index: The parameter index (0-based)
4478
+ default: Value to return if parameter doesn't exist
4479
+
4480
+ Returns:
4481
+ The parameter value or default
4482
+
4483
+ Raises:
4484
+ CSSLBuiltinError: If the parameter is a blocked module
4485
+ """
4486
+ param = self._get_parameter_object()
4487
+ if param is None:
4488
+ return default
4489
+
4490
+ value = param.get(index, default)
4491
+
4492
+ # Security check: block dangerous modules passed as parameters
4493
+ is_blocked, module_name = self._is_blocked_module(value)
4494
+ if is_blocked:
4495
+ raise CSSLBuiltinError(
4496
+ f"Security: Module '{module_name}' cannot be passed as a parameter.\n"
4497
+ f"Blocked modules: {', '.join(sorted(self.BLOCKED_MODULES))}\n"
4498
+ f"Use CSSL builtins instead for filesystem/system operations."
4499
+ )
4500
+
4501
+ return value
4502
+
4503
+ def builtin_python_parameter_return(self, value: Any) -> None:
4504
+ """Return a value back to Python from CSSL.
4505
+
4506
+ Usage:
4507
+ // CSSL:
4508
+ result = compute_something();
4509
+ python::parameter.return(result);
4510
+
4511
+ // Python:
4512
+ returned = cssl.run("script.cssl", arg1, arg2)
4513
+ print(returned) // Gets the value passed to parameter.return()
4514
+
4515
+ Args:
4516
+ value: The value to return to Python
4517
+ """
4518
+ param = self._get_parameter_object()
4519
+ if param is None:
4520
+ raise CSSLBuiltinError("python::parameter.return() can only be used when called from CsslLang.run()")
4521
+ param.return_(value)
4522
+
4523
+ def builtin_python_parameter_count(self) -> int:
4524
+ """Get the number of parameters passed from Python.
4525
+
4526
+ Usage:
4527
+ count = python::parameter.count();
4528
+ printl("Received " + str(count) + " parameters");
4529
+
4530
+ Returns:
4531
+ Number of parameters
4532
+ """
4533
+ param = self._get_parameter_object()
4534
+ if param is None:
4535
+ return 0
4536
+ return param.count()
4537
+
4538
+ def builtin_python_parameter_all(self) -> list:
4539
+ """Get all parameters as a list.
4540
+
4541
+ v4.8.8: Security - filters out blocked modules from the list.
4542
+
4543
+ Usage:
4544
+ all_args = python::param_all();
4545
+ foreach (arg in all_args) {
4546
+ printl(arg);
4547
+ }
4548
+
4549
+ Returns:
4550
+ List of all parameters (blocked modules are filtered out)
4551
+ """
4552
+ param = self._get_parameter_object()
4553
+ if param is None:
4554
+ return []
4555
+
4556
+ # Filter out blocked modules for security
4557
+ result = []
4558
+ for value in param.all():
4559
+ is_blocked, _ = self._is_blocked_module(value)
4560
+ if not is_blocked:
4561
+ result.append(value)
4562
+ return result
4563
+
4564
+ def builtin_python_parameter_has(self, index: int) -> bool:
4565
+ """Check if a parameter exists at the given index.
4566
+
4567
+ Usage:
4568
+ if (python::parameter.has(2)) {
4569
+ third_arg = python::parameter.get(2);
4570
+ }
4571
+
4572
+ Args:
4573
+ index: The parameter index to check
4574
+
4575
+ Returns:
4576
+ True if parameter exists, False otherwise
4577
+ """
4578
+ param = self._get_parameter_object()
4579
+ if param is None:
4580
+ return False
4581
+ return param.has(index)
4582
+
4583
+ # =========================================================================
4584
+ # v4.6.5: Watcher Namespace Functions - Live Python Instance Access
4585
+ # =========================================================================
4586
+
4587
+ def builtin_watcher_get(self, watcher_id: str) -> Any:
4588
+ """Get all instances from a Python CsslWatcher.
4589
+
4590
+ Syntax:
4591
+ all_instances = watcher::get("MyWatcher");
4592
+ pygame = all_instances['Game'];
4593
+ game_instance = all_instances['game'];
4594
+
4595
+ Example in Python:
4596
+ from includecpp.core.cssl_bridge import CsslWatcher
4597
+ cwatcher = CsslWatcher(id="MyWatcher")
4598
+ cwatcher.start()
4599
+
4600
+ class Game:
4601
+ def start(self): print("Game started!")
4602
+ game = Game()
4603
+
4604
+ Then in CSSL:
4605
+ instances = watcher::get("MyWatcher");
4606
+ instances['game'].start(); // "Game started!"
4607
+
4608
+ Args:
4609
+ watcher_id: The watcher's unique ID
4610
+
4611
+ Returns:
4612
+ Dict of all collected instances, classes, and functions
4613
+ """
4614
+ from ..cssl_bridge import watcher_get
4615
+ result = watcher_get(watcher_id)
4616
+ if result is None:
4617
+ return {}
4618
+ return result
4619
+
4620
+ def builtin_watcher_set(self, watcher_id: str, path: str, value: Any) -> bool:
4621
+ """Set/overwrite a value in a Python watcher (bidirectional).
4622
+
4623
+ Syntax:
4624
+ watcher::set("MyWatcher", "Game.start", myNewFunction);
4625
+
4626
+ This allows CSSL to overwrite Python functions/methods at runtime.
4627
+
4628
+ Args:
4629
+ watcher_id: The watcher's unique ID
4630
+ path: Path to the item (e.g., 'Game.start')
4631
+ value: New value (function, class, or instance)
4632
+
4633
+ Returns:
4634
+ true if successful, false otherwise
4635
+ """
4636
+ from ..cssl_bridge import watcher_set
4637
+ return watcher_set(watcher_id, path, value)
4638
+
4639
+ def builtin_watcher_list(self) -> list:
4640
+ """List all active watcher IDs.
4641
+
4642
+ Syntax:
4643
+ watchers = watcher::list();
4644
+ foreach (w in watchers) {
4645
+ printl("Active watcher: " + w);
4646
+ }
4647
+
4648
+ Returns:
4649
+ List of active watcher IDs
4650
+ """
4651
+ from ..cssl_bridge import list_watchers
4652
+ return list_watchers()
4653
+
4654
+ def builtin_watcher_exists(self, watcher_id: str) -> bool:
4655
+ """Check if a watcher with the given ID exists.
4656
+
4657
+ Syntax:
4658
+ if watcher::exists("MyWatcher") {
4659
+ instances = watcher::get("MyWatcher");
4660
+ }
4661
+
4662
+ Args:
4663
+ watcher_id: The watcher's unique ID
4664
+
4665
+ Returns:
4666
+ true if watcher exists, false otherwise
4667
+ """
4668
+ from ..cssl_bridge import get_watcher
4669
+ return get_watcher(watcher_id) is not None
4670
+
4671
+ def builtin_watcher_refresh(self, watcher_id: str) -> bool:
4672
+ """Manually refresh a watcher's collected instances.
4673
+
4674
+ Syntax:
4675
+ watcher::refresh("MyWatcher");
4676
+
4677
+ This forces the watcher to re-scan the Python scope for new instances.
4678
+
4679
+ Args:
4680
+ watcher_id: The watcher's unique ID
4681
+
4682
+ Returns:
4683
+ true if successful, false otherwise
4684
+ """
4685
+ from ..cssl_bridge import get_watcher
4686
+ watcher = get_watcher(watcher_id)
4687
+ if watcher:
4688
+ watcher.refresh()
4689
+ return True
4690
+ return False
4691
+
4692
+ # ============= v4.8.8: Snapshot Functions =============
4693
+ # Snapshot allows storing variable states and accessing them via %variable syntax
4694
+
4695
+ def builtin_snapshot(self, *args) -> bool:
4696
+ """Snapshot a variable's current value.
4697
+
4698
+ Usage:
4699
+ snapshot(variable) - Snapshot using auto-detected name
4700
+ snapshot(variable, "name") - Snapshot with explicit name
4701
+
4702
+ After snapshotting, access the value with %name syntax.
4703
+
4704
+ Example:
4705
+ string version = "1.0";
4706
+ snapshot(version);
4707
+ version = "2.0";
4708
+ println(version, %version); // Output: 2.0 1.0
4709
+ """
4710
+ import copy
4711
+
4712
+ if len(args) == 0:
4713
+ raise CSSLBuiltinError("snapshot() requires at least 1 argument")
4714
+
4715
+ value = args[0]
4716
+ name = args[1] if len(args) > 1 else None
4717
+
4718
+ # If no name provided, try to detect from runtime context
4719
+ if name is None and self.runtime:
4720
+ # Try to find the variable name from the current scope
4721
+ for var_name, var_val in self.runtime.scope.variables.items():
4722
+ if var_val is value and not var_name.startswith('_'):
4723
+ name = var_name
4724
+ break
4725
+ # Also check global scope
4726
+ if name is None:
4727
+ for var_name, var_val in self.runtime.global_scope.variables.items():
4728
+ if var_val is value and not var_name.startswith('_'):
4729
+ name = var_name
4730
+ break
4731
+ # v4.8.8: Also check builtins for function snapshots
4732
+ if name is None:
4733
+ for func_name, func_val in self._functions.items():
4734
+ if func_val is value and not func_name.startswith('_'):
4735
+ name = func_name
4736
+ break
4737
+
4738
+ if name is None:
4739
+ # Use a generic name based on type and hash
4740
+ name = f"_snap_{type(value).__name__}_{id(value)}"
4741
+
4742
+ # Deep copy to preserve the state
4743
+ try:
4744
+ snapped_value = copy.deepcopy(value)
4745
+ except:
4746
+ try:
4747
+ snapped_value = copy.copy(value)
4748
+ except:
4749
+ snapped_value = value
4750
+
4751
+ self._snapshots[name] = snapped_value
4752
+ return True
4753
+
4754
+ def builtin_get_snapshot(self, name: str) -> Any:
4755
+ """Get a snapshotted value by name.
4756
+
4757
+ Usage:
4758
+ value = get_snapshot("variableName")
4759
+
4760
+ This is the programmatic way to access snapshots.
4761
+ The %variable syntax is the preferred way in CSSL code.
4762
+ """
4763
+ if name not in self._snapshots:
4764
+ raise CSSLBuiltinError(f"No snapshot found for '{name}'")
4765
+ return self._snapshots[name]
4766
+
4767
+ def builtin_has_snapshot(self, name: str) -> bool:
4768
+ """Check if a snapshot exists.
4769
+
4770
+ Usage:
4771
+ if (has_snapshot("version")) { ... }
4772
+ """
4773
+ return name in self._snapshots
4774
+
4775
+ def builtin_clear_snapshot(self, name: str) -> bool:
4776
+ """Clear a specific snapshot.
4777
+
4778
+ Usage:
4779
+ clear_snapshot("variableName")
4780
+ """
4781
+ if name in self._snapshots:
4782
+ del self._snapshots[name]
4783
+ return True
4784
+ return False
4785
+
4786
+ def builtin_clear_all_snapshots(self) -> int:
4787
+ """Clear all snapshots, return count of cleared items.
4788
+
4789
+ Usage:
4790
+ count = clear_snapshots()
4791
+ """
4792
+ count = len(self._snapshots)
4793
+ self._snapshots.clear()
4794
+ return count
4795
+
4796
+ def builtin_list_snapshots(self) -> list:
4797
+ """List all snapshot names.
4798
+
4799
+ Usage:
4800
+ names = list_snapshots()
4801
+ for (name in names) { println(name + " = " + get_snapshot(name)); }
4802
+ """
4803
+ return list(self._snapshots.keys())
4804
+
4805
+ def builtin_restore_snapshot(self, name: str) -> Any:
4806
+ """Restore a snapshot value and remove it from storage.
4807
+
4808
+ Usage:
4809
+ version = restore_snapshot("version")
4810
+ """
4811
+ if name not in self._snapshots:
4812
+ raise CSSLBuiltinError(f"No snapshot found for '{name}'")
4813
+ value = self._snapshots[name]
4814
+ del self._snapshots[name]
4815
+ return value
4816
+
2872
4817
 
2873
4818
  class CSSLizedPythonObject:
2874
4819
  """CSSL wrapper for Python objects (classes, instances, functions).