IncludeCPP 4.2.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.
- includecpp/CHANGELOG.md +82 -115
- includecpp/DOCUMENTATION.md +208 -355
- includecpp/__init__.py +1 -1
- includecpp/cli/commands.py +2 -2
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +1505 -1467
- includecpp/core/cssl/cssl_builtins.py +129 -19
- includecpp/core/cssl/cssl_parser.py +539 -29
- includecpp/core/cssl/cssl_runtime.py +467 -33
- includecpp/core/cssl/cssl_types.py +189 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +38 -2
- includecpp-4.3.0.dist-info/METADATA +277 -0
- {includecpp-4.2.2.dist-info → includecpp-4.3.0.dist-info}/RECORD +16 -16
- includecpp-4.2.2.dist-info/METADATA +0 -1008
- {includecpp-4.2.2.dist-info → includecpp-4.3.0.dist-info}/WHEEL +0 -0
- {includecpp-4.2.2.dist-info → includecpp-4.3.0.dist-info}/entry_points.txt +0 -0
- {includecpp-4.2.2.dist-info → includecpp-4.3.0.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.2.2.dist-info → includecpp-4.3.0.dist-info}/top_level.txt +0 -0
|
@@ -350,22 +350,35 @@ class CSSLBuiltins:
|
|
|
350
350
|
|
|
351
351
|
def builtin_print(self, *args, **kwargs) -> None:
|
|
352
352
|
"""Print without newline"""
|
|
353
|
+
import sys
|
|
353
354
|
sep = kwargs.get('sep', ' ')
|
|
354
355
|
end = kwargs.get('end', '')
|
|
355
356
|
output = sep.join(str(a) for a in args) + end
|
|
356
357
|
if self.runtime and hasattr(self.runtime, 'output'):
|
|
357
358
|
self.runtime.output(output)
|
|
358
359
|
else:
|
|
359
|
-
|
|
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='')
|
|
360
366
|
|
|
361
367
|
def builtin_println(self, *args, **kwargs) -> None:
|
|
362
368
|
"""Print with newline"""
|
|
369
|
+
import sys
|
|
363
370
|
sep = kwargs.get('sep', ' ')
|
|
364
371
|
output = sep.join(str(a) for a in args)
|
|
365
372
|
if self.runtime and hasattr(self.runtime, 'output'):
|
|
366
373
|
self.runtime.output(output + '\n')
|
|
367
374
|
else:
|
|
368
|
-
|
|
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'))
|
|
369
382
|
|
|
370
383
|
def builtin_input(self, prompt: str = "") -> str:
|
|
371
384
|
"""Read user input from console.
|
|
@@ -2111,9 +2124,9 @@ class CSSLBuiltins:
|
|
|
2111
2124
|
|
|
2112
2125
|
return CppModuleStub(name, source)
|
|
2113
2126
|
|
|
2114
|
-
def builtin_payload(self, filepath: str) -> None:
|
|
2127
|
+
def builtin_payload(self, filepath: str, libname: str = None) -> None:
|
|
2115
2128
|
"""
|
|
2116
|
-
Load a CSSL payload file (.cssl-pl) and execute it
|
|
2129
|
+
Load a CSSL payload file (.cssl-pl) and execute it.
|
|
2117
2130
|
|
|
2118
2131
|
Payloads are like header files but for CSSL:
|
|
2119
2132
|
- Define global variables (accessible via @name)
|
|
@@ -2122,8 +2135,19 @@ class CSSLBuiltins:
|
|
|
2122
2135
|
- Set configuration values
|
|
2123
2136
|
|
|
2124
2137
|
Usage in .cssl file:
|
|
2138
|
+
// Standard: everything is applied globally
|
|
2125
2139
|
payload("myconfig.cssl-pl");
|
|
2126
|
-
|
|
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
|
|
2127
2151
|
|
|
2128
2152
|
Usage in Python:
|
|
2129
2153
|
cssl = CSSL.CsslLang()
|
|
@@ -2155,6 +2179,10 @@ class CSSLBuiltins:
|
|
|
2155
2179
|
if hasattr(self.runtime, '_inline_payloads') and filepath in self.runtime._inline_payloads:
|
|
2156
2180
|
source = self.runtime._inline_payloads[filepath]
|
|
2157
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
|
+
|
|
2158
2186
|
# Handle absolute paths
|
|
2159
2187
|
is_absolute = os.path.isabs(filepath) or (len(filepath) > 2 and filepath[1] == ':')
|
|
2160
2188
|
|
|
@@ -2164,42 +2192,124 @@ class CSSLBuiltins:
|
|
|
2164
2192
|
if os.path.exists(cwd_path):
|
|
2165
2193
|
filepath = cwd_path
|
|
2166
2194
|
else:
|
|
2167
|
-
#
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
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
|
|
2171
2204
|
if not os.path.exists(filepath):
|
|
2172
|
-
|
|
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})")
|
|
2173
2211
|
|
|
2174
2212
|
# Check payload cache to prevent double loading
|
|
2175
2213
|
if not hasattr(self.runtime, '_payload_cache'):
|
|
2176
2214
|
self.runtime._payload_cache = set()
|
|
2177
2215
|
|
|
2178
|
-
|
|
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:
|
|
2179
2219
|
return # Already loaded
|
|
2180
2220
|
|
|
2181
2221
|
# Read the payload file
|
|
2182
2222
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
2183
2223
|
source = f.read()
|
|
2184
2224
|
|
|
2185
|
-
self.runtime._payload_cache.add(
|
|
2225
|
+
self.runtime._payload_cache.add(cache_key)
|
|
2186
2226
|
|
|
2187
|
-
# Parse and execute the payload
|
|
2188
|
-
# This makes all globals, functions, and injections available
|
|
2227
|
+
# Parse and execute the payload
|
|
2189
2228
|
try:
|
|
2190
2229
|
from .cssl_parser import parse_cssl_program
|
|
2191
2230
|
|
|
2192
2231
|
ast = parse_cssl_program(source)
|
|
2193
2232
|
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
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)
|
|
2199
2243
|
|
|
2200
2244
|
except Exception as e:
|
|
2201
2245
|
raise CSSLBuiltinError(f"Failed to load payload '{filepath}': {e}")
|
|
2202
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
|
+
|
|
2203
2313
|
def builtin_get(self, source: Any, key: str = None) -> Any:
|
|
2204
2314
|
"""
|
|
2205
2315
|
Get value from module, ServiceDefinition, or dict
|