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
|
@@ -15,7 +15,7 @@ from .cssl_modules import get_module_registry, get_standard_module
|
|
|
15
15
|
from .cssl_types import (
|
|
16
16
|
Parameter, DataStruct, Shuffled, Iterator, Combo,
|
|
17
17
|
Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
|
|
18
|
-
CSSLClass, CSSLInstance
|
|
18
|
+
CSSLClass, CSSLInstance, ByteArrayed
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
|
|
@@ -532,6 +532,10 @@ class CSSLRuntime:
|
|
|
532
532
|
self._exec_struct(child)
|
|
533
533
|
elif child.type == 'class':
|
|
534
534
|
self._exec_class(child)
|
|
535
|
+
elif child.type == 'enum':
|
|
536
|
+
self._exec_enum(child)
|
|
537
|
+
elif child.type == 'bytearrayed':
|
|
538
|
+
self._exec_bytearrayed(child)
|
|
535
539
|
elif child.type == 'function':
|
|
536
540
|
self._exec_function(child)
|
|
537
541
|
elif child.type == 'global_assignment':
|
|
@@ -813,6 +817,74 @@ class CSSLRuntime:
|
|
|
813
817
|
|
|
814
818
|
return struct_data
|
|
815
819
|
|
|
820
|
+
def _exec_enum(self, node: ASTNode) -> Dict[str, Any]:
|
|
821
|
+
"""Execute enum declaration - registers enum values in scope.
|
|
822
|
+
|
|
823
|
+
Creates a dictionary-like enum object accessible via EnumName::VALUE syntax.
|
|
824
|
+
|
|
825
|
+
Example:
|
|
826
|
+
enum Colors { RED, GREEN, BLUE }
|
|
827
|
+
Colors::RED // returns 0
|
|
828
|
+
Colors::GREEN // returns 1
|
|
829
|
+
"""
|
|
830
|
+
enum_info = node.value
|
|
831
|
+
enum_name = enum_info.get('name')
|
|
832
|
+
members = enum_info.get('members', [])
|
|
833
|
+
|
|
834
|
+
# Create enum object as a dict-like object with members
|
|
835
|
+
enum_obj = {}
|
|
836
|
+
for member in members:
|
|
837
|
+
member_name = member['name']
|
|
838
|
+
member_value = member['value']
|
|
839
|
+
enum_obj[member_name] = member_value
|
|
840
|
+
|
|
841
|
+
# Register the enum in scope (accessible as EnumName::VALUE)
|
|
842
|
+
self.scope.set(enum_name, enum_obj)
|
|
843
|
+
self.global_scope.set(enum_name, enum_obj)
|
|
844
|
+
|
|
845
|
+
return enum_obj
|
|
846
|
+
|
|
847
|
+
def _exec_bytearrayed(self, node: ASTNode) -> 'ByteArrayed':
|
|
848
|
+
"""Execute bytearrayed declaration - function-to-byte mapping with pattern matching.
|
|
849
|
+
|
|
850
|
+
Creates a ByteArrayed object that:
|
|
851
|
+
- Maps function references to byte positions (0x0, 0x1, etc.)
|
|
852
|
+
- Executes functions "invisibly" when called to get return values
|
|
853
|
+
- Matches case patterns based on return values
|
|
854
|
+
- Supports indexing: MyBytes["0x0"] or MyBytes[0]
|
|
855
|
+
|
|
856
|
+
Example:
|
|
857
|
+
bytearrayed MyBytes {
|
|
858
|
+
&func1; // 0x0
|
|
859
|
+
&func2; // 0x1
|
|
860
|
+
case {0, 1} { // func1=0, func2=1
|
|
861
|
+
printl("Match!");
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
MyBytes(); // Execute pattern matching
|
|
865
|
+
x = MyBytes["0x0"]; // Get value at position 0
|
|
866
|
+
"""
|
|
867
|
+
info = node.value
|
|
868
|
+
name = info.get('name')
|
|
869
|
+
func_refs = info.get('func_refs', [])
|
|
870
|
+
cases = info.get('cases', [])
|
|
871
|
+
default_block = info.get('default')
|
|
872
|
+
|
|
873
|
+
# Create ByteArrayed object
|
|
874
|
+
bytearrayed_obj = ByteArrayed(
|
|
875
|
+
name=name,
|
|
876
|
+
func_refs=func_refs,
|
|
877
|
+
cases=cases,
|
|
878
|
+
default_block=default_block,
|
|
879
|
+
runtime=self
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
# Register in scope
|
|
883
|
+
self.scope.set(name, bytearrayed_obj)
|
|
884
|
+
self.global_scope.set(name, bytearrayed_obj)
|
|
885
|
+
|
|
886
|
+
return bytearrayed_obj
|
|
887
|
+
|
|
816
888
|
def _exec_class(self, node: ASTNode) -> CSSLClass:
|
|
817
889
|
"""Execute class definition - registers class in scope.
|
|
818
890
|
|
|
@@ -942,6 +1014,22 @@ class CSSLRuntime:
|
|
|
942
1014
|
self.global_scope.set(class_name, class_def)
|
|
943
1015
|
self._promoted_globals[class_name] = class_def
|
|
944
1016
|
|
|
1017
|
+
# v4.2.5: Handle &target replacement for classes
|
|
1018
|
+
# embedded class MyClass &$Target { } - immediate replacement
|
|
1019
|
+
# class MyClass &$Target { } - deferred until instantiation
|
|
1020
|
+
append_ref_class = class_info.get('append_ref_class')
|
|
1021
|
+
is_embedded = class_info.get('is_embedded', False)
|
|
1022
|
+
if append_ref_class and is_embedded:
|
|
1023
|
+
append_ref_member = class_info.get('append_ref_member')
|
|
1024
|
+
self._overwrite_class_target(append_ref_class, append_ref_member, class_def)
|
|
1025
|
+
class_def._target_applied = True
|
|
1026
|
+
elif append_ref_class:
|
|
1027
|
+
# Store reference info for deferred replacement
|
|
1028
|
+
class_def._pending_target = {
|
|
1029
|
+
'append_ref_class': append_ref_class,
|
|
1030
|
+
'append_ref_member': class_info.get('append_ref_member')
|
|
1031
|
+
}
|
|
1032
|
+
|
|
945
1033
|
# Handle class overwrites - replace methods in target class
|
|
946
1034
|
if overwrites_class_name:
|
|
947
1035
|
self._apply_class_overwrites(
|
|
@@ -1164,13 +1252,18 @@ class CSSLRuntime:
|
|
|
1164
1252
|
# Handle &Class::method syntax
|
|
1165
1253
|
# Without ++ = full replacement
|
|
1166
1254
|
# With ++ = append (run original first, then new code)
|
|
1167
|
-
if
|
|
1255
|
+
# v4.2.5: Only do immediate replacement if is_embedded=True
|
|
1256
|
+
# For regular 'define', replacement is deferred until function is called
|
|
1257
|
+
is_embedded = func_info.get('is_embedded', False)
|
|
1258
|
+
if append_ref_class and is_embedded:
|
|
1168
1259
|
if append_mode:
|
|
1169
1260
|
# Append mode: wrap original to run original + new
|
|
1170
1261
|
self._append_to_target(append_ref_class, append_ref_member, node)
|
|
1171
1262
|
else:
|
|
1172
1263
|
# Full replacement
|
|
1173
1264
|
self._overwrite_target(append_ref_class, append_ref_member, node)
|
|
1265
|
+
# Mark as already applied so we don't apply again on call
|
|
1266
|
+
node.value['_target_applied'] = True
|
|
1174
1267
|
|
|
1175
1268
|
# Handle overwrites keyword - replace the target function
|
|
1176
1269
|
if overwrites_func:
|
|
@@ -1215,6 +1308,34 @@ class CSSLRuntime:
|
|
|
1215
1308
|
return self._call_function(func_node, list(args), kwargs)
|
|
1216
1309
|
return wrapper
|
|
1217
1310
|
|
|
1311
|
+
def _create_python_method_wrapper(self, func_node: ASTNode, python_obj):
|
|
1312
|
+
"""Create a Python-callable wrapper that passes python_obj as 'this'.
|
|
1313
|
+
|
|
1314
|
+
v4.3.0: Used when replacing Python object methods with CSSL functions.
|
|
1315
|
+
The wrapper ensures 'this->' in CSSL refers to the Python object.
|
|
1316
|
+
"""
|
|
1317
|
+
runtime = self
|
|
1318
|
+
_captured_obj = python_obj
|
|
1319
|
+
|
|
1320
|
+
def wrapper(*args, **kwargs):
|
|
1321
|
+
# Save current instance context
|
|
1322
|
+
old_instance = runtime._current_instance
|
|
1323
|
+
old_this = runtime.scope.get('this')
|
|
1324
|
+
try:
|
|
1325
|
+
# Set Python object as current instance for this-> access
|
|
1326
|
+
runtime._current_instance = _captured_obj
|
|
1327
|
+
runtime.scope.set('this', _captured_obj)
|
|
1328
|
+
return runtime._call_function(func_node, list(args), kwargs)
|
|
1329
|
+
finally:
|
|
1330
|
+
# Restore previous context
|
|
1331
|
+
runtime._current_instance = old_instance
|
|
1332
|
+
if old_this is not None:
|
|
1333
|
+
runtime.scope.set('this', old_this)
|
|
1334
|
+
elif 'this' in runtime.scope.variables:
|
|
1335
|
+
del runtime.scope.variables['this']
|
|
1336
|
+
|
|
1337
|
+
return wrapper
|
|
1338
|
+
|
|
1218
1339
|
def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
|
|
1219
1340
|
"""Overwrite a class method or function with the replacement.
|
|
1220
1341
|
|
|
@@ -1222,9 +1343,25 @@ class CSSLRuntime:
|
|
|
1222
1343
|
- &ClassName::method - Overwrite method in CSSL class
|
|
1223
1344
|
- &$PyObject.method - Overwrite method in Python shared object
|
|
1224
1345
|
- &functionName - Overwrite standalone function
|
|
1346
|
+
|
|
1347
|
+
v4.2.6: In namespace mode (_payload_namespace_mode), track replacements
|
|
1348
|
+
instead of applying globally. The namespace handler will scope them.
|
|
1225
1349
|
"""
|
|
1226
1350
|
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1227
1351
|
|
|
1352
|
+
# v4.2.6: Check for namespace mode
|
|
1353
|
+
namespace_mode = getattr(self, '_payload_namespace_mode', None)
|
|
1354
|
+
if namespace_mode and ref_member is None and not ref_class.startswith('$'):
|
|
1355
|
+
# Standalone function replacement in namespace mode
|
|
1356
|
+
# Track it for the namespace handler
|
|
1357
|
+
if not hasattr(self, '_namespace_replacements'):
|
|
1358
|
+
self._namespace_replacements = {}
|
|
1359
|
+
self._namespace_replacements[ref_class] = replacement_node
|
|
1360
|
+
# Still register the function normally (namespace handler will move it)
|
|
1361
|
+
self.scope.set(ref_class, replacement_node)
|
|
1362
|
+
self.global_scope.set(ref_class, replacement_node)
|
|
1363
|
+
return
|
|
1364
|
+
|
|
1228
1365
|
# Handle Python shared objects
|
|
1229
1366
|
if ref_class.startswith('$'):
|
|
1230
1367
|
var_name = ref_class[1:]
|
|
@@ -1241,7 +1378,8 @@ class CSSLRuntime:
|
|
|
1241
1378
|
|
|
1242
1379
|
# Overwrite Python object method
|
|
1243
1380
|
if ref_member and hasattr(ref_obj, ref_member):
|
|
1244
|
-
wrapper
|
|
1381
|
+
# v4.3.0: Create wrapper that passes Python object as 'this' context
|
|
1382
|
+
wrapper = self._create_python_method_wrapper(replacement_node, ref_obj)
|
|
1245
1383
|
try:
|
|
1246
1384
|
setattr(ref_obj, ref_member, wrapper)
|
|
1247
1385
|
except (AttributeError, TypeError):
|
|
@@ -1250,12 +1388,49 @@ class CSSLRuntime:
|
|
|
1250
1388
|
|
|
1251
1389
|
# Handle CSSL class method overwrite
|
|
1252
1390
|
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1391
|
+
|
|
1392
|
+
# v4.2.3: Handle standalone function reference (&functionName) including builtins
|
|
1393
|
+
if ref_member is None:
|
|
1394
|
+
# Check if this is a builtin function
|
|
1395
|
+
is_builtin = hasattr(self, 'builtins') and self.builtins.has_function(ref_class)
|
|
1396
|
+
|
|
1397
|
+
# Check if target_class is NOT a CSSLClass (it's either a function/builtin or None)
|
|
1398
|
+
is_cssl_class = isinstance(target_class, CSSLClass)
|
|
1399
|
+
# v4.2.5: Also check if target is a CSSL function (ASTNode with type 'function')
|
|
1400
|
+
is_cssl_function = (hasattr(target_class, 'type') and target_class.type == 'function')
|
|
1401
|
+
|
|
1402
|
+
if is_builtin or (target_class is None) or (not is_cssl_class and callable(target_class)) or is_cssl_function:
|
|
1403
|
+
# v4.2.3: CRITICAL - Store original BEFORE overwriting for %name captures
|
|
1404
|
+
# This ensures %exit() refers to the ORIGINAL exit, not the replacement
|
|
1405
|
+
if ref_class not in self._original_functions:
|
|
1406
|
+
if is_builtin:
|
|
1407
|
+
# Store the original builtin from _functions dict
|
|
1408
|
+
# (handles aliases like printl -> builtin_println)
|
|
1409
|
+
original_builtin = self.builtins._functions.get(ref_class)
|
|
1410
|
+
if original_builtin is not None:
|
|
1411
|
+
self._original_functions[ref_class] = original_builtin
|
|
1412
|
+
elif is_cssl_function:
|
|
1413
|
+
# v4.2.5: Store original CSSL function (ASTNode)
|
|
1414
|
+
self._original_functions[ref_class] = target_class
|
|
1415
|
+
elif target_class is not None and callable(target_class):
|
|
1416
|
+
# Store the original function/callable
|
|
1417
|
+
self._original_functions[ref_class] = target_class
|
|
1418
|
+
|
|
1419
|
+
# &functionName - overwrite the function/builtin
|
|
1257
1420
|
self.scope.set(ref_class, replacement_node)
|
|
1258
1421
|
self.global_scope.set(ref_class, replacement_node)
|
|
1422
|
+
|
|
1423
|
+
# Also overwrite in builtins dict if it's a builtin
|
|
1424
|
+
if is_builtin:
|
|
1425
|
+
# Create wrapper that calls CSSL function instead of builtin
|
|
1426
|
+
def make_wrapper(node, runtime):
|
|
1427
|
+
def wrapper(*args, **kwargs):
|
|
1428
|
+
return runtime._call_function(node, list(args), kwargs)
|
|
1429
|
+
return wrapper
|
|
1430
|
+
self.builtins._functions[ref_class] = make_wrapper(replacement_node, self)
|
|
1431
|
+
return
|
|
1432
|
+
|
|
1433
|
+
if target_class is None:
|
|
1259
1434
|
return
|
|
1260
1435
|
|
|
1261
1436
|
if isinstance(target_class, CSSLClass) and ref_member:
|
|
@@ -1357,6 +1532,58 @@ class CSSLRuntime:
|
|
|
1357
1532
|
target_class.members[i] = append_node
|
|
1358
1533
|
break
|
|
1359
1534
|
|
|
1535
|
+
def _overwrite_class_target(self, ref_class: str, ref_member: str, replacement_class: 'CSSLClass'):
|
|
1536
|
+
"""Overwrite a target class with the replacement class.
|
|
1537
|
+
|
|
1538
|
+
v4.2.5: Used by 'embedded class' with &target syntax.
|
|
1539
|
+
Handles:
|
|
1540
|
+
- &ClassName - Overwrite CSSL class
|
|
1541
|
+
- &$PyObject - Overwrite Python shared object
|
|
1542
|
+
|
|
1543
|
+
v4.2.6: In namespace mode, track replacements instead of applying globally.
|
|
1544
|
+
"""
|
|
1545
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1546
|
+
|
|
1547
|
+
# v4.2.6: Check for namespace mode
|
|
1548
|
+
namespace_mode = getattr(self, '_payload_namespace_mode', None)
|
|
1549
|
+
if namespace_mode and not ref_class.startswith('$') and not ref_class.startswith('@'):
|
|
1550
|
+
# Class replacement in namespace mode
|
|
1551
|
+
# Track it for the namespace handler
|
|
1552
|
+
if not hasattr(self, '_namespace_replacements'):
|
|
1553
|
+
self._namespace_replacements = {}
|
|
1554
|
+
self._namespace_replacements[ref_class] = replacement_class
|
|
1555
|
+
# Still register the class normally (namespace handler will move it)
|
|
1556
|
+
self.scope.set(ref_class, replacement_class)
|
|
1557
|
+
self.global_scope.set(ref_class, replacement_class)
|
|
1558
|
+
return
|
|
1559
|
+
|
|
1560
|
+
# Handle Python shared objects
|
|
1561
|
+
if ref_class.startswith('$'):
|
|
1562
|
+
var_name = ref_class[1:]
|
|
1563
|
+
# Replace the shared object with the new class
|
|
1564
|
+
_live_objects[var_name] = replacement_class
|
|
1565
|
+
self.scope.set(var_name, replacement_class)
|
|
1566
|
+
self.global_scope.set(var_name, replacement_class)
|
|
1567
|
+
return
|
|
1568
|
+
|
|
1569
|
+
# Handle @ prefix (global reference)
|
|
1570
|
+
if ref_class.startswith('@'):
|
|
1571
|
+
var_name = ref_class[1:]
|
|
1572
|
+
self.global_scope.set(var_name, replacement_class)
|
|
1573
|
+
self._promoted_globals[var_name] = replacement_class
|
|
1574
|
+
return
|
|
1575
|
+
|
|
1576
|
+
# Handle regular class reference
|
|
1577
|
+
target_class = self.scope.get(ref_class)
|
|
1578
|
+
if target_class is None:
|
|
1579
|
+
target_class = self.global_scope.get(ref_class)
|
|
1580
|
+
|
|
1581
|
+
# Replace the class definition
|
|
1582
|
+
self.scope.set(ref_class, replacement_class)
|
|
1583
|
+
self.global_scope.set(ref_class, replacement_class)
|
|
1584
|
+
if ref_class in self._promoted_globals:
|
|
1585
|
+
self._promoted_globals[ref_class] = replacement_class
|
|
1586
|
+
|
|
1360
1587
|
def _exec_typed_declaration(self, node: ASTNode) -> Any:
|
|
1361
1588
|
"""Execute typed variable declaration: type<T> varName = value;
|
|
1362
1589
|
|
|
@@ -1638,6 +1865,18 @@ class CSSLRuntime:
|
|
|
1638
1865
|
modifiers = func_info.get('modifiers', [])
|
|
1639
1866
|
kwargs = kwargs or {}
|
|
1640
1867
|
|
|
1868
|
+
# v4.2.5: Deferred &target replacement for non-embedded functions
|
|
1869
|
+
# If function has &target and hasn't been applied yet, apply now on first call
|
|
1870
|
+
append_ref_class = func_info.get('append_ref_class')
|
|
1871
|
+
if append_ref_class and not func_info.get('_target_applied', False):
|
|
1872
|
+
append_mode = func_info.get('append_mode', False)
|
|
1873
|
+
append_ref_member = func_info.get('append_ref_member')
|
|
1874
|
+
if append_mode:
|
|
1875
|
+
self._append_to_target(append_ref_class, append_ref_member, func_node)
|
|
1876
|
+
else:
|
|
1877
|
+
self._overwrite_target(append_ref_class, append_ref_member, func_node)
|
|
1878
|
+
func_node.value['_target_applied'] = True
|
|
1879
|
+
|
|
1641
1880
|
# Check for undefined modifier - suppress errors if present
|
|
1642
1881
|
is_undefined = 'undefined' in modifiers
|
|
1643
1882
|
|
|
@@ -2012,6 +2251,101 @@ class CSSLRuntime:
|
|
|
2012
2251
|
|
|
2013
2252
|
return None
|
|
2014
2253
|
|
|
2254
|
+
def _exec_param_switch(self, node: ASTNode) -> Any:
|
|
2255
|
+
"""Execute param switch statement for open parameters.
|
|
2256
|
+
|
|
2257
|
+
v4.2.5: Switch on which parameters were provided.
|
|
2258
|
+
|
|
2259
|
+
Syntax:
|
|
2260
|
+
switch(Params) {
|
|
2261
|
+
case name: // if 'name' param exists
|
|
2262
|
+
case name & age: // if both exist
|
|
2263
|
+
case name & not age: // if 'name' exists but 'age' doesn't
|
|
2264
|
+
except name: // if 'name' does NOT exist
|
|
2265
|
+
default: // fallback
|
|
2266
|
+
always: // always runs after match
|
|
2267
|
+
finally: // cleanup, always runs
|
|
2268
|
+
}
|
|
2269
|
+
"""
|
|
2270
|
+
params_name = node.value.get('params')
|
|
2271
|
+
open_kwargs = self.scope.get('_OpenKwargs') or {}
|
|
2272
|
+
|
|
2273
|
+
matched = False
|
|
2274
|
+
always_node = None
|
|
2275
|
+
finally_node = None
|
|
2276
|
+
default_node = None
|
|
2277
|
+
|
|
2278
|
+
# First pass: find always, finally, default nodes
|
|
2279
|
+
for child in node.children:
|
|
2280
|
+
if child.type == 'param_always':
|
|
2281
|
+
always_node = child
|
|
2282
|
+
elif child.type == 'param_finally':
|
|
2283
|
+
finally_node = child
|
|
2284
|
+
elif child.type == 'param_default':
|
|
2285
|
+
default_node = child
|
|
2286
|
+
|
|
2287
|
+
try:
|
|
2288
|
+
# Execute matching cases
|
|
2289
|
+
for child in node.children:
|
|
2290
|
+
if child.type == 'param_case':
|
|
2291
|
+
condition = child.value.get('condition')
|
|
2292
|
+
if self._eval_param_condition(condition, open_kwargs):
|
|
2293
|
+
matched = True
|
|
2294
|
+
try:
|
|
2295
|
+
for stmt in child.children:
|
|
2296
|
+
self._execute_node(stmt)
|
|
2297
|
+
except CSSLBreak:
|
|
2298
|
+
break
|
|
2299
|
+
|
|
2300
|
+
# Execute default if no match
|
|
2301
|
+
if not matched and default_node:
|
|
2302
|
+
try:
|
|
2303
|
+
for stmt in default_node.children:
|
|
2304
|
+
self._execute_node(stmt)
|
|
2305
|
+
except CSSLBreak:
|
|
2306
|
+
pass
|
|
2307
|
+
|
|
2308
|
+
# Execute always block (runs after a match, before finally)
|
|
2309
|
+
if matched and always_node:
|
|
2310
|
+
try:
|
|
2311
|
+
for stmt in always_node.children:
|
|
2312
|
+
self._execute_node(stmt)
|
|
2313
|
+
except CSSLBreak:
|
|
2314
|
+
pass
|
|
2315
|
+
|
|
2316
|
+
finally:
|
|
2317
|
+
# Execute finally block (always runs, even on break/exception)
|
|
2318
|
+
if finally_node:
|
|
2319
|
+
for stmt in finally_node.children:
|
|
2320
|
+
self._execute_node(stmt)
|
|
2321
|
+
|
|
2322
|
+
return None
|
|
2323
|
+
|
|
2324
|
+
def _eval_param_condition(self, condition: dict, kwargs: dict) -> bool:
|
|
2325
|
+
"""Evaluate a param switch condition.
|
|
2326
|
+
|
|
2327
|
+
Condition types:
|
|
2328
|
+
{'type': 'exists', 'param': 'name'} -> 'name' in kwargs
|
|
2329
|
+
{'type': 'not', 'param': 'name'} -> 'name' not in kwargs
|
|
2330
|
+
{'type': 'and', 'left': {...}, 'right': {...}} -> left AND right
|
|
2331
|
+
"""
|
|
2332
|
+
cond_type = condition.get('type')
|
|
2333
|
+
|
|
2334
|
+
if cond_type == 'exists':
|
|
2335
|
+
param = condition.get('param')
|
|
2336
|
+
return param in kwargs
|
|
2337
|
+
|
|
2338
|
+
elif cond_type == 'not':
|
|
2339
|
+
param = condition.get('param')
|
|
2340
|
+
return param not in kwargs
|
|
2341
|
+
|
|
2342
|
+
elif cond_type == 'and':
|
|
2343
|
+
left = self._eval_param_condition(condition.get('left'), kwargs)
|
|
2344
|
+
right = self._eval_param_condition(condition.get('right'), kwargs)
|
|
2345
|
+
return left and right
|
|
2346
|
+
|
|
2347
|
+
return False
|
|
2348
|
+
|
|
2015
2349
|
def _exec_return(self, node: ASTNode) -> Any:
|
|
2016
2350
|
"""Execute return statement.
|
|
2017
2351
|
|
|
@@ -2114,7 +2448,7 @@ class CSSLRuntime:
|
|
|
2114
2448
|
return None
|
|
2115
2449
|
|
|
2116
2450
|
def _exec_try(self, node: ASTNode) -> Any:
|
|
2117
|
-
"""Execute try/catch block"""
|
|
2451
|
+
"""Execute try/catch/finally block"""
|
|
2118
2452
|
try:
|
|
2119
2453
|
for child in node.children:
|
|
2120
2454
|
if child.type == 'try-block':
|
|
@@ -2128,6 +2462,21 @@ class CSSLRuntime:
|
|
|
2128
2462
|
self.scope.set(error_var, str(e))
|
|
2129
2463
|
for stmt in child.children:
|
|
2130
2464
|
self._execute_node(stmt)
|
|
2465
|
+
except Exception as e:
|
|
2466
|
+
# v4.2.6: Also catch Python exceptions
|
|
2467
|
+
for child in node.children:
|
|
2468
|
+
if child.type == 'catch-block':
|
|
2469
|
+
error_var = child.value.get('error_var') if child.value else None
|
|
2470
|
+
if error_var:
|
|
2471
|
+
self.scope.set(error_var, str(e))
|
|
2472
|
+
for stmt in child.children:
|
|
2473
|
+
self._execute_node(stmt)
|
|
2474
|
+
finally:
|
|
2475
|
+
# v4.2.6: Execute finally block if present
|
|
2476
|
+
for child in node.children:
|
|
2477
|
+
if child.type == 'finally-block':
|
|
2478
|
+
for stmt in child.children:
|
|
2479
|
+
self._execute_node(stmt)
|
|
2131
2480
|
|
|
2132
2481
|
return None
|
|
2133
2482
|
|
|
@@ -2831,6 +3180,15 @@ class CSSLRuntime:
|
|
|
2831
3180
|
final_value = {**current_value, **source}
|
|
2832
3181
|
elif isinstance(current_value, str) and isinstance(source, str):
|
|
2833
3182
|
final_value = current_value + source
|
|
3183
|
+
# Handle CSSL container types (DataStruct, Vector, Stack, etc.)
|
|
3184
|
+
elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add'):
|
|
3185
|
+
if hasattr(current_value, 'append'):
|
|
3186
|
+
current_value.append(source)
|
|
3187
|
+
elif hasattr(current_value, 'push'):
|
|
3188
|
+
current_value.push(source)
|
|
3189
|
+
elif hasattr(current_value, 'add'):
|
|
3190
|
+
current_value.add(source)
|
|
3191
|
+
final_value = current_value
|
|
2834
3192
|
elif current_value is None:
|
|
2835
3193
|
final_value = [source] if not isinstance(source, list) else source
|
|
2836
3194
|
else:
|
|
@@ -3353,6 +3711,31 @@ class CSSLRuntime:
|
|
|
3353
3711
|
|
|
3354
3712
|
if node.type == 'identifier':
|
|
3355
3713
|
name = node.value
|
|
3714
|
+
|
|
3715
|
+
# Handle enum/namespace access: Colors::RED, MyNamespace::func
|
|
3716
|
+
if '::' in name:
|
|
3717
|
+
parts = name.split('::', 1)
|
|
3718
|
+
container_name = parts[0]
|
|
3719
|
+
member_name = parts[1]
|
|
3720
|
+
|
|
3721
|
+
# Look up the container (enum, class, or namespace)
|
|
3722
|
+
container = self.scope.get(container_name)
|
|
3723
|
+
if container is None:
|
|
3724
|
+
container = self.global_scope.get(container_name)
|
|
3725
|
+
if container is None:
|
|
3726
|
+
container = self._promoted_globals.get(container_name)
|
|
3727
|
+
|
|
3728
|
+
if container is not None:
|
|
3729
|
+
# If it's a dict-like object (enum or namespace), get the member
|
|
3730
|
+
if isinstance(container, dict):
|
|
3731
|
+
return container.get(member_name)
|
|
3732
|
+
# If it's an object with the member as an attribute
|
|
3733
|
+
elif hasattr(container, member_name):
|
|
3734
|
+
return getattr(container, member_name)
|
|
3735
|
+
|
|
3736
|
+
# Fall through to normal lookup if container not found
|
|
3737
|
+
return None
|
|
3738
|
+
|
|
3356
3739
|
value = self.scope.get(name)
|
|
3357
3740
|
# Check if it's a class member in current instance context
|
|
3358
3741
|
# This allows accessing members without 'this->' inside methods
|
|
@@ -3426,14 +3809,22 @@ class CSSLRuntime:
|
|
|
3426
3809
|
|
|
3427
3810
|
if node.type == 'captured_ref':
|
|
3428
3811
|
# %<name> captured reference - use value captured at infusion registration time
|
|
3812
|
+
# Priority: The % prefix means "get the ORIGINAL value before any replacement"
|
|
3429
3813
|
name = node.value
|
|
3430
|
-
|
|
3814
|
+
|
|
3815
|
+
# 1. First check captured values from current injection context
|
|
3431
3816
|
if name in self._current_captured_values:
|
|
3432
3817
|
captured_value = self._current_captured_values[name]
|
|
3433
|
-
# Only use captured value if it's not None
|
|
3434
3818
|
if captured_value is not None:
|
|
3435
3819
|
return captured_value
|
|
3436
|
-
|
|
3820
|
+
|
|
3821
|
+
# 2. v4.2.3: Check _original_functions FIRST - this is the pre-replacement value
|
|
3822
|
+
# This ensures %exit() refers to the ORIGINAL exit when using &exit
|
|
3823
|
+
value = self._original_functions.get(name)
|
|
3824
|
+
if value is not None:
|
|
3825
|
+
return value
|
|
3826
|
+
|
|
3827
|
+
# 3. Fall back to scope/builtins if no original was captured
|
|
3437
3828
|
value = self.scope.get(name)
|
|
3438
3829
|
if value is None:
|
|
3439
3830
|
value = self.global_scope.get(name)
|
|
@@ -3444,9 +3835,6 @@ class CSSLRuntime:
|
|
|
3444
3835
|
value = lambda code=0, rt=runtime: rt.exit(code)
|
|
3445
3836
|
else:
|
|
3446
3837
|
value = getattr(self.builtins, f'builtin_{name}', None)
|
|
3447
|
-
if value is None:
|
|
3448
|
-
# Check original functions (for replaced functions)
|
|
3449
|
-
value = self._original_functions.get(name)
|
|
3450
3838
|
if value is not None:
|
|
3451
3839
|
return value
|
|
3452
3840
|
# Build helpful error for captured reference
|
|
@@ -3782,15 +4170,21 @@ class CSSLRuntime:
|
|
|
3782
4170
|
|
|
3783
4171
|
if op == '/':
|
|
3784
4172
|
r = self._to_number(right)
|
|
3785
|
-
|
|
4173
|
+
if r == 0:
|
|
4174
|
+
raise CSSLRuntimeError("Division by zero")
|
|
4175
|
+
return self._to_number(left) / r
|
|
3786
4176
|
|
|
3787
4177
|
if op == '//':
|
|
3788
4178
|
r = self._to_number(right)
|
|
3789
|
-
|
|
4179
|
+
if r == 0:
|
|
4180
|
+
raise CSSLRuntimeError("Integer division by zero")
|
|
4181
|
+
return self._to_number(left) // r
|
|
3790
4182
|
|
|
3791
4183
|
if op == '%':
|
|
3792
4184
|
r = self._to_number(right)
|
|
3793
|
-
|
|
4185
|
+
if r == 0:
|
|
4186
|
+
raise CSSLRuntimeError("Modulo by zero")
|
|
4187
|
+
return self._to_number(left) % r
|
|
3794
4188
|
|
|
3795
4189
|
if op == '**':
|
|
3796
4190
|
return self._to_number(left) ** self._to_number(right)
|
|
@@ -4144,30 +4538,44 @@ class CSSLRuntime:
|
|
|
4144
4538
|
)
|
|
4145
4539
|
|
|
4146
4540
|
def _eval_new(self, node: ASTNode) -> CSSLInstance:
|
|
4147
|
-
"""Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
|
|
4541
|
+
"""Evaluate 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' expression.
|
|
4148
4542
|
|
|
4149
4543
|
Creates a new instance of a CSSL class and calls its constructor.
|
|
4150
4544
|
Supports multiple constructors (constr keyword), class parameters,
|
|
4151
4545
|
and automatic parent constructor calling.
|
|
4152
4546
|
|
|
4153
4547
|
With '@' prefix (new @ClassName), looks only in global scope.
|
|
4548
|
+
With Namespace:: prefix, looks in the namespace dict first.
|
|
4154
4549
|
"""
|
|
4155
4550
|
class_name = node.value.get('class')
|
|
4551
|
+
namespace = node.value.get('namespace') # v4.2.6: Namespace::ClassName support
|
|
4156
4552
|
is_global_ref = node.value.get('is_global_ref', False)
|
|
4157
4553
|
args = [self._evaluate(arg) for arg in node.value.get('args', [])]
|
|
4158
4554
|
kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
|
|
4159
4555
|
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4556
|
+
class_def = None
|
|
4557
|
+
|
|
4558
|
+
# v4.2.6: Handle Namespace::ClassName lookup
|
|
4559
|
+
if namespace:
|
|
4560
|
+
# Look up namespace dict first
|
|
4561
|
+
ns_dict = self.scope.get(namespace)
|
|
4562
|
+
if ns_dict is None:
|
|
4563
|
+
ns_dict = self.global_scope.get(namespace)
|
|
4564
|
+
if isinstance(ns_dict, dict) and class_name in ns_dict:
|
|
4565
|
+
class_def = ns_dict[class_name]
|
|
4566
|
+
|
|
4567
|
+
# Get class definition from scope (if not found in namespace)
|
|
4568
|
+
if class_def is None:
|
|
4569
|
+
if is_global_ref:
|
|
4570
|
+
# With @ prefix, only look in global scope
|
|
4571
|
+
class_def = self._promoted_globals.get(class_name)
|
|
4572
|
+
if class_def is None:
|
|
4573
|
+
class_def = self.global_scope.get(class_name)
|
|
4574
|
+
else:
|
|
4575
|
+
# Normal lookup: local scope first, then global
|
|
4576
|
+
class_def = self.scope.get(class_name)
|
|
4577
|
+
if class_def is None:
|
|
4578
|
+
class_def = self.global_scope.get(class_name)
|
|
4171
4579
|
|
|
4172
4580
|
if class_def is None:
|
|
4173
4581
|
# Build detailed error with suggestions
|
|
@@ -4216,6 +4624,18 @@ class CSSLRuntime:
|
|
|
4216
4624
|
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
4217
4625
|
)
|
|
4218
4626
|
|
|
4627
|
+
# v4.2.5: Deferred &target replacement for non-embedded classes
|
|
4628
|
+
# Apply on first instantiation if pending
|
|
4629
|
+
if hasattr(class_def, '_pending_target') and class_def._pending_target:
|
|
4630
|
+
pending = class_def._pending_target
|
|
4631
|
+
self._overwrite_class_target(
|
|
4632
|
+
pending['append_ref_class'],
|
|
4633
|
+
pending.get('append_ref_member'),
|
|
4634
|
+
class_def
|
|
4635
|
+
)
|
|
4636
|
+
class_def._target_applied = True
|
|
4637
|
+
class_def._pending_target = None # Clear pending
|
|
4638
|
+
|
|
4219
4639
|
# Create new instance
|
|
4220
4640
|
instance = CSSLInstance(class_def)
|
|
4221
4641
|
|
|
@@ -4939,12 +5359,19 @@ class CSSLRuntime:
|
|
|
4939
5359
|
index = self._evaluate(node.value.get('index'))
|
|
4940
5360
|
|
|
4941
5361
|
if obj is None:
|
|
4942
|
-
|
|
5362
|
+
raise CSSLRuntimeError(f"Cannot index into None/null value")
|
|
4943
5363
|
|
|
4944
5364
|
try:
|
|
4945
5365
|
return obj[index]
|
|
4946
|
-
except
|
|
4947
|
-
|
|
5366
|
+
except IndexError:
|
|
5367
|
+
# v4.2.6: Throw error instead of returning None
|
|
5368
|
+
length = len(obj) if hasattr(obj, '__len__') else 'unknown'
|
|
5369
|
+
raise CSSLRuntimeError(f"Index {index} out of bounds (length: {length})")
|
|
5370
|
+
except KeyError:
|
|
5371
|
+
# v4.2.6: Throw error for missing dict keys
|
|
5372
|
+
raise CSSLRuntimeError(f"Key '{index}' not found in dictionary")
|
|
5373
|
+
except TypeError as e:
|
|
5374
|
+
raise CSSLRuntimeError(f"Cannot index: {e}")
|
|
4948
5375
|
|
|
4949
5376
|
def _set_member(self, node: ASTNode, value: Any):
|
|
4950
5377
|
"""Set member value"""
|
|
@@ -5238,10 +5665,17 @@ class CSSLRuntime:
|
|
|
5238
5665
|
|
|
5239
5666
|
def _emit_output(self, text: str, level: str = 'normal'):
|
|
5240
5667
|
"""Emit output through callback or print"""
|
|
5668
|
+
import sys
|
|
5241
5669
|
if self._output_callback:
|
|
5242
5670
|
self._output_callback(text, level)
|
|
5243
5671
|
else:
|
|
5244
|
-
|
|
5672
|
+
# Handle encoding issues on Windows console
|
|
5673
|
+
try:
|
|
5674
|
+
print(text, end='')
|
|
5675
|
+
except UnicodeEncodeError:
|
|
5676
|
+
# Fallback: encode with errors='replace' for unsupported chars
|
|
5677
|
+
encoded = text.encode(sys.stdout.encoding or 'utf-8', errors='replace')
|
|
5678
|
+
print(encoded.decode(sys.stdout.encoding or 'utf-8', errors='replace'), end='')
|
|
5245
5679
|
|
|
5246
5680
|
def output(self, text: str):
|
|
5247
5681
|
"""Output text"""
|