IncludeCPP 4.0.2__py3-none-any.whl → 4.3.0__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.
@@ -36,6 +36,7 @@ class CSSLBuiltins:
36
36
  # Output functions
37
37
  self._functions['print'] = self.builtin_print
38
38
  self._functions['println'] = self.builtin_println
39
+ self._functions['input'] = self.builtin_input
39
40
  self._functions['debug'] = self.builtin_debug
40
41
  self._functions['error'] = self.builtin_error
41
42
  self._functions['warn'] = self.builtin_warn
@@ -291,6 +292,7 @@ class CSSLBuiltins:
291
292
  self._functions['include'] = self.builtin_include
292
293
  self._functions['payload'] = self.builtin_payload
293
294
  self._functions['get'] = self.builtin_get
295
+ self._functions['libinclude'] = self.builtin_libinclude # v4.1.0: Multi-language support
294
296
 
295
297
  # NEW: Extended OS Functions
296
298
  self._functions['Listdir'] = self.builtin_listdir # Alias with capital L
@@ -348,22 +350,56 @@ class CSSLBuiltins:
348
350
 
349
351
  def builtin_print(self, *args, **kwargs) -> None:
350
352
  """Print without newline"""
353
+ import sys
351
354
  sep = kwargs.get('sep', ' ')
352
355
  end = kwargs.get('end', '')
353
356
  output = sep.join(str(a) for a in args) + end
354
357
  if self.runtime and hasattr(self.runtime, 'output'):
355
358
  self.runtime.output(output)
356
359
  else:
357
- print(output, end='')
360
+ # Handle encoding issues on Windows console
361
+ try:
362
+ print(output, end='')
363
+ except UnicodeEncodeError:
364
+ encoded = output.encode(sys.stdout.encoding or 'utf-8', errors='replace')
365
+ print(encoded.decode(sys.stdout.encoding or 'utf-8', errors='replace'), end='')
358
366
 
359
367
  def builtin_println(self, *args, **kwargs) -> None:
360
368
  """Print with newline"""
369
+ import sys
361
370
  sep = kwargs.get('sep', ' ')
362
371
  output = sep.join(str(a) for a in args)
363
372
  if self.runtime and hasattr(self.runtime, 'output'):
364
373
  self.runtime.output(output + '\n')
365
374
  else:
366
- print(output)
375
+ # Handle encoding issues on Windows console
376
+ try:
377
+ print(output)
378
+ except UnicodeEncodeError:
379
+ # Fallback: encode with errors='replace' for unsupported chars
380
+ encoded = output.encode(sys.stdout.encoding or 'utf-8', errors='replace')
381
+ print(encoded.decode(sys.stdout.encoding or 'utf-8', errors='replace'))
382
+
383
+ def builtin_input(self, prompt: str = "") -> str:
384
+ """Read user input from console.
385
+
386
+ Usage:
387
+ name = input("Enter your name: ");
388
+ age = int(input("Enter your age: "));
389
+
390
+ // Without prompt
391
+ value = input();
392
+
393
+ Args:
394
+ prompt: Optional prompt text to display
395
+
396
+ Returns:
397
+ The user's input as a string
398
+ """
399
+ if self.runtime and hasattr(self.runtime, 'input'):
400
+ return self.runtime.input(prompt)
401
+ else:
402
+ return input(prompt)
367
403
 
368
404
  def builtin_debug(self, *args) -> None:
369
405
  """Debug output"""
@@ -1418,8 +1454,20 @@ class CSSLBuiltins:
1418
1454
  time.sleep(ms / 1000.0)
1419
1455
 
1420
1456
  def builtin_pyimport(self, module_name: str) -> Any:
1421
- """Import a Python module for use in CSSL"""
1422
- return __import__(module_name)
1457
+ """
1458
+ Import a Python module for use in CSSL.
1459
+
1460
+ v4.1.1: Fixed to use importlib.import_module for proper nested module support.
1461
+
1462
+ Usage:
1463
+ module = pyimport("plugins.app")
1464
+ result = module.my_function()
1465
+
1466
+ os = pyimport("os")
1467
+ path = os.path.join("a", "b")
1468
+ """
1469
+ import importlib
1470
+ return importlib.import_module(module_name)
1423
1471
 
1424
1472
  # ============= Extended String Functions =============
1425
1473
 
@@ -1937,6 +1985,45 @@ class CSSLBuiltins:
1937
1985
  except Exception as e:
1938
1986
  raise CSSLBuiltinError(f"Failed to include '{filepath}': {e}")
1939
1987
 
1988
+ def builtin_libinclude(self, lang_id: str) -> Any:
1989
+ """
1990
+ Load a language support module for multi-language syntax support.
1991
+
1992
+ v4.1.0: Multi-language support for CSSL.
1993
+
1994
+ Usage:
1995
+ @py = libinclude("python")
1996
+ cpp = libinclude("c++")
1997
+ java = libinclude("java")
1998
+ js = libinclude("javascript")
1999
+ csharp = libinclude("c#")
2000
+
2001
+ Returns: LanguageSupport object that can be used with 'supports' keyword.
2002
+
2003
+ Example:
2004
+ @py = libinclude("python");
2005
+
2006
+ define my_func() : supports @py {
2007
+ # Python syntax here!
2008
+ for i in range(10):
2009
+ print(i)
2010
+ }
2011
+
2012
+ class MyClass : extends cpp$BaseClass {
2013
+ // Inherit from C++ class
2014
+ }
2015
+ """
2016
+ from .cssl_languages import get_language
2017
+
2018
+ lang_support = get_language(lang_id)
2019
+ if lang_support is None:
2020
+ supported = ["python", "py", "java", "c#", "csharp", "c++", "cpp", "javascript", "js"]
2021
+ raise CSSLBuiltinError(
2022
+ f"Unknown language '{lang_id}'. Supported languages: {', '.join(supported)}"
2023
+ )
2024
+
2025
+ return lang_support
2026
+
1940
2027
  def _load_cssl_module(self, filepath: str, source: str) -> Any:
1941
2028
  """
1942
2029
  Load a .cssl-mod module file and return a callable module object.
@@ -2037,9 +2124,9 @@ class CSSLBuiltins:
2037
2124
 
2038
2125
  return CppModuleStub(name, source)
2039
2126
 
2040
- def builtin_payload(self, filepath: str) -> None:
2127
+ def builtin_payload(self, filepath: str, libname: str = None) -> None:
2041
2128
  """
2042
- Load a CSSL payload file (.cssl-pl) and execute it in the current scope.
2129
+ Load a CSSL payload file (.cssl-pl) and execute it.
2043
2130
 
2044
2131
  Payloads are like header files but for CSSL:
2045
2132
  - Define global variables (accessible via @name)
@@ -2048,8 +2135,19 @@ class CSSLBuiltins:
2048
2135
  - Set configuration values
2049
2136
 
2050
2137
  Usage in .cssl file:
2138
+ // Standard: everything is applied globally
2051
2139
  payload("myconfig.cssl-pl");
2052
- // Now all globals, functions, and injections are active
2140
+
2141
+ // Namespaced: everything goes into a namespace
2142
+ payload("myconfig.cssl-pl", "mylib");
2143
+ mylib::myFunction(); // Access via namespace
2144
+
2145
+ Namespace behavior:
2146
+ - Without libname: All changes are global (embedded replaces globally)
2147
+ - With libname: All definitions go into namespace
2148
+ - embedded new_exit &exit {} -> mylib::exit is new_exit
2149
+ - Global exit remains unchanged
2150
+ - Access via libname::function(), libname::MyClass
2053
2151
 
2054
2152
  Usage in Python:
2055
2153
  cssl = CSSL.CsslLang()
@@ -2081,6 +2179,10 @@ class CSSLBuiltins:
2081
2179
  if hasattr(self.runtime, '_inline_payloads') and filepath in self.runtime._inline_payloads:
2082
2180
  source = self.runtime._inline_payloads[filepath]
2083
2181
  else:
2182
+ # v4.2.6: Auto-append .cssl-pl extension if not specified
2183
+ # payload("engine") -> looks for engine.cssl-pl
2184
+ original_filepath = filepath
2185
+
2084
2186
  # Handle absolute paths
2085
2187
  is_absolute = os.path.isabs(filepath) or (len(filepath) > 2 and filepath[1] == ':')
2086
2188
 
@@ -2090,42 +2192,124 @@ class CSSLBuiltins:
2090
2192
  if os.path.exists(cwd_path):
2091
2193
  filepath = cwd_path
2092
2194
  else:
2093
- # Fall back to cso_root for service context
2094
- filepath = self.builtin_cso_root(filepath)
2095
-
2096
- # Check file exists
2195
+ # Try with .cssl-pl extension
2196
+ cwd_path_pl = os.path.join(os.getcwd(), filepath + '.cssl-pl')
2197
+ if os.path.exists(cwd_path_pl):
2198
+ filepath = cwd_path_pl
2199
+ else:
2200
+ # Fall back to cso_root for service context
2201
+ filepath = self.builtin_cso_root(filepath)
2202
+
2203
+ # Check file exists, try with .cssl-pl extension if not
2097
2204
  if not os.path.exists(filepath):
2098
- raise CSSLBuiltinError(f"Payload file not found: {filepath}")
2205
+ # Try adding .cssl-pl extension
2206
+ filepath_with_ext = filepath + '.cssl-pl'
2207
+ if os.path.exists(filepath_with_ext):
2208
+ filepath = filepath_with_ext
2209
+ else:
2210
+ raise CSSLBuiltinError(f"Payload file not found: {original_filepath} (tried {filepath} and {filepath_with_ext})")
2099
2211
 
2100
2212
  # Check payload cache to prevent double loading
2101
2213
  if not hasattr(self.runtime, '_payload_cache'):
2102
2214
  self.runtime._payload_cache = set()
2103
2215
 
2104
- if filepath in self.runtime._payload_cache:
2216
+ # For namespaced payloads, use different cache key
2217
+ cache_key = f"{filepath}::{libname}" if libname else filepath
2218
+ if cache_key in self.runtime._payload_cache:
2105
2219
  return # Already loaded
2106
2220
 
2107
2221
  # Read the payload file
2108
2222
  with open(filepath, 'r', encoding='utf-8') as f:
2109
2223
  source = f.read()
2110
2224
 
2111
- self.runtime._payload_cache.add(filepath)
2225
+ self.runtime._payload_cache.add(cache_key)
2112
2226
 
2113
- # Parse and execute the payload in current scope
2114
- # This makes all globals, functions, and injections available
2227
+ # Parse and execute the payload
2115
2228
  try:
2116
2229
  from .cssl_parser import parse_cssl_program
2117
2230
 
2118
2231
  ast = parse_cssl_program(source)
2119
2232
 
2120
- # Execute the payload - this will:
2121
- # - Register global variables (accessible via @name)
2122
- # - Define functions in current scope
2123
- # - Set up any code injections
2124
- self.runtime._execute_node(ast)
2233
+ if libname:
2234
+ # Namespaced execution: execute in isolated scope
2235
+ self._execute_payload_namespaced(ast, libname, source)
2236
+ else:
2237
+ # Standard execution: apply globally
2238
+ # Execute the payload - this will:
2239
+ # - Register global variables (accessible via @name)
2240
+ # - Define functions in current scope
2241
+ # - Set up any code injections
2242
+ self.runtime._execute_node(ast)
2125
2243
 
2126
2244
  except Exception as e:
2127
2245
  raise CSSLBuiltinError(f"Failed to load payload '{filepath}': {e}")
2128
2246
 
2247
+ def _execute_payload_namespaced(self, ast, libname: str, source: str) -> None:
2248
+ """
2249
+ Execute payload in a namespace.
2250
+
2251
+ All definitions go into the namespace dict.
2252
+ embedded replacements are scoped to the namespace only.
2253
+ """
2254
+ # Create namespace dict
2255
+ namespace = {}
2256
+
2257
+ # Save current scope state
2258
+ old_scope = self.runtime.scope.copy()
2259
+ old_global = self.runtime.global_scope.copy()
2260
+
2261
+ # Track what gets added during payload execution
2262
+ scope_before = set(self.runtime.scope.keys())
2263
+ global_before = set(self.runtime.global_scope.keys())
2264
+
2265
+ # Set a flag to tell runtime we're in namespace mode
2266
+ self.runtime._payload_namespace_mode = libname
2267
+
2268
+ try:
2269
+ # Execute the payload
2270
+ self.runtime._execute_node(ast)
2271
+
2272
+ # Collect new definitions from scope
2273
+ for key in self.runtime.scope:
2274
+ if key not in scope_before:
2275
+ namespace[key] = self.runtime.scope[key]
2276
+
2277
+ # Collect new definitions from global_scope (but not the namespace itself)
2278
+ for key in self.runtime.global_scope:
2279
+ if key not in global_before and key != libname:
2280
+ namespace[key] = self.runtime.global_scope[key]
2281
+
2282
+ # Handle embedded replacements: move replaced targets into namespace
2283
+ if hasattr(self.runtime, '_namespace_replacements'):
2284
+ for target_name, new_impl in self.runtime._namespace_replacements.items():
2285
+ # Get the original from before execution
2286
+ original = old_scope.get(target_name) or old_global.get(target_name)
2287
+ if original is not None:
2288
+ # Restore original globally
2289
+ if target_name in old_scope:
2290
+ self.runtime.scope[target_name] = original
2291
+ if target_name in old_global:
2292
+ self.runtime.global_scope[target_name] = original
2293
+ # Put the replacement in namespace
2294
+ namespace[target_name] = new_impl
2295
+ del self.runtime._namespace_replacements
2296
+
2297
+ # Remove new definitions from global scope (they're in namespace now)
2298
+ for key in list(self.runtime.scope.keys()):
2299
+ if key not in scope_before:
2300
+ del self.runtime.scope[key]
2301
+
2302
+ for key in list(self.runtime.global_scope.keys()):
2303
+ if key not in global_before and key != libname:
2304
+ del self.runtime.global_scope[key]
2305
+
2306
+ finally:
2307
+ # Clear namespace mode flag
2308
+ self.runtime._payload_namespace_mode = None
2309
+
2310
+ # Register namespace in global scope
2311
+ self.runtime.global_scope[libname] = namespace
2312
+
2129
2313
  def builtin_get(self, source: Any, key: str = None) -> Any:
2130
2314
  """
2131
2315
  Get value from module, ServiceDefinition, or dict
@@ -2688,8 +2872,8 @@ class PythonizedCSSLInstance:
2688
2872
  return value
2689
2873
 
2690
2874
  # Check for method
2691
- method = instance.get_method(name)
2692
- if method is not None:
2875
+ if instance.has_method(name):
2876
+ method = instance.get_method(name)
2693
2877
  # Return a callable wrapper for the method
2694
2878
  return PythonizedMethod(instance, name, method, runtime)
2695
2879
 
@@ -2744,6 +2928,29 @@ class PythonizedMethod:
2744
2928
  if self._runtime is None:
2745
2929
  raise RuntimeError(f"Cannot call method '{self._method_name}' - no runtime available")
2746
2930
 
2931
+ # Validate argument count
2932
+ # Method AST structure: node.value is a dict with 'params' key
2933
+ method_info = getattr(self._method_ast, 'value', {}) or {}
2934
+ method_params = method_info.get('params', []) if isinstance(method_info, dict) else []
2935
+ param_names = [p.get('name', str(p)) if isinstance(p, dict) else (p.name if hasattr(p, 'name') else str(p)) for p in method_params]
2936
+ expected_count = len(param_names)
2937
+ actual_count = len(args)
2938
+
2939
+ if actual_count < expected_count:
2940
+ missing = param_names[actual_count:]
2941
+ class_name = self._instance._class.name
2942
+ raise TypeError(
2943
+ f"{class_name}.{self._method_name}() missing {len(missing)} required argument(s): {', '.join(repr(p) for p in missing)}\n"
2944
+ f" Expected: {self._method_name}({', '.join(param_names)})\n"
2945
+ f" Got: {self._method_name}({', '.join(repr(a) for a in args)})"
2946
+ )
2947
+ elif actual_count > expected_count:
2948
+ class_name = self._instance._class.name
2949
+ raise TypeError(
2950
+ f"{class_name}.{self._method_name}() takes {expected_count} argument(s) but {actual_count} were given\n"
2951
+ f" Expected: {self._method_name}({', '.join(param_names)})"
2952
+ )
2953
+
2747
2954
  # Execute the method through the runtime
2748
2955
  # Pass the method AST node, not the method name
2749
2956
  result = self._runtime._call_method(self._instance, self._method_ast, list(args), kwargs)