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.
- includecpp/CHANGELOG.md +142 -0
- includecpp/DOCUMENTATION.md +446 -0
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +4 -1
- includecpp/cli/commands.py +700 -86
- includecpp/core/ai_integration.py +46 -13
- includecpp/core/cpp_api_extensions.pyi +350 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +1535 -1316
- includecpp/core/cssl/cssl_builtins.py +230 -23
- includecpp/core/cssl/cssl_languages.py +1757 -0
- includecpp/core/cssl/cssl_parser.py +967 -126
- includecpp/core/cssl/cssl_runtime.py +1133 -84
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +361 -1
- includecpp/core/cssl_bridge.py +194 -8
- includecpp/core/cssl_bridge.pyi +148 -10
- includecpp/generator/parser.cpp +121 -4
- includecpp/generator/parser.h +6 -0
- includecpp/vscode/cssl/package.json +43 -1
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +178 -19
- includecpp-4.3.0.dist-info/METADATA +277 -0
- {includecpp-4.0.2.dist-info → includecpp-4.3.0.dist-info}/RECORD +26 -22
- includecpp-4.0.2.dist-info/METADATA +0 -908
- {includecpp-4.0.2.dist-info → includecpp-4.3.0.dist-info}/WHEEL +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.3.0.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.3.0.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.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
|
|
|
@@ -278,6 +278,26 @@ class CSSLRuntime:
|
|
|
278
278
|
self._setup_modules()
|
|
279
279
|
self._setup_builtins()
|
|
280
280
|
|
|
281
|
+
def output(self, text: str, level: str = 'normal') -> None:
|
|
282
|
+
"""Output text, using callback if available, otherwise print."""
|
|
283
|
+
if self._output_callback:
|
|
284
|
+
self._output_callback(text, level)
|
|
285
|
+
else:
|
|
286
|
+
print(text, end='')
|
|
287
|
+
self.output_buffer.append(text)
|
|
288
|
+
|
|
289
|
+
def debug(self, text: str) -> None:
|
|
290
|
+
"""Debug output."""
|
|
291
|
+
self.output(f"[DEBUG] {text}\n", 'debug')
|
|
292
|
+
|
|
293
|
+
def error(self, text: str) -> None:
|
|
294
|
+
"""Error output."""
|
|
295
|
+
self.output(f"[ERROR] {text}\n", 'error')
|
|
296
|
+
|
|
297
|
+
def warn(self, text: str) -> None:
|
|
298
|
+
"""Warning output."""
|
|
299
|
+
self.output(f"[WARN] {text}\n", 'warning')
|
|
300
|
+
|
|
281
301
|
def _setup_modules(self):
|
|
282
302
|
"""Setup module references for @KernelClient, @VSRAM, etc."""
|
|
283
303
|
if self.service_engine:
|
|
@@ -512,6 +532,10 @@ class CSSLRuntime:
|
|
|
512
532
|
self._exec_struct(child)
|
|
513
533
|
elif child.type == 'class':
|
|
514
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)
|
|
515
539
|
elif child.type == 'function':
|
|
516
540
|
self._exec_function(child)
|
|
517
541
|
elif child.type == 'global_assignment':
|
|
@@ -793,6 +817,74 @@ class CSSLRuntime:
|
|
|
793
817
|
|
|
794
818
|
return struct_data
|
|
795
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
|
+
|
|
796
888
|
def _exec_class(self, node: ASTNode) -> CSSLClass:
|
|
797
889
|
"""Execute class definition - registers class in scope.
|
|
798
890
|
|
|
@@ -863,6 +955,18 @@ class CSSLRuntime:
|
|
|
863
955
|
class_params = class_info.get('class_params', [])
|
|
864
956
|
extends_args = class_info.get('extends_args', [])
|
|
865
957
|
|
|
958
|
+
# v4.2.0: Handle 'supports' language transformation for raw_body
|
|
959
|
+
supports_language = class_info.get('supports_language')
|
|
960
|
+
raw_body = class_info.get('raw_body')
|
|
961
|
+
|
|
962
|
+
if raw_body and supports_language:
|
|
963
|
+
# Transform raw body from target language to CSSL and parse
|
|
964
|
+
transformed_children = self._transform_and_parse_class_body(
|
|
965
|
+
raw_body, supports_language, class_name
|
|
966
|
+
)
|
|
967
|
+
# Add transformed children to node's children
|
|
968
|
+
node.children = transformed_children
|
|
969
|
+
|
|
866
970
|
for child in node.children:
|
|
867
971
|
if child.type == 'constructor':
|
|
868
972
|
# New-style constructor from 'constr' keyword
|
|
@@ -910,6 +1014,22 @@ class CSSLRuntime:
|
|
|
910
1014
|
self.global_scope.set(class_name, class_def)
|
|
911
1015
|
self._promoted_globals[class_name] = class_def
|
|
912
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
|
+
|
|
913
1033
|
# Handle class overwrites - replace methods in target class
|
|
914
1034
|
if overwrites_class_name:
|
|
915
1035
|
self._apply_class_overwrites(
|
|
@@ -987,6 +1107,119 @@ class CSSLRuntime:
|
|
|
987
1107
|
self._current_instance = old_instance
|
|
988
1108
|
return wrapper
|
|
989
1109
|
|
|
1110
|
+
def _transform_and_parse_class_body(self, raw_body: str, language: str, class_name: str) -> list:
|
|
1111
|
+
"""Transform source code from another language to CSSL and parse as class body.
|
|
1112
|
+
|
|
1113
|
+
v4.2.0: Used for 'supports <lang>' in class definitions.
|
|
1114
|
+
|
|
1115
|
+
Args:
|
|
1116
|
+
raw_body: Raw source code in the target language
|
|
1117
|
+
language: Language identifier (py, python, cpp, c++, js, javascript, etc.)
|
|
1118
|
+
class_name: Name of the class (for constructor recognition)
|
|
1119
|
+
|
|
1120
|
+
Returns:
|
|
1121
|
+
List of parsed AST nodes representing methods, constructors, and members
|
|
1122
|
+
"""
|
|
1123
|
+
import textwrap
|
|
1124
|
+
from .cssl_languages import get_language
|
|
1125
|
+
from .cssl_parser import parse_cssl_program, ASTNode
|
|
1126
|
+
|
|
1127
|
+
# Normalize language ID
|
|
1128
|
+
lang_id = language.lstrip('@').lower()
|
|
1129
|
+
|
|
1130
|
+
# Get language support and transformer
|
|
1131
|
+
lang_support = get_language(lang_id)
|
|
1132
|
+
if lang_support is None:
|
|
1133
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' clause")
|
|
1134
|
+
|
|
1135
|
+
# Dedent the raw body to normalize indentation
|
|
1136
|
+
# This fixes the issue where code inside CSSL {} has relative indentation
|
|
1137
|
+
dedented_body = textwrap.dedent(raw_body)
|
|
1138
|
+
|
|
1139
|
+
# Transform the raw body to CSSL syntax
|
|
1140
|
+
transformer = lang_support.get_transformer()
|
|
1141
|
+
transformed_source = transformer.transform_source(dedented_body)
|
|
1142
|
+
|
|
1143
|
+
# Wrap in a dummy class for parsing
|
|
1144
|
+
wrapper_source = f"class _TempClass {{\n{transformed_source}\n}}"
|
|
1145
|
+
|
|
1146
|
+
try:
|
|
1147
|
+
ast = parse_cssl_program(wrapper_source)
|
|
1148
|
+
except Exception as e:
|
|
1149
|
+
raise CSSLRuntimeError(
|
|
1150
|
+
f"Failed to parse transformed '{lang_id}' code: {e}\n"
|
|
1151
|
+
f"Dedented:\n{dedented_body}\n"
|
|
1152
|
+
f"Transformed:\n{transformed_source}"
|
|
1153
|
+
)
|
|
1154
|
+
|
|
1155
|
+
# Extract children from the parsed temp class
|
|
1156
|
+
children = []
|
|
1157
|
+
for top_level in ast.children:
|
|
1158
|
+
if top_level.type == 'class':
|
|
1159
|
+
for child in top_level.children:
|
|
1160
|
+
# Mark constructor if method name matches class_name or is __init__
|
|
1161
|
+
if child.type == 'function':
|
|
1162
|
+
func_info = child.value
|
|
1163
|
+
method_name = func_info.get('name')
|
|
1164
|
+
if method_name == class_name or method_name == '__init__':
|
|
1165
|
+
child.value['is_constructor'] = True
|
|
1166
|
+
children.append(child)
|
|
1167
|
+
break
|
|
1168
|
+
|
|
1169
|
+
return children
|
|
1170
|
+
|
|
1171
|
+
def _transform_and_parse_function_body(self, raw_body: str, language: str) -> list:
|
|
1172
|
+
"""Transform source code from another language to CSSL and parse as function body.
|
|
1173
|
+
|
|
1174
|
+
v4.2.0: Used for 'supports <lang>' in function definitions.
|
|
1175
|
+
|
|
1176
|
+
Args:
|
|
1177
|
+
raw_body: Raw source code in the target language
|
|
1178
|
+
language: Language identifier (py, python, cpp, c++, js, javascript, etc.)
|
|
1179
|
+
|
|
1180
|
+
Returns:
|
|
1181
|
+
List of parsed AST nodes representing statements in the function body
|
|
1182
|
+
"""
|
|
1183
|
+
import textwrap
|
|
1184
|
+
from .cssl_languages import get_language
|
|
1185
|
+
from .cssl_parser import parse_cssl_program
|
|
1186
|
+
|
|
1187
|
+
# Normalize language ID
|
|
1188
|
+
lang_id = language.lstrip('@').lower()
|
|
1189
|
+
|
|
1190
|
+
# Get language support and transformer
|
|
1191
|
+
lang_support = get_language(lang_id)
|
|
1192
|
+
if lang_support is None:
|
|
1193
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' clause")
|
|
1194
|
+
|
|
1195
|
+
# Dedent the raw body to normalize indentation
|
|
1196
|
+
dedented_body = textwrap.dedent(raw_body)
|
|
1197
|
+
|
|
1198
|
+
# Transform the raw body to CSSL syntax
|
|
1199
|
+
transformer = lang_support.get_transformer()
|
|
1200
|
+
transformed_source = transformer.transform_source(dedented_body)
|
|
1201
|
+
|
|
1202
|
+
# Wrap in a dummy function for parsing
|
|
1203
|
+
wrapper_source = f"define _TempFunc() {{\n{transformed_source}\n}}"
|
|
1204
|
+
|
|
1205
|
+
try:
|
|
1206
|
+
ast = parse_cssl_program(wrapper_source)
|
|
1207
|
+
except Exception as e:
|
|
1208
|
+
raise CSSLRuntimeError(
|
|
1209
|
+
f"Failed to parse transformed '{lang_id}' code: {e}\n"
|
|
1210
|
+
f"Dedented:\n{dedented_body}\n"
|
|
1211
|
+
f"Transformed:\n{transformed_source}"
|
|
1212
|
+
)
|
|
1213
|
+
|
|
1214
|
+
# Extract children from the parsed temp function
|
|
1215
|
+
children = []
|
|
1216
|
+
for top_level in ast.children:
|
|
1217
|
+
if top_level.type == 'function':
|
|
1218
|
+
children = top_level.children
|
|
1219
|
+
break
|
|
1220
|
+
|
|
1221
|
+
return children
|
|
1222
|
+
|
|
990
1223
|
def _exec_function(self, node: ASTNode) -> Any:
|
|
991
1224
|
"""Execute function definition - registers it and handles extends/overwrites.
|
|
992
1225
|
|
|
@@ -1019,13 +1252,18 @@ class CSSLRuntime:
|
|
|
1019
1252
|
# Handle &Class::method syntax
|
|
1020
1253
|
# Without ++ = full replacement
|
|
1021
1254
|
# With ++ = append (run original first, then new code)
|
|
1022
|
-
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:
|
|
1023
1259
|
if append_mode:
|
|
1024
1260
|
# Append mode: wrap original to run original + new
|
|
1025
1261
|
self._append_to_target(append_ref_class, append_ref_member, node)
|
|
1026
1262
|
else:
|
|
1027
1263
|
# Full replacement
|
|
1028
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
|
|
1029
1267
|
|
|
1030
1268
|
# Handle overwrites keyword - replace the target function
|
|
1031
1269
|
if overwrites_func:
|
|
@@ -1070,6 +1308,34 @@ class CSSLRuntime:
|
|
|
1070
1308
|
return self._call_function(func_node, list(args), kwargs)
|
|
1071
1309
|
return wrapper
|
|
1072
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
|
+
|
|
1073
1339
|
def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
|
|
1074
1340
|
"""Overwrite a class method or function with the replacement.
|
|
1075
1341
|
|
|
@@ -1077,9 +1343,25 @@ class CSSLRuntime:
|
|
|
1077
1343
|
- &ClassName::method - Overwrite method in CSSL class
|
|
1078
1344
|
- &$PyObject.method - Overwrite method in Python shared object
|
|
1079
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.
|
|
1080
1349
|
"""
|
|
1081
1350
|
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1082
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
|
+
|
|
1083
1365
|
# Handle Python shared objects
|
|
1084
1366
|
if ref_class.startswith('$'):
|
|
1085
1367
|
var_name = ref_class[1:]
|
|
@@ -1096,7 +1378,8 @@ class CSSLRuntime:
|
|
|
1096
1378
|
|
|
1097
1379
|
# Overwrite Python object method
|
|
1098
1380
|
if ref_member and hasattr(ref_obj, ref_member):
|
|
1099
|
-
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)
|
|
1100
1383
|
try:
|
|
1101
1384
|
setattr(ref_obj, ref_member, wrapper)
|
|
1102
1385
|
except (AttributeError, TypeError):
|
|
@@ -1105,12 +1388,49 @@ class CSSLRuntime:
|
|
|
1105
1388
|
|
|
1106
1389
|
# Handle CSSL class method overwrite
|
|
1107
1390
|
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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
|
|
1112
1420
|
self.scope.set(ref_class, replacement_node)
|
|
1113
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:
|
|
1114
1434
|
return
|
|
1115
1435
|
|
|
1116
1436
|
if isinstance(target_class, CSSLClass) and ref_member:
|
|
@@ -1152,16 +1472,22 @@ class CSSLRuntime:
|
|
|
1152
1472
|
if ref_member and hasattr(ref_obj, ref_member):
|
|
1153
1473
|
original_method = getattr(ref_obj, ref_member)
|
|
1154
1474
|
runtime = self
|
|
1475
|
+
# Store the original method before wrapping
|
|
1476
|
+
_saved_original = original_method
|
|
1155
1477
|
def appended_wrapper(*args, **kwargs):
|
|
1156
|
-
# Run original first
|
|
1478
|
+
# Run original first (use saved reference to avoid recursion)
|
|
1157
1479
|
result = None
|
|
1158
|
-
if callable(
|
|
1480
|
+
if callable(_saved_original):
|
|
1159
1481
|
try:
|
|
1160
|
-
result =
|
|
1482
|
+
result = _saved_original(*args, **kwargs)
|
|
1161
1483
|
except:
|
|
1162
1484
|
pass
|
|
1163
|
-
# Then run appended code
|
|
1164
|
-
|
|
1485
|
+
# Then run appended code - disable append_mode to prevent recursion
|
|
1486
|
+
append_node.value['append_mode'] = False
|
|
1487
|
+
try:
|
|
1488
|
+
return runtime._call_function(append_node, list(args), kwargs)
|
|
1489
|
+
finally:
|
|
1490
|
+
append_node.value['append_mode'] = True
|
|
1165
1491
|
try:
|
|
1166
1492
|
setattr(ref_obj, ref_member, appended_wrapper)
|
|
1167
1493
|
except (AttributeError, TypeError):
|
|
@@ -1206,6 +1532,58 @@ class CSSLRuntime:
|
|
|
1206
1532
|
target_class.members[i] = append_node
|
|
1207
1533
|
break
|
|
1208
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
|
+
|
|
1209
1587
|
def _exec_typed_declaration(self, node: ASTNode) -> Any:
|
|
1210
1588
|
"""Execute typed variable declaration: type<T> varName = value;
|
|
1211
1589
|
|
|
@@ -1310,29 +1688,38 @@ class CSSLRuntime:
|
|
|
1310
1688
|
def _exec_instance_declaration(self, node: ASTNode) -> Any:
|
|
1311
1689
|
"""Execute instance declaration: instance<"name"> varName;
|
|
1312
1690
|
|
|
1313
|
-
Gets or creates a shared instance by name.
|
|
1691
|
+
Gets or creates a universal shared instance by name.
|
|
1692
|
+
Universal instances are accessible from CSSL, Python, and C++.
|
|
1693
|
+
|
|
1694
|
+
Usage:
|
|
1695
|
+
instance<"myContainer"> container; // Creates or gets instance
|
|
1696
|
+
container.member = "value"; // Set member
|
|
1697
|
+
container +<<== { void func() {} } // Inject methods
|
|
1314
1698
|
"""
|
|
1315
|
-
from
|
|
1699
|
+
from .cssl_types import UniversalInstance
|
|
1700
|
+
|
|
1316
1701
|
decl = node.value
|
|
1317
1702
|
instance_name = decl.get('instance_name')
|
|
1318
1703
|
var_name = decl.get('name')
|
|
1319
1704
|
value_node = decl.get('value')
|
|
1320
1705
|
|
|
1321
|
-
# Get existing
|
|
1322
|
-
instance =
|
|
1323
|
-
if instance_name in _live_objects:
|
|
1324
|
-
instance = SharedObjectProxy(instance_name, _live_objects[instance_name])
|
|
1325
|
-
elif self.global_scope.has(f'${instance_name}'):
|
|
1326
|
-
instance = self.global_scope.get(f'${instance_name}')
|
|
1706
|
+
# Get existing or create new universal instance
|
|
1707
|
+
instance = UniversalInstance.get_or_create(instance_name)
|
|
1327
1708
|
|
|
1328
|
-
# If value is provided,
|
|
1709
|
+
# If value is provided, set it as initial content
|
|
1329
1710
|
if value_node:
|
|
1330
|
-
|
|
1331
|
-
#
|
|
1332
|
-
|
|
1711
|
+
initial_value = self._evaluate(value_node)
|
|
1712
|
+
# If it's a dict, set all keys as members
|
|
1713
|
+
if isinstance(initial_value, dict):
|
|
1714
|
+
for key, val in initial_value.items():
|
|
1715
|
+
instance.set_member(key, val)
|
|
1716
|
+
else:
|
|
1717
|
+
instance.set_member('value', initial_value)
|
|
1333
1718
|
|
|
1334
|
-
# Store in scope
|
|
1719
|
+
# Store in scope and global scope for access
|
|
1335
1720
|
self.scope.set(var_name, instance)
|
|
1721
|
+
self.global_scope.set(f'${instance_name}', instance)
|
|
1722
|
+
|
|
1336
1723
|
return instance
|
|
1337
1724
|
|
|
1338
1725
|
def _exec_super_func(self, node: ASTNode) -> Any:
|
|
@@ -1478,6 +1865,18 @@ class CSSLRuntime:
|
|
|
1478
1865
|
modifiers = func_info.get('modifiers', [])
|
|
1479
1866
|
kwargs = kwargs or {}
|
|
1480
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
|
+
|
|
1481
1880
|
# Check for undefined modifier - suppress errors if present
|
|
1482
1881
|
is_undefined = 'undefined' in modifiers
|
|
1483
1882
|
|
|
@@ -1517,13 +1916,15 @@ class CSSLRuntime:
|
|
|
1517
1916
|
|
|
1518
1917
|
# Bind parameters - handle both positional and named arguments
|
|
1519
1918
|
for i, param in enumerate(params):
|
|
1520
|
-
# Extract param name and
|
|
1919
|
+
# Extract param name, type, and default from dict format: {'name': 'a', 'type': 'int', 'default': ...}
|
|
1521
1920
|
if isinstance(param, dict):
|
|
1522
1921
|
param_name = param['name']
|
|
1523
1922
|
param_type = param.get('type', '')
|
|
1923
|
+
param_default = param.get('default') # v4.2.0: Default value AST node
|
|
1524
1924
|
else:
|
|
1525
1925
|
param_name = param
|
|
1526
1926
|
param_type = ''
|
|
1927
|
+
param_default = None
|
|
1527
1928
|
|
|
1528
1929
|
# Check if this is an 'open' parameter - receives all args as a list
|
|
1529
1930
|
# The parser sets param['open'] = True for 'open' keyword
|
|
@@ -1546,6 +1947,10 @@ class CSSLRuntime:
|
|
|
1546
1947
|
elif i < len(args):
|
|
1547
1948
|
# Positional argument
|
|
1548
1949
|
new_scope.set(param_name, args[i])
|
|
1950
|
+
elif param_default is not None:
|
|
1951
|
+
# v4.2.0: Use default value if no argument provided
|
|
1952
|
+
default_value = self._evaluate(param_default)
|
|
1953
|
+
new_scope.set(param_name, default_value)
|
|
1549
1954
|
else:
|
|
1550
1955
|
new_scope.set(param_name, None)
|
|
1551
1956
|
|
|
@@ -1565,11 +1970,24 @@ class CSSLRuntime:
|
|
|
1565
1970
|
args, kwargs, {}, is_constructor=False
|
|
1566
1971
|
)
|
|
1567
1972
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1973
|
+
# v4.2.0: Handle raw_body with supports_language (multi-language support)
|
|
1974
|
+
raw_body = func_info.get('raw_body')
|
|
1975
|
+
supports_language = func_info.get('supports_language')
|
|
1976
|
+
|
|
1977
|
+
if raw_body and supports_language:
|
|
1978
|
+
# Transform and parse the raw body from the target language
|
|
1979
|
+
body_children = self._transform_and_parse_function_body(raw_body, supports_language)
|
|
1980
|
+
for child in body_children:
|
|
1981
|
+
if not self._running:
|
|
1982
|
+
break
|
|
1983
|
+
self._execute_node(child)
|
|
1984
|
+
else:
|
|
1985
|
+
# Normal CSSL function body
|
|
1986
|
+
for child in func_node.children:
|
|
1987
|
+
# Check if exit() was called
|
|
1988
|
+
if not self._running:
|
|
1989
|
+
break
|
|
1990
|
+
self._execute_node(child)
|
|
1573
1991
|
except CSSLReturn as ret:
|
|
1574
1992
|
return_value = ret.value
|
|
1575
1993
|
|
|
@@ -1833,6 +2251,101 @@ class CSSLRuntime:
|
|
|
1833
2251
|
|
|
1834
2252
|
return None
|
|
1835
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
|
+
|
|
1836
2349
|
def _exec_return(self, node: ASTNode) -> Any:
|
|
1837
2350
|
"""Execute return statement.
|
|
1838
2351
|
|
|
@@ -1935,7 +2448,7 @@ class CSSLRuntime:
|
|
|
1935
2448
|
return None
|
|
1936
2449
|
|
|
1937
2450
|
def _exec_try(self, node: ASTNode) -> Any:
|
|
1938
|
-
"""Execute try/catch block"""
|
|
2451
|
+
"""Execute try/catch/finally block"""
|
|
1939
2452
|
try:
|
|
1940
2453
|
for child in node.children:
|
|
1941
2454
|
if child.type == 'try-block':
|
|
@@ -1949,9 +2462,89 @@ class CSSLRuntime:
|
|
|
1949
2462
|
self.scope.set(error_var, str(e))
|
|
1950
2463
|
for stmt in child.children:
|
|
1951
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)
|
|
1952
2480
|
|
|
1953
2481
|
return None
|
|
1954
2482
|
|
|
2483
|
+
def _exec_supports_block(self, node: ASTNode) -> Any:
|
|
2484
|
+
"""Execute standalone supports block for multi-language syntax.
|
|
2485
|
+
|
|
2486
|
+
v4.2.0: Allows 'supports' to be used anywhere, not just in class/function.
|
|
2487
|
+
|
|
2488
|
+
Syntax:
|
|
2489
|
+
supports py {
|
|
2490
|
+
for i in range(10):
|
|
2491
|
+
print(i)
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
supports cpp {
|
|
2495
|
+
int x = 42;
|
|
2496
|
+
std::cout << x << std::endl;
|
|
2497
|
+
}
|
|
2498
|
+
"""
|
|
2499
|
+
block_info = node.value
|
|
2500
|
+
language = block_info.get('language')
|
|
2501
|
+
raw_source = block_info.get('raw_source')
|
|
2502
|
+
|
|
2503
|
+
# If we have raw_source, transform and execute
|
|
2504
|
+
if raw_source and language:
|
|
2505
|
+
import textwrap
|
|
2506
|
+
from .cssl_languages import get_language
|
|
2507
|
+
from .cssl_parser import parse_cssl_program
|
|
2508
|
+
|
|
2509
|
+
# Normalize language ID
|
|
2510
|
+
lang_id = language.lstrip('@').lower()
|
|
2511
|
+
|
|
2512
|
+
# Get language support and transformer
|
|
2513
|
+
lang_support = get_language(lang_id)
|
|
2514
|
+
if lang_support is None:
|
|
2515
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' block")
|
|
2516
|
+
|
|
2517
|
+
# Dedent the raw source to normalize indentation
|
|
2518
|
+
dedented_source = textwrap.dedent(raw_source)
|
|
2519
|
+
|
|
2520
|
+
# Transform the raw source to CSSL
|
|
2521
|
+
transformer = lang_support.get_transformer()
|
|
2522
|
+
transformed_source = transformer.transform_source(dedented_source)
|
|
2523
|
+
|
|
2524
|
+
# Parse the transformed CSSL
|
|
2525
|
+
try:
|
|
2526
|
+
ast = parse_cssl_program(transformed_source)
|
|
2527
|
+
except Exception as e:
|
|
2528
|
+
raise CSSLRuntimeError(
|
|
2529
|
+
f"Failed to parse transformed '{lang_id}' code in supports block: {e}\n"
|
|
2530
|
+
f"Dedented:\n{dedented_source}\n"
|
|
2531
|
+
f"Transformed:\n{transformed_source}"
|
|
2532
|
+
)
|
|
2533
|
+
|
|
2534
|
+
# Execute the transformed AST
|
|
2535
|
+
result = None
|
|
2536
|
+
for child in ast.children:
|
|
2537
|
+
result = self._execute_node(child)
|
|
2538
|
+
|
|
2539
|
+
return result
|
|
2540
|
+
|
|
2541
|
+
# Fallback: execute already-parsed children (CSSL syntax)
|
|
2542
|
+
result = None
|
|
2543
|
+
for child in node.children:
|
|
2544
|
+
result = self._execute_node(child)
|
|
2545
|
+
|
|
2546
|
+
return result
|
|
2547
|
+
|
|
1955
2548
|
def _exec_createcmd_inject(self, node: ASTNode) -> Any:
|
|
1956
2549
|
"""Execute createcmd injection: createcmd('cmd') <== { action }"""
|
|
1957
2550
|
command_call = node.value.get('command_call')
|
|
@@ -2520,24 +3113,60 @@ class CSSLRuntime:
|
|
|
2520
3113
|
if filter_info:
|
|
2521
3114
|
source = self._apply_injection_filter(source, filter_info)
|
|
2522
3115
|
|
|
2523
|
-
# Get current target value for add/move modes
|
|
3116
|
+
# Get current target value for add/move/replace modes (needed for UniversalInstance handling)
|
|
2524
3117
|
current_value = None
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
current_value = None
|
|
3118
|
+
try:
|
|
3119
|
+
current_value = self._evaluate(target)
|
|
3120
|
+
except Exception:
|
|
3121
|
+
# Target might not exist yet, that's okay for add mode
|
|
3122
|
+
current_value = None
|
|
2531
3123
|
|
|
2532
3124
|
# Determine final value based on mode
|
|
2533
3125
|
if mode == 'replace':
|
|
2534
|
-
|
|
3126
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
3127
|
+
# Special handling for UniversalInstance targets - inject instead of replace
|
|
3128
|
+
if isinstance(current_value, UniversalInstance):
|
|
3129
|
+
if isinstance(source, CSSLClass):
|
|
3130
|
+
current_value.set_member(source.name, source)
|
|
3131
|
+
final_value = current_value
|
|
3132
|
+
elif isinstance(source, ASTNode) and source.type == 'function':
|
|
3133
|
+
func_info = source.value
|
|
3134
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
3135
|
+
if func_name:
|
|
3136
|
+
current_value.set_method(func_name, source, self)
|
|
3137
|
+
final_value = current_value
|
|
3138
|
+
elif isinstance(source, CSSLInstance):
|
|
3139
|
+
current_value.set_member(source._class.name, source)
|
|
3140
|
+
final_value = current_value
|
|
3141
|
+
else:
|
|
3142
|
+
# For other types, store as member with source type name
|
|
3143
|
+
final_value = source
|
|
3144
|
+
else:
|
|
3145
|
+
final_value = source
|
|
2535
3146
|
elif mode == 'add':
|
|
2536
3147
|
# Copy & add - preserve target and add source
|
|
2537
|
-
from .cssl_types import CSSLInstance
|
|
3148
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2538
3149
|
|
|
3150
|
+
# Special handling for UniversalInstance + CSSLClass
|
|
3151
|
+
if isinstance(current_value, UniversalInstance) and isinstance(source, CSSLClass):
|
|
3152
|
+
# Inject class definition into universal instance
|
|
3153
|
+
current_value.set_member(source.name, source)
|
|
3154
|
+
final_value = current_value
|
|
3155
|
+
# Special handling for UniversalInstance + Function (AST node)
|
|
3156
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, ASTNode) and source.type == 'function':
|
|
3157
|
+
# Inject function as a method into universal instance
|
|
3158
|
+
func_info = source.value
|
|
3159
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
3160
|
+
if func_name:
|
|
3161
|
+
current_value.set_method(func_name, source, self)
|
|
3162
|
+
final_value = current_value
|
|
3163
|
+
# Special handling for UniversalInstance + CSSLInstance
|
|
3164
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, CSSLInstance):
|
|
3165
|
+
class_name = source._class.name
|
|
3166
|
+
current_value.set_member(class_name, source)
|
|
3167
|
+
final_value = current_value
|
|
2539
3168
|
# Special handling for CSSLInstance - merge classes
|
|
2540
|
-
|
|
3169
|
+
elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
|
|
2541
3170
|
# Add the new class instance as a member with class name as key
|
|
2542
3171
|
class_name = source._class.name
|
|
2543
3172
|
current_value._members[class_name] = source
|
|
@@ -2551,6 +3180,15 @@ class CSSLRuntime:
|
|
|
2551
3180
|
final_value = {**current_value, **source}
|
|
2552
3181
|
elif isinstance(current_value, str) and isinstance(source, str):
|
|
2553
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
|
|
2554
3192
|
elif current_value is None:
|
|
2555
3193
|
final_value = [source] if not isinstance(source, list) else source
|
|
2556
3194
|
else:
|
|
@@ -2736,8 +3374,13 @@ class CSSLRuntime:
|
|
|
2736
3374
|
- add: func +<<== { code } - ADDS code to function (both execute)
|
|
2737
3375
|
- remove: func -<<== { code } - REMOVES matching code from function
|
|
2738
3376
|
|
|
3377
|
+
Also supports instance injection:
|
|
3378
|
+
- instance +<<== { void method() { ... } } - ADDS methods to UniversalInstance
|
|
3379
|
+
|
|
2739
3380
|
Also supports expression form: func <<== %exit() (wraps in action_block)
|
|
2740
3381
|
"""
|
|
3382
|
+
from .cssl_types import UniversalInstance
|
|
3383
|
+
|
|
2741
3384
|
target = node.value.get('target')
|
|
2742
3385
|
code_block = node.value.get('code')
|
|
2743
3386
|
source_expr = node.value.get('source') # For expression form: func <<== expr
|
|
@@ -2749,6 +3392,17 @@ class CSSLRuntime:
|
|
|
2749
3392
|
expr_node = ASTNode('expression', value=source_expr)
|
|
2750
3393
|
code_block = ASTNode('action_block', children=[expr_node])
|
|
2751
3394
|
|
|
3395
|
+
# Check if target is a UniversalInstance
|
|
3396
|
+
target_value = None
|
|
3397
|
+
if isinstance(target, ASTNode) and target.type == 'identifier':
|
|
3398
|
+
target_value = self.scope.get(target.value)
|
|
3399
|
+
if target_value is None:
|
|
3400
|
+
target_value = self.global_scope.get(target.value)
|
|
3401
|
+
|
|
3402
|
+
# Handle UniversalInstance injection
|
|
3403
|
+
if isinstance(target_value, UniversalInstance):
|
|
3404
|
+
return self._inject_into_instance(target_value, code_block, mode)
|
|
3405
|
+
|
|
2752
3406
|
# Get function name from target
|
|
2753
3407
|
func_name = None
|
|
2754
3408
|
if isinstance(target, ASTNode):
|
|
@@ -2796,6 +3450,60 @@ class CSSLRuntime:
|
|
|
2796
3450
|
|
|
2797
3451
|
return None
|
|
2798
3452
|
|
|
3453
|
+
def _inject_into_instance(self, instance: Any, code_block: Any, mode: str) -> Any:
|
|
3454
|
+
"""Inject code/methods into a UniversalInstance.
|
|
3455
|
+
|
|
3456
|
+
Usage:
|
|
3457
|
+
instance<"myContainer"> container;
|
|
3458
|
+
container +<<== {
|
|
3459
|
+
void sayHello() { printl("Hello!"); }
|
|
3460
|
+
int value = 42;
|
|
3461
|
+
}
|
|
3462
|
+
"""
|
|
3463
|
+
from .cssl_types import UniversalInstance
|
|
3464
|
+
|
|
3465
|
+
if not isinstance(instance, UniversalInstance):
|
|
3466
|
+
return None
|
|
3467
|
+
|
|
3468
|
+
if code_block is None:
|
|
3469
|
+
return None
|
|
3470
|
+
|
|
3471
|
+
# Store the raw injection
|
|
3472
|
+
instance.add_injection(code_block)
|
|
3473
|
+
|
|
3474
|
+
# Parse the code block for function definitions and variable declarations
|
|
3475
|
+
if isinstance(code_block, ASTNode):
|
|
3476
|
+
children = code_block.children if hasattr(code_block, 'children') else []
|
|
3477
|
+
|
|
3478
|
+
for child in children:
|
|
3479
|
+
if isinstance(child, ASTNode):
|
|
3480
|
+
if child.type == 'function':
|
|
3481
|
+
# Extract function name and store the AST node
|
|
3482
|
+
func_info = child.value
|
|
3483
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
3484
|
+
if func_name:
|
|
3485
|
+
instance.set_method(func_name, child, self)
|
|
3486
|
+
elif child.type == 'var_declaration':
|
|
3487
|
+
# Extract variable and value
|
|
3488
|
+
var_info = child.value
|
|
3489
|
+
if isinstance(var_info, dict):
|
|
3490
|
+
var_name = var_info.get('name')
|
|
3491
|
+
value_node = var_info.get('value')
|
|
3492
|
+
if var_name:
|
|
3493
|
+
value = self._evaluate(value_node) if value_node else None
|
|
3494
|
+
instance.set_member(var_name, value)
|
|
3495
|
+
elif child.type == 'typed_var_declaration':
|
|
3496
|
+
# Typed variable declaration
|
|
3497
|
+
var_info = child.value
|
|
3498
|
+
if isinstance(var_info, dict):
|
|
3499
|
+
var_name = var_info.get('name')
|
|
3500
|
+
value_node = var_info.get('value')
|
|
3501
|
+
if var_name:
|
|
3502
|
+
value = self._evaluate(value_node) if value_node else None
|
|
3503
|
+
instance.set_member(var_name, value)
|
|
3504
|
+
|
|
3505
|
+
return instance
|
|
3506
|
+
|
|
2799
3507
|
def _exec_infuse_right(self, node: ASTNode) -> Any:
|
|
2800
3508
|
"""Execute right-side code infusion (==>>)"""
|
|
2801
3509
|
source = node.value.get('source')
|
|
@@ -2882,7 +3590,13 @@ class CSSLRuntime:
|
|
|
2882
3590
|
if self._current_instance is None:
|
|
2883
3591
|
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
2884
3592
|
member = target.value.get('member')
|
|
2885
|
-
self._current_instance
|
|
3593
|
+
instance = self._current_instance
|
|
3594
|
+
# Check if instance is a CSSL instance or a plain Python object
|
|
3595
|
+
if hasattr(instance, 'set_member'):
|
|
3596
|
+
instance.set_member(member, value)
|
|
3597
|
+
else:
|
|
3598
|
+
# Plain Python object - use setattr
|
|
3599
|
+
setattr(instance, member, value)
|
|
2886
3600
|
elif isinstance(target, str):
|
|
2887
3601
|
self.scope.set(target, value)
|
|
2888
3602
|
|
|
@@ -2997,6 +3711,31 @@ class CSSLRuntime:
|
|
|
2997
3711
|
|
|
2998
3712
|
if node.type == 'identifier':
|
|
2999
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
|
+
|
|
3000
3739
|
value = self.scope.get(name)
|
|
3001
3740
|
# Check if it's a class member in current instance context
|
|
3002
3741
|
# This allows accessing members without 'this->' inside methods
|
|
@@ -3070,14 +3809,22 @@ class CSSLRuntime:
|
|
|
3070
3809
|
|
|
3071
3810
|
if node.type == 'captured_ref':
|
|
3072
3811
|
# %<name> captured reference - use value captured at infusion registration time
|
|
3812
|
+
# Priority: The % prefix means "get the ORIGINAL value before any replacement"
|
|
3073
3813
|
name = node.value
|
|
3074
|
-
|
|
3814
|
+
|
|
3815
|
+
# 1. First check captured values from current injection context
|
|
3075
3816
|
if name in self._current_captured_values:
|
|
3076
3817
|
captured_value = self._current_captured_values[name]
|
|
3077
|
-
# Only use captured value if it's not None
|
|
3078
3818
|
if captured_value is not None:
|
|
3079
3819
|
return captured_value
|
|
3080
|
-
|
|
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
|
|
3081
3828
|
value = self.scope.get(name)
|
|
3082
3829
|
if value is None:
|
|
3083
3830
|
value = self.global_scope.get(name)
|
|
@@ -3088,9 +3835,6 @@ class CSSLRuntime:
|
|
|
3088
3835
|
value = lambda code=0, rt=runtime: rt.exit(code)
|
|
3089
3836
|
else:
|
|
3090
3837
|
value = getattr(self.builtins, f'builtin_{name}', None)
|
|
3091
|
-
if value is None:
|
|
3092
|
-
# Check original functions (for replaced functions)
|
|
3093
|
-
value = self._original_functions.get(name)
|
|
3094
3838
|
if value is not None:
|
|
3095
3839
|
return value
|
|
3096
3840
|
# Build helpful error for captured reference
|
|
@@ -3115,6 +3859,83 @@ class CSSLRuntime:
|
|
|
3115
3859
|
# Return None if instance doesn't exist (can be created via ==>)
|
|
3116
3860
|
return None
|
|
3117
3861
|
|
|
3862
|
+
# v4.1.0/v4.1.1: Cross-language instance reference: cpp$ClassName, py$Object
|
|
3863
|
+
# Enhanced bidirectional access with real runtime bridges
|
|
3864
|
+
if node.type == 'lang_instance_ref':
|
|
3865
|
+
ref = node.value # {'lang': 'cpp', 'instance': 'ClassName'}
|
|
3866
|
+
lang_id = ref['lang']
|
|
3867
|
+
instance_name = ref['instance']
|
|
3868
|
+
|
|
3869
|
+
# First, try to get the language support object from scope
|
|
3870
|
+
lang_support = self.scope.get(lang_id)
|
|
3871
|
+
if lang_support is None:
|
|
3872
|
+
lang_support = self.global_scope.get(lang_id)
|
|
3873
|
+
|
|
3874
|
+
# If not found in scope, try to get from modules
|
|
3875
|
+
if lang_support is None:
|
|
3876
|
+
lang_support = self._modules.get(lang_id)
|
|
3877
|
+
|
|
3878
|
+
# If still not found, try getting default language support
|
|
3879
|
+
if lang_support is None:
|
|
3880
|
+
from .cssl_languages import get_language
|
|
3881
|
+
lang_support = get_language(lang_id)
|
|
3882
|
+
|
|
3883
|
+
if lang_support is not None:
|
|
3884
|
+
# v4.1.1: Check if it's a LanguageSupport object with get_instance method
|
|
3885
|
+
if hasattr(lang_support, 'get_instance'):
|
|
3886
|
+
instance = lang_support.get_instance(instance_name)
|
|
3887
|
+
if instance is not None:
|
|
3888
|
+
return instance
|
|
3889
|
+
|
|
3890
|
+
# v4.1.1: For C++, also try to get class from loaded modules directly
|
|
3891
|
+
if hasattr(lang_support, '_get_bridge'):
|
|
3892
|
+
bridge = lang_support._get_bridge()
|
|
3893
|
+
if bridge is not None:
|
|
3894
|
+
# Try to get from bridge's instances
|
|
3895
|
+
bridge_instance = bridge.get_instance(instance_name)
|
|
3896
|
+
if bridge_instance is not None:
|
|
3897
|
+
return bridge_instance
|
|
3898
|
+
|
|
3899
|
+
# For C++: Try to access class from IncludeCPP modules
|
|
3900
|
+
if hasattr(bridge, '_modules'):
|
|
3901
|
+
for mod in bridge._modules.values():
|
|
3902
|
+
if hasattr(mod, instance_name):
|
|
3903
|
+
cls_or_instance = getattr(mod, instance_name)
|
|
3904
|
+
# Cache it for future access
|
|
3905
|
+
lang_support._instances[instance_name] = cls_or_instance
|
|
3906
|
+
return cls_or_instance
|
|
3907
|
+
|
|
3908
|
+
# Check _instances dict directly as fallback
|
|
3909
|
+
if hasattr(lang_support, '_instances'):
|
|
3910
|
+
if instance_name in lang_support._instances:
|
|
3911
|
+
return lang_support._instances[instance_name]
|
|
3912
|
+
|
|
3913
|
+
# Build helpful error message based on language
|
|
3914
|
+
if lang_id in ('cpp', 'c++'):
|
|
3915
|
+
hint = (f"For C++ access:\n"
|
|
3916
|
+
f" 1. Build your module with 'includecpp build'\n"
|
|
3917
|
+
f" 2. Use cpp.share(\"{instance_name}\", instance) to register\n"
|
|
3918
|
+
f" 3. Or access a class directly: obj = new cpp${instance_name}()")
|
|
3919
|
+
elif lang_id in ('java',):
|
|
3920
|
+
hint = (f"For Java access:\n"
|
|
3921
|
+
f" 1. Install JPype: pip install jpype1\n"
|
|
3922
|
+
f" 2. Add classpath: java.add_classpath(\"path/to/jar\")\n"
|
|
3923
|
+
f" 3. Load class: MyClass = java.load_class(\"com.example.{instance_name}\")\n"
|
|
3924
|
+
f" 4. Share instance: java.share(\"{instance_name}\", instance)")
|
|
3925
|
+
elif lang_id in ('js', 'javascript'):
|
|
3926
|
+
hint = (f"For JavaScript access:\n"
|
|
3927
|
+
f" 1. Make sure Node.js is installed\n"
|
|
3928
|
+
f" 2. Define in JS: js.eval(\"function {instance_name}() {{...}}\")\n"
|
|
3929
|
+
f" 3. Share result: js.share(\"{instance_name}\", result)")
|
|
3930
|
+
else:
|
|
3931
|
+
hint = f"Use '{lang_id}.share(\"{instance_name}\", instance)' to register the instance first."
|
|
3932
|
+
|
|
3933
|
+
raise self._format_error(
|
|
3934
|
+
node.line if hasattr(node, 'line') else 0,
|
|
3935
|
+
f"Cross-language instance '{lang_id}${instance_name}' not found",
|
|
3936
|
+
hint
|
|
3937
|
+
)
|
|
3938
|
+
|
|
3118
3939
|
if node.type == 'new':
|
|
3119
3940
|
# Create new instance of a class: new ClassName(args)
|
|
3120
3941
|
return self._eval_new(node)
|
|
@@ -3188,6 +4009,14 @@ class CSSLRuntime:
|
|
|
3188
4009
|
if node.type == 'unary':
|
|
3189
4010
|
return self._eval_unary(node)
|
|
3190
4011
|
|
|
4012
|
+
# Increment: ++i or i++
|
|
4013
|
+
if node.type == 'increment':
|
|
4014
|
+
return self._eval_increment(node)
|
|
4015
|
+
|
|
4016
|
+
# Decrement: --i or i--
|
|
4017
|
+
if node.type == 'decrement':
|
|
4018
|
+
return self._eval_decrement(node)
|
|
4019
|
+
|
|
3191
4020
|
if node.type == 'non_null_assert':
|
|
3192
4021
|
# *$var, *@module, *identifier - assert value is not null/None
|
|
3193
4022
|
operand = node.value.get('operand')
|
|
@@ -3341,15 +4170,21 @@ class CSSLRuntime:
|
|
|
3341
4170
|
|
|
3342
4171
|
if op == '/':
|
|
3343
4172
|
r = self._to_number(right)
|
|
3344
|
-
|
|
4173
|
+
if r == 0:
|
|
4174
|
+
raise CSSLRuntimeError("Division by zero")
|
|
4175
|
+
return self._to_number(left) / r
|
|
3345
4176
|
|
|
3346
4177
|
if op == '//':
|
|
3347
4178
|
r = self._to_number(right)
|
|
3348
|
-
|
|
4179
|
+
if r == 0:
|
|
4180
|
+
raise CSSLRuntimeError("Integer division by zero")
|
|
4181
|
+
return self._to_number(left) // r
|
|
3349
4182
|
|
|
3350
4183
|
if op == '%':
|
|
3351
4184
|
r = self._to_number(right)
|
|
3352
|
-
|
|
4185
|
+
if r == 0:
|
|
4186
|
+
raise CSSLRuntimeError("Modulo by zero")
|
|
4187
|
+
return self._to_number(left) % r
|
|
3353
4188
|
|
|
3354
4189
|
if op == '**':
|
|
3355
4190
|
return self._to_number(left) ** self._to_number(right)
|
|
@@ -3473,6 +4308,74 @@ class CSSLRuntime:
|
|
|
3473
4308
|
|
|
3474
4309
|
return None
|
|
3475
4310
|
|
|
4311
|
+
def _eval_increment(self, node: ASTNode) -> Any:
|
|
4312
|
+
"""Evaluate increment operation (++i or i++)"""
|
|
4313
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
4314
|
+
operand = node.value.get('operand')
|
|
4315
|
+
|
|
4316
|
+
# Get variable name
|
|
4317
|
+
var_name = None
|
|
4318
|
+
if isinstance(operand, ASTNode):
|
|
4319
|
+
if operand.type == 'identifier':
|
|
4320
|
+
var_name = operand.value
|
|
4321
|
+
elif operand.type == 'shared_ref':
|
|
4322
|
+
# Handle $var++
|
|
4323
|
+
var_name = operand.value
|
|
4324
|
+
current = self.shared_vars.get(var_name, 0)
|
|
4325
|
+
if op_type == 'prefix':
|
|
4326
|
+
self.shared_vars[var_name] = current + 1
|
|
4327
|
+
return current + 1
|
|
4328
|
+
else: # postfix
|
|
4329
|
+
self.shared_vars[var_name] = current + 1
|
|
4330
|
+
return current
|
|
4331
|
+
|
|
4332
|
+
if var_name:
|
|
4333
|
+
current = self.scope.get(var_name, 0)
|
|
4334
|
+
if op_type == 'prefix':
|
|
4335
|
+
# ++i: increment then return
|
|
4336
|
+
self.scope.set(var_name, current + 1)
|
|
4337
|
+
return current + 1
|
|
4338
|
+
else:
|
|
4339
|
+
# i++: return then increment
|
|
4340
|
+
self.scope.set(var_name, current + 1)
|
|
4341
|
+
return current
|
|
4342
|
+
|
|
4343
|
+
return None
|
|
4344
|
+
|
|
4345
|
+
def _eval_decrement(self, node: ASTNode) -> Any:
|
|
4346
|
+
"""Evaluate decrement operation (--i or i--)"""
|
|
4347
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
4348
|
+
operand = node.value.get('operand')
|
|
4349
|
+
|
|
4350
|
+
# Get variable name
|
|
4351
|
+
var_name = None
|
|
4352
|
+
if isinstance(operand, ASTNode):
|
|
4353
|
+
if operand.type == 'identifier':
|
|
4354
|
+
var_name = operand.value
|
|
4355
|
+
elif operand.type == 'shared_ref':
|
|
4356
|
+
# Handle $var--
|
|
4357
|
+
var_name = operand.value
|
|
4358
|
+
current = self.shared_vars.get(var_name, 0)
|
|
4359
|
+
if op_type == 'prefix':
|
|
4360
|
+
self.shared_vars[var_name] = current - 1
|
|
4361
|
+
return current - 1
|
|
4362
|
+
else: # postfix
|
|
4363
|
+
self.shared_vars[var_name] = current - 1
|
|
4364
|
+
return current
|
|
4365
|
+
|
|
4366
|
+
if var_name:
|
|
4367
|
+
current = self.scope.get(var_name, 0)
|
|
4368
|
+
if op_type == 'prefix':
|
|
4369
|
+
# --i: decrement then return
|
|
4370
|
+
self.scope.set(var_name, current - 1)
|
|
4371
|
+
return current - 1
|
|
4372
|
+
else:
|
|
4373
|
+
# i--: return then decrement
|
|
4374
|
+
self.scope.set(var_name, current - 1)
|
|
4375
|
+
return current
|
|
4376
|
+
|
|
4377
|
+
return None
|
|
4378
|
+
|
|
3476
4379
|
def _eval_call(self, node: ASTNode) -> Any:
|
|
3477
4380
|
"""Evaluate function call with optional named arguments"""
|
|
3478
4381
|
callee_node = node.value.get('callee')
|
|
@@ -3516,7 +4419,33 @@ class CSSLRuntime:
|
|
|
3516
4419
|
if isinstance(callee, ASTNode) and callee.type == 'function':
|
|
3517
4420
|
return self._call_function(callee, args, kwargs)
|
|
3518
4421
|
|
|
3519
|
-
|
|
4422
|
+
# Extract callee name for error messages
|
|
4423
|
+
if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value'):
|
|
4424
|
+
val = callee_node.value
|
|
4425
|
+
if isinstance(val, str):
|
|
4426
|
+
callee_name = val
|
|
4427
|
+
elif isinstance(val, dict):
|
|
4428
|
+
# For member access nodes like obj.method, get the member name
|
|
4429
|
+
if 'member' in val:
|
|
4430
|
+
obj_node = val.get('object')
|
|
4431
|
+
member = val.get('member')
|
|
4432
|
+
obj_name = obj_node.value if isinstance(obj_node, ASTNode) else str(obj_node)
|
|
4433
|
+
callee_name = f"{obj_name}.{member}"
|
|
4434
|
+
# For call nodes, try to get the callee name
|
|
4435
|
+
elif 'callee' in val:
|
|
4436
|
+
callee_val = val.get('callee')
|
|
4437
|
+
if isinstance(callee_val, ASTNode):
|
|
4438
|
+
callee_name = callee_val.value if isinstance(callee_val.value, str) else str(callee_val.value)
|
|
4439
|
+
else:
|
|
4440
|
+
callee_name = str(callee_val)
|
|
4441
|
+
elif 'name' in val:
|
|
4442
|
+
callee_name = str(val.get('name'))
|
|
4443
|
+
else:
|
|
4444
|
+
callee_name = str(val)
|
|
4445
|
+
else:
|
|
4446
|
+
callee_name = str(val)
|
|
4447
|
+
else:
|
|
4448
|
+
callee_name = str(callee_node)
|
|
3520
4449
|
|
|
3521
4450
|
# Build detailed error with suggestions
|
|
3522
4451
|
available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
|
|
@@ -3609,30 +4538,44 @@ class CSSLRuntime:
|
|
|
3609
4538
|
)
|
|
3610
4539
|
|
|
3611
4540
|
def _eval_new(self, node: ASTNode) -> CSSLInstance:
|
|
3612
|
-
"""Evaluate 'new ClassName(args)' or 'new @ClassName(args)' expression.
|
|
4541
|
+
"""Evaluate 'new ClassName(args)' or 'new @ClassName(args)' or 'new Namespace::ClassName(args)' expression.
|
|
3613
4542
|
|
|
3614
4543
|
Creates a new instance of a CSSL class and calls its constructor.
|
|
3615
4544
|
Supports multiple constructors (constr keyword), class parameters,
|
|
3616
4545
|
and automatic parent constructor calling.
|
|
3617
4546
|
|
|
3618
4547
|
With '@' prefix (new @ClassName), looks only in global scope.
|
|
4548
|
+
With Namespace:: prefix, looks in the namespace dict first.
|
|
3619
4549
|
"""
|
|
3620
4550
|
class_name = node.value.get('class')
|
|
4551
|
+
namespace = node.value.get('namespace') # v4.2.6: Namespace::ClassName support
|
|
3621
4552
|
is_global_ref = node.value.get('is_global_ref', False)
|
|
3622
4553
|
args = [self._evaluate(arg) for arg in node.value.get('args', [])]
|
|
3623
4554
|
kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
|
|
3624
4555
|
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
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)
|
|
3636
4579
|
|
|
3637
4580
|
if class_def is None:
|
|
3638
4581
|
# Build detailed error with suggestions
|
|
@@ -3658,6 +4601,22 @@ class CSSLRuntime:
|
|
|
3658
4601
|
hint
|
|
3659
4602
|
)
|
|
3660
4603
|
|
|
4604
|
+
# If we got a variable that holds a class reference, use that class
|
|
4605
|
+
if not isinstance(class_def, CSSLClass):
|
|
4606
|
+
# Check if it's a variable holding a class (dynamic class instantiation)
|
|
4607
|
+
if hasattr(class_def, '__class__') and isinstance(class_def, CSSLClass):
|
|
4608
|
+
pass # Already a CSSLClass, continue
|
|
4609
|
+
elif isinstance(class_def, dict) and 'class_def' in class_def:
|
|
4610
|
+
# Injected class reference from +<== operator
|
|
4611
|
+
class_def = class_def['class_def']
|
|
4612
|
+
else:
|
|
4613
|
+
# Not a class - show error
|
|
4614
|
+
raise CSSLRuntimeError(
|
|
4615
|
+
f"'{class_name}' is not a class",
|
|
4616
|
+
node.line,
|
|
4617
|
+
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
4618
|
+
)
|
|
4619
|
+
|
|
3661
4620
|
if not isinstance(class_def, CSSLClass):
|
|
3662
4621
|
raise CSSLRuntimeError(
|
|
3663
4622
|
f"'{class_name}' is not a class",
|
|
@@ -3665,6 +4624,18 @@ class CSSLRuntime:
|
|
|
3665
4624
|
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
3666
4625
|
)
|
|
3667
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
|
+
|
|
3668
4639
|
# Create new instance
|
|
3669
4640
|
instance = CSSLInstance(class_def)
|
|
3670
4641
|
|
|
@@ -3955,24 +4926,40 @@ class CSSLRuntime:
|
|
|
3955
4926
|
# Direct this->member access
|
|
3956
4927
|
instance = self._current_instance
|
|
3957
4928
|
|
|
3958
|
-
# Check if
|
|
3959
|
-
|
|
3960
|
-
|
|
4929
|
+
# Check if instance is a CSSL instance or a plain Python object
|
|
4930
|
+
is_cssl_instance = hasattr(instance, 'has_member') and hasattr(instance, 'has_method')
|
|
4931
|
+
|
|
4932
|
+
if is_cssl_instance:
|
|
4933
|
+
# CSSL instance - use CSSL methods
|
|
4934
|
+
if instance.has_member(member):
|
|
4935
|
+
return instance.get_member(member)
|
|
3961
4936
|
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
4937
|
+
# Check if it's a method
|
|
4938
|
+
if instance.has_method(member):
|
|
4939
|
+
# Return a callable that will invoke the method with instance context
|
|
4940
|
+
method_node = instance.get_method(member)
|
|
4941
|
+
# Check if this is an inherited Python method
|
|
4942
|
+
if isinstance(method_node, tuple) and method_node[0] == 'python_method':
|
|
4943
|
+
python_method = method_node[1]
|
|
4944
|
+
return lambda *args, **kwargs: python_method(*args, **kwargs)
|
|
4945
|
+
return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
|
|
4946
|
+
else:
|
|
4947
|
+
# Plain Python object - use standard attribute access
|
|
4948
|
+
if hasattr(instance, member):
|
|
4949
|
+
return getattr(instance, member)
|
|
4950
|
+
# Also check __dict__ for dynamic attributes
|
|
4951
|
+
if hasattr(instance, '__dict__') and member in instance.__dict__:
|
|
4952
|
+
return instance.__dict__[member]
|
|
3971
4953
|
|
|
3972
4954
|
# Build helpful error with available members
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
4955
|
+
if is_cssl_instance:
|
|
4956
|
+
class_name = instance._class.name
|
|
4957
|
+
available_members = list(instance._members.keys()) if hasattr(instance, '_members') else []
|
|
4958
|
+
available_methods = list(instance._methods.keys()) if hasattr(instance, '_methods') else []
|
|
4959
|
+
else:
|
|
4960
|
+
class_name = type(instance).__name__
|
|
4961
|
+
available_members = [k for k in dir(instance) if not k.startswith('_')]
|
|
4962
|
+
available_methods = []
|
|
3976
4963
|
all_available = available_members + available_methods
|
|
3977
4964
|
similar = _find_similar_names(member, all_available)
|
|
3978
4965
|
|
|
@@ -4112,6 +5099,48 @@ class CSSLRuntime:
|
|
|
4112
5099
|
hint
|
|
4113
5100
|
)
|
|
4114
5101
|
|
|
5102
|
+
# === UNIVERSAL INSTANCE METHODS ===
|
|
5103
|
+
from .cssl_types import UniversalInstance
|
|
5104
|
+
if isinstance(obj, UniversalInstance):
|
|
5105
|
+
# Check for member variable
|
|
5106
|
+
if obj.has_member(member):
|
|
5107
|
+
return obj.get_member(member)
|
|
5108
|
+
# Check for method
|
|
5109
|
+
if obj.has_method(member):
|
|
5110
|
+
method_node = obj.get_method(member)
|
|
5111
|
+
# Create a callable that executes the method in context
|
|
5112
|
+
def instance_method_caller(*args, **kwargs):
|
|
5113
|
+
# Set 'this' to refer to the instance
|
|
5114
|
+
old_this = self.scope.get('this')
|
|
5115
|
+
self.scope.set('this', obj)
|
|
5116
|
+
try:
|
|
5117
|
+
return self._call_function(method_node, list(args))
|
|
5118
|
+
finally:
|
|
5119
|
+
if old_this is not None:
|
|
5120
|
+
self.scope.set('this', old_this)
|
|
5121
|
+
else:
|
|
5122
|
+
self.scope.remove('this') if hasattr(self.scope, 'remove') else None
|
|
5123
|
+
return instance_method_caller
|
|
5124
|
+
# Build helpful error with available members
|
|
5125
|
+
instance_name = obj.name
|
|
5126
|
+
available_members = list(obj.get_all_members().keys())
|
|
5127
|
+
available_methods = list(obj.get_all_methods().keys())
|
|
5128
|
+
all_available = available_members + available_methods
|
|
5129
|
+
similar = _find_similar_names(member, all_available)
|
|
5130
|
+
|
|
5131
|
+
if similar:
|
|
5132
|
+
hint = f"Did you mean: {', '.join(similar)}?"
|
|
5133
|
+
elif all_available:
|
|
5134
|
+
hint = f"Available: {', '.join(all_available[:5])}"
|
|
5135
|
+
else:
|
|
5136
|
+
hint = f"Instance '{instance_name}' has no accessible members."
|
|
5137
|
+
|
|
5138
|
+
raise self._format_error(
|
|
5139
|
+
node.line,
|
|
5140
|
+
f"Instance '{instance_name}' has no member or method '{member}'",
|
|
5141
|
+
hint
|
|
5142
|
+
)
|
|
5143
|
+
|
|
4115
5144
|
# === STRING METHODS ===
|
|
4116
5145
|
if isinstance(obj, str):
|
|
4117
5146
|
string_methods = self._get_string_method(obj, member)
|
|
@@ -4330,12 +5359,19 @@ class CSSLRuntime:
|
|
|
4330
5359
|
index = self._evaluate(node.value.get('index'))
|
|
4331
5360
|
|
|
4332
5361
|
if obj is None:
|
|
4333
|
-
|
|
5362
|
+
raise CSSLRuntimeError(f"Cannot index into None/null value")
|
|
4334
5363
|
|
|
4335
5364
|
try:
|
|
4336
5365
|
return obj[index]
|
|
4337
|
-
except
|
|
4338
|
-
|
|
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}")
|
|
4339
5375
|
|
|
4340
5376
|
def _set_member(self, node: ASTNode, value: Any):
|
|
4341
5377
|
"""Set member value"""
|
|
@@ -4350,6 +5386,12 @@ class CSSLRuntime:
|
|
|
4350
5386
|
obj.set_member(member, value)
|
|
4351
5387
|
return
|
|
4352
5388
|
|
|
5389
|
+
# Check for UniversalInstance - use set_member method
|
|
5390
|
+
from .cssl_types import UniversalInstance
|
|
5391
|
+
if isinstance(obj, UniversalInstance):
|
|
5392
|
+
obj.set_member(member, value)
|
|
5393
|
+
return
|
|
5394
|
+
|
|
4353
5395
|
# Check for SharedObjectProxy - directly access underlying object
|
|
4354
5396
|
# This is more robust than relying on the proxy's __setattr__
|
|
4355
5397
|
if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
|
|
@@ -4623,10 +5665,17 @@ class CSSLRuntime:
|
|
|
4623
5665
|
|
|
4624
5666
|
def _emit_output(self, text: str, level: str = 'normal'):
|
|
4625
5667
|
"""Emit output through callback or print"""
|
|
5668
|
+
import sys
|
|
4626
5669
|
if self._output_callback:
|
|
4627
5670
|
self._output_callback(text, level)
|
|
4628
5671
|
else:
|
|
4629
|
-
|
|
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='')
|
|
4630
5679
|
|
|
4631
5680
|
def output(self, text: str):
|
|
4632
5681
|
"""Output text"""
|