IncludeCPP 4.0.0__py3-none-any.whl → 4.2.2__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 +175 -0
- includecpp/DOCUMENTATION.md +593 -0
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +4 -1
- includecpp/cli/commands.py +705 -98
- includecpp/core/ai_integration.py +46 -13
- includecpp/core/cpp_api_extensions.pyi +350 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +186 -5
- includecpp/core/cssl/cssl_builtins.py +101 -4
- includecpp/core/cssl/cssl_languages.py +1757 -0
- includecpp/core/cssl/cssl_parser.py +437 -104
- includecpp/core/cssl/cssl_runtime.py +865 -62
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +172 -1
- includecpp/core/cssl_bridge.py +198 -12
- includecpp/core/cssl_bridge.pyi +717 -186
- 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 +140 -17
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/METADATA +102 -1
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/RECORD +26 -22
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/WHEEL +0 -0
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.0.dist-info → includecpp-4.2.2.dist-info}/top_level.txt +0 -0
|
@@ -68,11 +68,11 @@ class CSSLRuntimeError(Exception):
|
|
|
68
68
|
# Build detailed error message
|
|
69
69
|
error_parts = []
|
|
70
70
|
|
|
71
|
-
# Main error message
|
|
71
|
+
# Main error message (no "Error:" prefix - CLI handles that)
|
|
72
72
|
if line:
|
|
73
|
-
error_parts.append(f"
|
|
73
|
+
error_parts.append(f"Line {line}: {message}")
|
|
74
74
|
else:
|
|
75
|
-
error_parts.append(
|
|
75
|
+
error_parts.append(message)
|
|
76
76
|
|
|
77
77
|
# Add context if available
|
|
78
78
|
if context:
|
|
@@ -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:
|
|
@@ -363,11 +383,11 @@ class CSSLRuntime:
|
|
|
363
383
|
"""Format a detailed error with source context"""
|
|
364
384
|
error_parts = []
|
|
365
385
|
|
|
366
|
-
# Main error header
|
|
386
|
+
# Main error header (no "Error:" prefix - CLI handles that)
|
|
367
387
|
if line and line > 0:
|
|
368
|
-
error_parts.append(f"
|
|
388
|
+
error_parts.append(f"Line {line} in {self._current_file}:")
|
|
369
389
|
else:
|
|
370
|
-
error_parts.append(f"
|
|
390
|
+
error_parts.append(f"In {self._current_file}:")
|
|
371
391
|
|
|
372
392
|
# Extract message without existing line info
|
|
373
393
|
clean_msg = message
|
|
@@ -863,6 +883,18 @@ class CSSLRuntime:
|
|
|
863
883
|
class_params = class_info.get('class_params', [])
|
|
864
884
|
extends_args = class_info.get('extends_args', [])
|
|
865
885
|
|
|
886
|
+
# v4.2.0: Handle 'supports' language transformation for raw_body
|
|
887
|
+
supports_language = class_info.get('supports_language')
|
|
888
|
+
raw_body = class_info.get('raw_body')
|
|
889
|
+
|
|
890
|
+
if raw_body and supports_language:
|
|
891
|
+
# Transform raw body from target language to CSSL and parse
|
|
892
|
+
transformed_children = self._transform_and_parse_class_body(
|
|
893
|
+
raw_body, supports_language, class_name
|
|
894
|
+
)
|
|
895
|
+
# Add transformed children to node's children
|
|
896
|
+
node.children = transformed_children
|
|
897
|
+
|
|
866
898
|
for child in node.children:
|
|
867
899
|
if child.type == 'constructor':
|
|
868
900
|
# New-style constructor from 'constr' keyword
|
|
@@ -987,6 +1019,119 @@ class CSSLRuntime:
|
|
|
987
1019
|
self._current_instance = old_instance
|
|
988
1020
|
return wrapper
|
|
989
1021
|
|
|
1022
|
+
def _transform_and_parse_class_body(self, raw_body: str, language: str, class_name: str) -> list:
|
|
1023
|
+
"""Transform source code from another language to CSSL and parse as class body.
|
|
1024
|
+
|
|
1025
|
+
v4.2.0: Used for 'supports <lang>' in class definitions.
|
|
1026
|
+
|
|
1027
|
+
Args:
|
|
1028
|
+
raw_body: Raw source code in the target language
|
|
1029
|
+
language: Language identifier (py, python, cpp, c++, js, javascript, etc.)
|
|
1030
|
+
class_name: Name of the class (for constructor recognition)
|
|
1031
|
+
|
|
1032
|
+
Returns:
|
|
1033
|
+
List of parsed AST nodes representing methods, constructors, and members
|
|
1034
|
+
"""
|
|
1035
|
+
import textwrap
|
|
1036
|
+
from .cssl_languages import get_language
|
|
1037
|
+
from .cssl_parser import parse_cssl_program, ASTNode
|
|
1038
|
+
|
|
1039
|
+
# Normalize language ID
|
|
1040
|
+
lang_id = language.lstrip('@').lower()
|
|
1041
|
+
|
|
1042
|
+
# Get language support and transformer
|
|
1043
|
+
lang_support = get_language(lang_id)
|
|
1044
|
+
if lang_support is None:
|
|
1045
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' clause")
|
|
1046
|
+
|
|
1047
|
+
# Dedent the raw body to normalize indentation
|
|
1048
|
+
# This fixes the issue where code inside CSSL {} has relative indentation
|
|
1049
|
+
dedented_body = textwrap.dedent(raw_body)
|
|
1050
|
+
|
|
1051
|
+
# Transform the raw body to CSSL syntax
|
|
1052
|
+
transformer = lang_support.get_transformer()
|
|
1053
|
+
transformed_source = transformer.transform_source(dedented_body)
|
|
1054
|
+
|
|
1055
|
+
# Wrap in a dummy class for parsing
|
|
1056
|
+
wrapper_source = f"class _TempClass {{\n{transformed_source}\n}}"
|
|
1057
|
+
|
|
1058
|
+
try:
|
|
1059
|
+
ast = parse_cssl_program(wrapper_source)
|
|
1060
|
+
except Exception as e:
|
|
1061
|
+
raise CSSLRuntimeError(
|
|
1062
|
+
f"Failed to parse transformed '{lang_id}' code: {e}\n"
|
|
1063
|
+
f"Dedented:\n{dedented_body}\n"
|
|
1064
|
+
f"Transformed:\n{transformed_source}"
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
# Extract children from the parsed temp class
|
|
1068
|
+
children = []
|
|
1069
|
+
for top_level in ast.children:
|
|
1070
|
+
if top_level.type == 'class':
|
|
1071
|
+
for child in top_level.children:
|
|
1072
|
+
# Mark constructor if method name matches class_name or is __init__
|
|
1073
|
+
if child.type == 'function':
|
|
1074
|
+
func_info = child.value
|
|
1075
|
+
method_name = func_info.get('name')
|
|
1076
|
+
if method_name == class_name or method_name == '__init__':
|
|
1077
|
+
child.value['is_constructor'] = True
|
|
1078
|
+
children.append(child)
|
|
1079
|
+
break
|
|
1080
|
+
|
|
1081
|
+
return children
|
|
1082
|
+
|
|
1083
|
+
def _transform_and_parse_function_body(self, raw_body: str, language: str) -> list:
|
|
1084
|
+
"""Transform source code from another language to CSSL and parse as function body.
|
|
1085
|
+
|
|
1086
|
+
v4.2.0: Used for 'supports <lang>' in function definitions.
|
|
1087
|
+
|
|
1088
|
+
Args:
|
|
1089
|
+
raw_body: Raw source code in the target language
|
|
1090
|
+
language: Language identifier (py, python, cpp, c++, js, javascript, etc.)
|
|
1091
|
+
|
|
1092
|
+
Returns:
|
|
1093
|
+
List of parsed AST nodes representing statements in the function body
|
|
1094
|
+
"""
|
|
1095
|
+
import textwrap
|
|
1096
|
+
from .cssl_languages import get_language
|
|
1097
|
+
from .cssl_parser import parse_cssl_program
|
|
1098
|
+
|
|
1099
|
+
# Normalize language ID
|
|
1100
|
+
lang_id = language.lstrip('@').lower()
|
|
1101
|
+
|
|
1102
|
+
# Get language support and transformer
|
|
1103
|
+
lang_support = get_language(lang_id)
|
|
1104
|
+
if lang_support is None:
|
|
1105
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' clause")
|
|
1106
|
+
|
|
1107
|
+
# Dedent the raw body to normalize indentation
|
|
1108
|
+
dedented_body = textwrap.dedent(raw_body)
|
|
1109
|
+
|
|
1110
|
+
# Transform the raw body to CSSL syntax
|
|
1111
|
+
transformer = lang_support.get_transformer()
|
|
1112
|
+
transformed_source = transformer.transform_source(dedented_body)
|
|
1113
|
+
|
|
1114
|
+
# Wrap in a dummy function for parsing
|
|
1115
|
+
wrapper_source = f"define _TempFunc() {{\n{transformed_source}\n}}"
|
|
1116
|
+
|
|
1117
|
+
try:
|
|
1118
|
+
ast = parse_cssl_program(wrapper_source)
|
|
1119
|
+
except Exception as e:
|
|
1120
|
+
raise CSSLRuntimeError(
|
|
1121
|
+
f"Failed to parse transformed '{lang_id}' code: {e}\n"
|
|
1122
|
+
f"Dedented:\n{dedented_body}\n"
|
|
1123
|
+
f"Transformed:\n{transformed_source}"
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
# Extract children from the parsed temp function
|
|
1127
|
+
children = []
|
|
1128
|
+
for top_level in ast.children:
|
|
1129
|
+
if top_level.type == 'function':
|
|
1130
|
+
children = top_level.children
|
|
1131
|
+
break
|
|
1132
|
+
|
|
1133
|
+
return children
|
|
1134
|
+
|
|
990
1135
|
def _exec_function(self, node: ASTNode) -> Any:
|
|
991
1136
|
"""Execute function definition - registers it and handles extends/overwrites.
|
|
992
1137
|
|
|
@@ -1005,13 +1150,29 @@ class CSSLRuntime:
|
|
|
1005
1150
|
overwrites_func = func_info.get('overwrites')
|
|
1006
1151
|
overwrites_is_python = func_info.get('overwrites_is_python', False)
|
|
1007
1152
|
|
|
1153
|
+
# Get append/overwrite reference info (&Class::method syntax)
|
|
1154
|
+
append_mode = func_info.get('append_mode', False)
|
|
1155
|
+
append_ref_class = func_info.get('append_ref_class')
|
|
1156
|
+
append_ref_member = func_info.get('append_ref_member')
|
|
1157
|
+
|
|
1008
1158
|
# Store function extends info for runtime use
|
|
1009
1159
|
if extends_func:
|
|
1010
1160
|
node.value['_extends_resolved'] = self._resolve_function_target(
|
|
1011
1161
|
extends_func, extends_is_python
|
|
1012
1162
|
)
|
|
1013
1163
|
|
|
1014
|
-
# Handle
|
|
1164
|
+
# Handle &Class::method syntax
|
|
1165
|
+
# Without ++ = full replacement
|
|
1166
|
+
# With ++ = append (run original first, then new code)
|
|
1167
|
+
if append_ref_class:
|
|
1168
|
+
if append_mode:
|
|
1169
|
+
# Append mode: wrap original to run original + new
|
|
1170
|
+
self._append_to_target(append_ref_class, append_ref_member, node)
|
|
1171
|
+
else:
|
|
1172
|
+
# Full replacement
|
|
1173
|
+
self._overwrite_target(append_ref_class, append_ref_member, node)
|
|
1174
|
+
|
|
1175
|
+
# Handle overwrites keyword - replace the target function
|
|
1015
1176
|
if overwrites_func:
|
|
1016
1177
|
target = self._resolve_function_target(overwrites_func, overwrites_is_python)
|
|
1017
1178
|
if target is not None:
|
|
@@ -1054,6 +1215,148 @@ class CSSLRuntime:
|
|
|
1054
1215
|
return self._call_function(func_node, list(args), kwargs)
|
|
1055
1216
|
return wrapper
|
|
1056
1217
|
|
|
1218
|
+
def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
|
|
1219
|
+
"""Overwrite a class method or function with the replacement.
|
|
1220
|
+
|
|
1221
|
+
Handles:
|
|
1222
|
+
- &ClassName::method - Overwrite method in CSSL class
|
|
1223
|
+
- &$PyObject.method - Overwrite method in Python shared object
|
|
1224
|
+
- &functionName - Overwrite standalone function
|
|
1225
|
+
"""
|
|
1226
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1227
|
+
|
|
1228
|
+
# Handle Python shared objects
|
|
1229
|
+
if ref_class.startswith('$'):
|
|
1230
|
+
var_name = ref_class[1:]
|
|
1231
|
+
ref_obj = _live_objects.get(var_name)
|
|
1232
|
+
if ref_obj is None:
|
|
1233
|
+
ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
|
|
1234
|
+
|
|
1235
|
+
if ref_obj is None:
|
|
1236
|
+
return
|
|
1237
|
+
|
|
1238
|
+
# Unwrap SharedObjectProxy
|
|
1239
|
+
if isinstance(ref_obj, SharedObjectProxy):
|
|
1240
|
+
ref_obj = ref_obj._obj
|
|
1241
|
+
|
|
1242
|
+
# Overwrite Python object method
|
|
1243
|
+
if ref_member and hasattr(ref_obj, ref_member):
|
|
1244
|
+
wrapper = self._create_python_wrapper(replacement_node)
|
|
1245
|
+
try:
|
|
1246
|
+
setattr(ref_obj, ref_member, wrapper)
|
|
1247
|
+
except (AttributeError, TypeError):
|
|
1248
|
+
pass # Can't overwrite (immutable or builtin)
|
|
1249
|
+
return
|
|
1250
|
+
|
|
1251
|
+
# Handle CSSL class method overwrite
|
|
1252
|
+
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1253
|
+
if target_class is None:
|
|
1254
|
+
# Maybe it's a standalone function reference (no ::member)
|
|
1255
|
+
if ref_member is None:
|
|
1256
|
+
# &functionName - overwrite the function
|
|
1257
|
+
self.scope.set(ref_class, replacement_node)
|
|
1258
|
+
self.global_scope.set(ref_class, replacement_node)
|
|
1259
|
+
return
|
|
1260
|
+
|
|
1261
|
+
if isinstance(target_class, CSSLClass) and ref_member:
|
|
1262
|
+
# Overwrite method in the class
|
|
1263
|
+
if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
|
|
1264
|
+
target_class.methods[ref_member] = replacement_node
|
|
1265
|
+
# Also check members list
|
|
1266
|
+
if hasattr(target_class, 'members'):
|
|
1267
|
+
for i, member in enumerate(target_class.members):
|
|
1268
|
+
if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
|
|
1269
|
+
target_class.members[i] = replacement_node
|
|
1270
|
+
break
|
|
1271
|
+
|
|
1272
|
+
def _append_to_target(self, ref_class: str, ref_member: str, append_node: ASTNode):
|
|
1273
|
+
"""Append new code to an existing class method or function.
|
|
1274
|
+
|
|
1275
|
+
Creates a wrapper that runs original first, then the appended code.
|
|
1276
|
+
Handles:
|
|
1277
|
+
- &ClassName::method ++ - Append to method in CSSL class
|
|
1278
|
+
- &$PyObject.method ++ - Append to method in Python shared object
|
|
1279
|
+
- &functionName ++ - Append to standalone function
|
|
1280
|
+
"""
|
|
1281
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1282
|
+
|
|
1283
|
+
# Handle Python shared objects
|
|
1284
|
+
if ref_class.startswith('$'):
|
|
1285
|
+
var_name = ref_class[1:]
|
|
1286
|
+
ref_obj = _live_objects.get(var_name)
|
|
1287
|
+
if ref_obj is None:
|
|
1288
|
+
ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
|
|
1289
|
+
|
|
1290
|
+
if ref_obj is None:
|
|
1291
|
+
return
|
|
1292
|
+
|
|
1293
|
+
if isinstance(ref_obj, SharedObjectProxy):
|
|
1294
|
+
ref_obj = ref_obj._obj
|
|
1295
|
+
|
|
1296
|
+
# Create wrapper that calls original + new
|
|
1297
|
+
if ref_member and hasattr(ref_obj, ref_member):
|
|
1298
|
+
original_method = getattr(ref_obj, ref_member)
|
|
1299
|
+
runtime = self
|
|
1300
|
+
# Store the original method before wrapping
|
|
1301
|
+
_saved_original = original_method
|
|
1302
|
+
def appended_wrapper(*args, **kwargs):
|
|
1303
|
+
# Run original first (use saved reference to avoid recursion)
|
|
1304
|
+
result = None
|
|
1305
|
+
if callable(_saved_original):
|
|
1306
|
+
try:
|
|
1307
|
+
result = _saved_original(*args, **kwargs)
|
|
1308
|
+
except:
|
|
1309
|
+
pass
|
|
1310
|
+
# Then run appended code - disable append_mode to prevent recursion
|
|
1311
|
+
append_node.value['append_mode'] = False
|
|
1312
|
+
try:
|
|
1313
|
+
return runtime._call_function(append_node, list(args), kwargs)
|
|
1314
|
+
finally:
|
|
1315
|
+
append_node.value['append_mode'] = True
|
|
1316
|
+
try:
|
|
1317
|
+
setattr(ref_obj, ref_member, appended_wrapper)
|
|
1318
|
+
except (AttributeError, TypeError):
|
|
1319
|
+
pass
|
|
1320
|
+
return
|
|
1321
|
+
|
|
1322
|
+
# Handle CSSL class method append
|
|
1323
|
+
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1324
|
+
if target_class is None:
|
|
1325
|
+
# Standalone function append
|
|
1326
|
+
if ref_member is None:
|
|
1327
|
+
original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1328
|
+
if original_func:
|
|
1329
|
+
# Store original in append_node so it can run first
|
|
1330
|
+
append_node.value['_original_func'] = original_func
|
|
1331
|
+
self.scope.set(ref_class, append_node)
|
|
1332
|
+
self.global_scope.set(ref_class, append_node)
|
|
1333
|
+
return
|
|
1334
|
+
|
|
1335
|
+
if isinstance(target_class, CSSLClass) and ref_member:
|
|
1336
|
+
# Find original method
|
|
1337
|
+
original_method = None
|
|
1338
|
+
if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
|
|
1339
|
+
original_method = target_class.methods.get(ref_member)
|
|
1340
|
+
|
|
1341
|
+
if original_method is None and hasattr(target_class, 'members'):
|
|
1342
|
+
for member in target_class.members:
|
|
1343
|
+
if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
|
|
1344
|
+
original_method = member
|
|
1345
|
+
break
|
|
1346
|
+
|
|
1347
|
+
# Store original in append_node for runtime execution
|
|
1348
|
+
if original_method:
|
|
1349
|
+
append_node.value['_original_method'] = original_method
|
|
1350
|
+
|
|
1351
|
+
# Replace with append_node (which will call original first via _call_function)
|
|
1352
|
+
if hasattr(target_class, 'methods') and isinstance(target_class.methods, dict):
|
|
1353
|
+
target_class.methods[ref_member] = append_node
|
|
1354
|
+
if hasattr(target_class, 'members'):
|
|
1355
|
+
for i, member in enumerate(target_class.members):
|
|
1356
|
+
if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
|
|
1357
|
+
target_class.members[i] = append_node
|
|
1358
|
+
break
|
|
1359
|
+
|
|
1057
1360
|
def _exec_typed_declaration(self, node: ASTNode) -> Any:
|
|
1058
1361
|
"""Execute typed variable declaration: type<T> varName = value;
|
|
1059
1362
|
|
|
@@ -1158,29 +1461,38 @@ class CSSLRuntime:
|
|
|
1158
1461
|
def _exec_instance_declaration(self, node: ASTNode) -> Any:
|
|
1159
1462
|
"""Execute instance declaration: instance<"name"> varName;
|
|
1160
1463
|
|
|
1161
|
-
Gets or creates a shared instance by name.
|
|
1464
|
+
Gets or creates a universal shared instance by name.
|
|
1465
|
+
Universal instances are accessible from CSSL, Python, and C++.
|
|
1466
|
+
|
|
1467
|
+
Usage:
|
|
1468
|
+
instance<"myContainer"> container; // Creates or gets instance
|
|
1469
|
+
container.member = "value"; // Set member
|
|
1470
|
+
container +<<== { void func() {} } // Inject methods
|
|
1162
1471
|
"""
|
|
1163
|
-
from
|
|
1472
|
+
from .cssl_types import UniversalInstance
|
|
1473
|
+
|
|
1164
1474
|
decl = node.value
|
|
1165
1475
|
instance_name = decl.get('instance_name')
|
|
1166
1476
|
var_name = decl.get('name')
|
|
1167
1477
|
value_node = decl.get('value')
|
|
1168
1478
|
|
|
1169
|
-
# Get existing
|
|
1170
|
-
instance =
|
|
1171
|
-
if instance_name in _live_objects:
|
|
1172
|
-
instance = SharedObjectProxy(instance_name, _live_objects[instance_name])
|
|
1173
|
-
elif self.global_scope.has(f'${instance_name}'):
|
|
1174
|
-
instance = self.global_scope.get(f'${instance_name}')
|
|
1479
|
+
# Get existing or create new universal instance
|
|
1480
|
+
instance = UniversalInstance.get_or_create(instance_name)
|
|
1175
1481
|
|
|
1176
|
-
# If value is provided,
|
|
1482
|
+
# If value is provided, set it as initial content
|
|
1177
1483
|
if value_node:
|
|
1178
|
-
|
|
1179
|
-
#
|
|
1180
|
-
|
|
1484
|
+
initial_value = self._evaluate(value_node)
|
|
1485
|
+
# If it's a dict, set all keys as members
|
|
1486
|
+
if isinstance(initial_value, dict):
|
|
1487
|
+
for key, val in initial_value.items():
|
|
1488
|
+
instance.set_member(key, val)
|
|
1489
|
+
else:
|
|
1490
|
+
instance.set_member('value', initial_value)
|
|
1181
1491
|
|
|
1182
|
-
# Store in scope
|
|
1492
|
+
# Store in scope and global scope for access
|
|
1183
1493
|
self.scope.set(var_name, instance)
|
|
1494
|
+
self.global_scope.set(f'${instance_name}', instance)
|
|
1495
|
+
|
|
1184
1496
|
return instance
|
|
1185
1497
|
|
|
1186
1498
|
def _exec_super_func(self, node: ASTNode) -> Any:
|
|
@@ -1352,23 +1664,28 @@ class CSSLRuntime:
|
|
|
1352
1664
|
if not self._running:
|
|
1353
1665
|
break
|
|
1354
1666
|
self._execute_node(child)
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1667
|
+
except CSSLReturn:
|
|
1668
|
+
# Parent returned - that's fine, we just want the local vars
|
|
1669
|
+
pass
|
|
1358
1670
|
except:
|
|
1359
1671
|
pass
|
|
1360
1672
|
finally:
|
|
1673
|
+
# ALWAYS copy local vars (even if parent returned)
|
|
1674
|
+
for name, value in temp_scope.variables.items():
|
|
1675
|
+
new_scope.set(name, value)
|
|
1361
1676
|
self.scope = old_scope
|
|
1362
1677
|
|
|
1363
1678
|
# Bind parameters - handle both positional and named arguments
|
|
1364
1679
|
for i, param in enumerate(params):
|
|
1365
|
-
# Extract param name and
|
|
1680
|
+
# Extract param name, type, and default from dict format: {'name': 'a', 'type': 'int', 'default': ...}
|
|
1366
1681
|
if isinstance(param, dict):
|
|
1367
1682
|
param_name = param['name']
|
|
1368
1683
|
param_type = param.get('type', '')
|
|
1684
|
+
param_default = param.get('default') # v4.2.0: Default value AST node
|
|
1369
1685
|
else:
|
|
1370
1686
|
param_name = param
|
|
1371
1687
|
param_type = ''
|
|
1688
|
+
param_default = None
|
|
1372
1689
|
|
|
1373
1690
|
# Check if this is an 'open' parameter - receives all args as a list
|
|
1374
1691
|
# The parser sets param['open'] = True for 'open' keyword
|
|
@@ -1391,6 +1708,10 @@ class CSSLRuntime:
|
|
|
1391
1708
|
elif i < len(args):
|
|
1392
1709
|
# Positional argument
|
|
1393
1710
|
new_scope.set(param_name, args[i])
|
|
1711
|
+
elif param_default is not None:
|
|
1712
|
+
# v4.2.0: Use default value if no argument provided
|
|
1713
|
+
default_value = self._evaluate(param_default)
|
|
1714
|
+
new_scope.set(param_name, default_value)
|
|
1394
1715
|
else:
|
|
1395
1716
|
new_scope.set(param_name, None)
|
|
1396
1717
|
|
|
@@ -1410,17 +1731,30 @@ class CSSLRuntime:
|
|
|
1410
1731
|
args, kwargs, {}, is_constructor=False
|
|
1411
1732
|
)
|
|
1412
1733
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1734
|
+
# v4.2.0: Handle raw_body with supports_language (multi-language support)
|
|
1735
|
+
raw_body = func_info.get('raw_body')
|
|
1736
|
+
supports_language = func_info.get('supports_language')
|
|
1737
|
+
|
|
1738
|
+
if raw_body and supports_language:
|
|
1739
|
+
# Transform and parse the raw body from the target language
|
|
1740
|
+
body_children = self._transform_and_parse_function_body(raw_body, supports_language)
|
|
1741
|
+
for child in body_children:
|
|
1742
|
+
if not self._running:
|
|
1743
|
+
break
|
|
1744
|
+
self._execute_node(child)
|
|
1745
|
+
else:
|
|
1746
|
+
# Normal CSSL function body
|
|
1747
|
+
for child in func_node.children:
|
|
1748
|
+
# Check if exit() was called
|
|
1749
|
+
if not self._running:
|
|
1750
|
+
break
|
|
1751
|
+
self._execute_node(child)
|
|
1418
1752
|
except CSSLReturn as ret:
|
|
1419
1753
|
return_value = ret.value
|
|
1420
1754
|
|
|
1421
1755
|
# Check exclude_type: *[type] - must NOT return excluded type
|
|
1422
1756
|
exclude_type = func_info.get('exclude_type')
|
|
1423
|
-
if exclude_type:
|
|
1757
|
+
if exclude_type and isinstance(exclude_type, str):
|
|
1424
1758
|
type_map = {
|
|
1425
1759
|
'string': str, 'int': int, 'float': float, 'bool': bool,
|
|
1426
1760
|
'null': type(None), 'none': type(None),
|
|
@@ -1797,6 +2131,71 @@ class CSSLRuntime:
|
|
|
1797
2131
|
|
|
1798
2132
|
return None
|
|
1799
2133
|
|
|
2134
|
+
def _exec_supports_block(self, node: ASTNode) -> Any:
|
|
2135
|
+
"""Execute standalone supports block for multi-language syntax.
|
|
2136
|
+
|
|
2137
|
+
v4.2.0: Allows 'supports' to be used anywhere, not just in class/function.
|
|
2138
|
+
|
|
2139
|
+
Syntax:
|
|
2140
|
+
supports py {
|
|
2141
|
+
for i in range(10):
|
|
2142
|
+
print(i)
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
supports cpp {
|
|
2146
|
+
int x = 42;
|
|
2147
|
+
std::cout << x << std::endl;
|
|
2148
|
+
}
|
|
2149
|
+
"""
|
|
2150
|
+
block_info = node.value
|
|
2151
|
+
language = block_info.get('language')
|
|
2152
|
+
raw_source = block_info.get('raw_source')
|
|
2153
|
+
|
|
2154
|
+
# If we have raw_source, transform and execute
|
|
2155
|
+
if raw_source and language:
|
|
2156
|
+
import textwrap
|
|
2157
|
+
from .cssl_languages import get_language
|
|
2158
|
+
from .cssl_parser import parse_cssl_program
|
|
2159
|
+
|
|
2160
|
+
# Normalize language ID
|
|
2161
|
+
lang_id = language.lstrip('@').lower()
|
|
2162
|
+
|
|
2163
|
+
# Get language support and transformer
|
|
2164
|
+
lang_support = get_language(lang_id)
|
|
2165
|
+
if lang_support is None:
|
|
2166
|
+
raise CSSLRuntimeError(f"Unknown language '{lang_id}' in 'supports' block")
|
|
2167
|
+
|
|
2168
|
+
# Dedent the raw source to normalize indentation
|
|
2169
|
+
dedented_source = textwrap.dedent(raw_source)
|
|
2170
|
+
|
|
2171
|
+
# Transform the raw source to CSSL
|
|
2172
|
+
transformer = lang_support.get_transformer()
|
|
2173
|
+
transformed_source = transformer.transform_source(dedented_source)
|
|
2174
|
+
|
|
2175
|
+
# Parse the transformed CSSL
|
|
2176
|
+
try:
|
|
2177
|
+
ast = parse_cssl_program(transformed_source)
|
|
2178
|
+
except Exception as e:
|
|
2179
|
+
raise CSSLRuntimeError(
|
|
2180
|
+
f"Failed to parse transformed '{lang_id}' code in supports block: {e}\n"
|
|
2181
|
+
f"Dedented:\n{dedented_source}\n"
|
|
2182
|
+
f"Transformed:\n{transformed_source}"
|
|
2183
|
+
)
|
|
2184
|
+
|
|
2185
|
+
# Execute the transformed AST
|
|
2186
|
+
result = None
|
|
2187
|
+
for child in ast.children:
|
|
2188
|
+
result = self._execute_node(child)
|
|
2189
|
+
|
|
2190
|
+
return result
|
|
2191
|
+
|
|
2192
|
+
# Fallback: execute already-parsed children (CSSL syntax)
|
|
2193
|
+
result = None
|
|
2194
|
+
for child in node.children:
|
|
2195
|
+
result = self._execute_node(child)
|
|
2196
|
+
|
|
2197
|
+
return result
|
|
2198
|
+
|
|
1800
2199
|
def _exec_createcmd_inject(self, node: ASTNode) -> Any:
|
|
1801
2200
|
"""Execute createcmd injection: createcmd('cmd') <== { action }"""
|
|
1802
2201
|
command_call = node.value.get('command_call')
|
|
@@ -2365,24 +2764,60 @@ class CSSLRuntime:
|
|
|
2365
2764
|
if filter_info:
|
|
2366
2765
|
source = self._apply_injection_filter(source, filter_info)
|
|
2367
2766
|
|
|
2368
|
-
# Get current target value for add/move modes
|
|
2767
|
+
# Get current target value for add/move/replace modes (needed for UniversalInstance handling)
|
|
2369
2768
|
current_value = None
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
current_value = None
|
|
2769
|
+
try:
|
|
2770
|
+
current_value = self._evaluate(target)
|
|
2771
|
+
except Exception:
|
|
2772
|
+
# Target might not exist yet, that's okay for add mode
|
|
2773
|
+
current_value = None
|
|
2376
2774
|
|
|
2377
2775
|
# Determine final value based on mode
|
|
2378
2776
|
if mode == 'replace':
|
|
2379
|
-
|
|
2777
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2778
|
+
# Special handling for UniversalInstance targets - inject instead of replace
|
|
2779
|
+
if isinstance(current_value, UniversalInstance):
|
|
2780
|
+
if isinstance(source, CSSLClass):
|
|
2781
|
+
current_value.set_member(source.name, source)
|
|
2782
|
+
final_value = current_value
|
|
2783
|
+
elif isinstance(source, ASTNode) and source.type == 'function':
|
|
2784
|
+
func_info = source.value
|
|
2785
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2786
|
+
if func_name:
|
|
2787
|
+
current_value.set_method(func_name, source, self)
|
|
2788
|
+
final_value = current_value
|
|
2789
|
+
elif isinstance(source, CSSLInstance):
|
|
2790
|
+
current_value.set_member(source._class.name, source)
|
|
2791
|
+
final_value = current_value
|
|
2792
|
+
else:
|
|
2793
|
+
# For other types, store as member with source type name
|
|
2794
|
+
final_value = source
|
|
2795
|
+
else:
|
|
2796
|
+
final_value = source
|
|
2380
2797
|
elif mode == 'add':
|
|
2381
2798
|
# Copy & add - preserve target and add source
|
|
2382
|
-
from .cssl_types import CSSLInstance
|
|
2799
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2383
2800
|
|
|
2801
|
+
# Special handling for UniversalInstance + CSSLClass
|
|
2802
|
+
if isinstance(current_value, UniversalInstance) and isinstance(source, CSSLClass):
|
|
2803
|
+
# Inject class definition into universal instance
|
|
2804
|
+
current_value.set_member(source.name, source)
|
|
2805
|
+
final_value = current_value
|
|
2806
|
+
# Special handling for UniversalInstance + Function (AST node)
|
|
2807
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, ASTNode) and source.type == 'function':
|
|
2808
|
+
# Inject function as a method into universal instance
|
|
2809
|
+
func_info = source.value
|
|
2810
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2811
|
+
if func_name:
|
|
2812
|
+
current_value.set_method(func_name, source, self)
|
|
2813
|
+
final_value = current_value
|
|
2814
|
+
# Special handling for UniversalInstance + CSSLInstance
|
|
2815
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, CSSLInstance):
|
|
2816
|
+
class_name = source._class.name
|
|
2817
|
+
current_value.set_member(class_name, source)
|
|
2818
|
+
final_value = current_value
|
|
2384
2819
|
# Special handling for CSSLInstance - merge classes
|
|
2385
|
-
|
|
2820
|
+
elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
|
|
2386
2821
|
# Add the new class instance as a member with class name as key
|
|
2387
2822
|
class_name = source._class.name
|
|
2388
2823
|
current_value._members[class_name] = source
|
|
@@ -2581,8 +3016,13 @@ class CSSLRuntime:
|
|
|
2581
3016
|
- add: func +<<== { code } - ADDS code to function (both execute)
|
|
2582
3017
|
- remove: func -<<== { code } - REMOVES matching code from function
|
|
2583
3018
|
|
|
3019
|
+
Also supports instance injection:
|
|
3020
|
+
- instance +<<== { void method() { ... } } - ADDS methods to UniversalInstance
|
|
3021
|
+
|
|
2584
3022
|
Also supports expression form: func <<== %exit() (wraps in action_block)
|
|
2585
3023
|
"""
|
|
3024
|
+
from .cssl_types import UniversalInstance
|
|
3025
|
+
|
|
2586
3026
|
target = node.value.get('target')
|
|
2587
3027
|
code_block = node.value.get('code')
|
|
2588
3028
|
source_expr = node.value.get('source') # For expression form: func <<== expr
|
|
@@ -2594,6 +3034,17 @@ class CSSLRuntime:
|
|
|
2594
3034
|
expr_node = ASTNode('expression', value=source_expr)
|
|
2595
3035
|
code_block = ASTNode('action_block', children=[expr_node])
|
|
2596
3036
|
|
|
3037
|
+
# Check if target is a UniversalInstance
|
|
3038
|
+
target_value = None
|
|
3039
|
+
if isinstance(target, ASTNode) and target.type == 'identifier':
|
|
3040
|
+
target_value = self.scope.get(target.value)
|
|
3041
|
+
if target_value is None:
|
|
3042
|
+
target_value = self.global_scope.get(target.value)
|
|
3043
|
+
|
|
3044
|
+
# Handle UniversalInstance injection
|
|
3045
|
+
if isinstance(target_value, UniversalInstance):
|
|
3046
|
+
return self._inject_into_instance(target_value, code_block, mode)
|
|
3047
|
+
|
|
2597
3048
|
# Get function name from target
|
|
2598
3049
|
func_name = None
|
|
2599
3050
|
if isinstance(target, ASTNode):
|
|
@@ -2641,6 +3092,60 @@ class CSSLRuntime:
|
|
|
2641
3092
|
|
|
2642
3093
|
return None
|
|
2643
3094
|
|
|
3095
|
+
def _inject_into_instance(self, instance: Any, code_block: Any, mode: str) -> Any:
|
|
3096
|
+
"""Inject code/methods into a UniversalInstance.
|
|
3097
|
+
|
|
3098
|
+
Usage:
|
|
3099
|
+
instance<"myContainer"> container;
|
|
3100
|
+
container +<<== {
|
|
3101
|
+
void sayHello() { printl("Hello!"); }
|
|
3102
|
+
int value = 42;
|
|
3103
|
+
}
|
|
3104
|
+
"""
|
|
3105
|
+
from .cssl_types import UniversalInstance
|
|
3106
|
+
|
|
3107
|
+
if not isinstance(instance, UniversalInstance):
|
|
3108
|
+
return None
|
|
3109
|
+
|
|
3110
|
+
if code_block is None:
|
|
3111
|
+
return None
|
|
3112
|
+
|
|
3113
|
+
# Store the raw injection
|
|
3114
|
+
instance.add_injection(code_block)
|
|
3115
|
+
|
|
3116
|
+
# Parse the code block for function definitions and variable declarations
|
|
3117
|
+
if isinstance(code_block, ASTNode):
|
|
3118
|
+
children = code_block.children if hasattr(code_block, 'children') else []
|
|
3119
|
+
|
|
3120
|
+
for child in children:
|
|
3121
|
+
if isinstance(child, ASTNode):
|
|
3122
|
+
if child.type == 'function':
|
|
3123
|
+
# Extract function name and store the AST node
|
|
3124
|
+
func_info = child.value
|
|
3125
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
3126
|
+
if func_name:
|
|
3127
|
+
instance.set_method(func_name, child, self)
|
|
3128
|
+
elif child.type == 'var_declaration':
|
|
3129
|
+
# Extract variable and value
|
|
3130
|
+
var_info = child.value
|
|
3131
|
+
if isinstance(var_info, dict):
|
|
3132
|
+
var_name = var_info.get('name')
|
|
3133
|
+
value_node = var_info.get('value')
|
|
3134
|
+
if var_name:
|
|
3135
|
+
value = self._evaluate(value_node) if value_node else None
|
|
3136
|
+
instance.set_member(var_name, value)
|
|
3137
|
+
elif child.type == 'typed_var_declaration':
|
|
3138
|
+
# Typed variable declaration
|
|
3139
|
+
var_info = child.value
|
|
3140
|
+
if isinstance(var_info, dict):
|
|
3141
|
+
var_name = var_info.get('name')
|
|
3142
|
+
value_node = var_info.get('value')
|
|
3143
|
+
if var_name:
|
|
3144
|
+
value = self._evaluate(value_node) if value_node else None
|
|
3145
|
+
instance.set_member(var_name, value)
|
|
3146
|
+
|
|
3147
|
+
return instance
|
|
3148
|
+
|
|
2644
3149
|
def _exec_infuse_right(self, node: ASTNode) -> Any:
|
|
2645
3150
|
"""Execute right-side code infusion (==>>)"""
|
|
2646
3151
|
source = node.value.get('source')
|
|
@@ -2727,7 +3232,13 @@ class CSSLRuntime:
|
|
|
2727
3232
|
if self._current_instance is None:
|
|
2728
3233
|
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
2729
3234
|
member = target.value.get('member')
|
|
2730
|
-
self._current_instance
|
|
3235
|
+
instance = self._current_instance
|
|
3236
|
+
# Check if instance is a CSSL instance or a plain Python object
|
|
3237
|
+
if hasattr(instance, 'set_member'):
|
|
3238
|
+
instance.set_member(member, value)
|
|
3239
|
+
else:
|
|
3240
|
+
# Plain Python object - use setattr
|
|
3241
|
+
setattr(instance, member, value)
|
|
2731
3242
|
elif isinstance(target, str):
|
|
2732
3243
|
self.scope.set(target, value)
|
|
2733
3244
|
|
|
@@ -2960,6 +3471,83 @@ class CSSLRuntime:
|
|
|
2960
3471
|
# Return None if instance doesn't exist (can be created via ==>)
|
|
2961
3472
|
return None
|
|
2962
3473
|
|
|
3474
|
+
# v4.1.0/v4.1.1: Cross-language instance reference: cpp$ClassName, py$Object
|
|
3475
|
+
# Enhanced bidirectional access with real runtime bridges
|
|
3476
|
+
if node.type == 'lang_instance_ref':
|
|
3477
|
+
ref = node.value # {'lang': 'cpp', 'instance': 'ClassName'}
|
|
3478
|
+
lang_id = ref['lang']
|
|
3479
|
+
instance_name = ref['instance']
|
|
3480
|
+
|
|
3481
|
+
# First, try to get the language support object from scope
|
|
3482
|
+
lang_support = self.scope.get(lang_id)
|
|
3483
|
+
if lang_support is None:
|
|
3484
|
+
lang_support = self.global_scope.get(lang_id)
|
|
3485
|
+
|
|
3486
|
+
# If not found in scope, try to get from modules
|
|
3487
|
+
if lang_support is None:
|
|
3488
|
+
lang_support = self._modules.get(lang_id)
|
|
3489
|
+
|
|
3490
|
+
# If still not found, try getting default language support
|
|
3491
|
+
if lang_support is None:
|
|
3492
|
+
from .cssl_languages import get_language
|
|
3493
|
+
lang_support = get_language(lang_id)
|
|
3494
|
+
|
|
3495
|
+
if lang_support is not None:
|
|
3496
|
+
# v4.1.1: Check if it's a LanguageSupport object with get_instance method
|
|
3497
|
+
if hasattr(lang_support, 'get_instance'):
|
|
3498
|
+
instance = lang_support.get_instance(instance_name)
|
|
3499
|
+
if instance is not None:
|
|
3500
|
+
return instance
|
|
3501
|
+
|
|
3502
|
+
# v4.1.1: For C++, also try to get class from loaded modules directly
|
|
3503
|
+
if hasattr(lang_support, '_get_bridge'):
|
|
3504
|
+
bridge = lang_support._get_bridge()
|
|
3505
|
+
if bridge is not None:
|
|
3506
|
+
# Try to get from bridge's instances
|
|
3507
|
+
bridge_instance = bridge.get_instance(instance_name)
|
|
3508
|
+
if bridge_instance is not None:
|
|
3509
|
+
return bridge_instance
|
|
3510
|
+
|
|
3511
|
+
# For C++: Try to access class from IncludeCPP modules
|
|
3512
|
+
if hasattr(bridge, '_modules'):
|
|
3513
|
+
for mod in bridge._modules.values():
|
|
3514
|
+
if hasattr(mod, instance_name):
|
|
3515
|
+
cls_or_instance = getattr(mod, instance_name)
|
|
3516
|
+
# Cache it for future access
|
|
3517
|
+
lang_support._instances[instance_name] = cls_or_instance
|
|
3518
|
+
return cls_or_instance
|
|
3519
|
+
|
|
3520
|
+
# Check _instances dict directly as fallback
|
|
3521
|
+
if hasattr(lang_support, '_instances'):
|
|
3522
|
+
if instance_name in lang_support._instances:
|
|
3523
|
+
return lang_support._instances[instance_name]
|
|
3524
|
+
|
|
3525
|
+
# Build helpful error message based on language
|
|
3526
|
+
if lang_id in ('cpp', 'c++'):
|
|
3527
|
+
hint = (f"For C++ access:\n"
|
|
3528
|
+
f" 1. Build your module with 'includecpp build'\n"
|
|
3529
|
+
f" 2. Use cpp.share(\"{instance_name}\", instance) to register\n"
|
|
3530
|
+
f" 3. Or access a class directly: obj = new cpp${instance_name}()")
|
|
3531
|
+
elif lang_id in ('java',):
|
|
3532
|
+
hint = (f"For Java access:\n"
|
|
3533
|
+
f" 1. Install JPype: pip install jpype1\n"
|
|
3534
|
+
f" 2. Add classpath: java.add_classpath(\"path/to/jar\")\n"
|
|
3535
|
+
f" 3. Load class: MyClass = java.load_class(\"com.example.{instance_name}\")\n"
|
|
3536
|
+
f" 4. Share instance: java.share(\"{instance_name}\", instance)")
|
|
3537
|
+
elif lang_id in ('js', 'javascript'):
|
|
3538
|
+
hint = (f"For JavaScript access:\n"
|
|
3539
|
+
f" 1. Make sure Node.js is installed\n"
|
|
3540
|
+
f" 2. Define in JS: js.eval(\"function {instance_name}() {{...}}\")\n"
|
|
3541
|
+
f" 3. Share result: js.share(\"{instance_name}\", result)")
|
|
3542
|
+
else:
|
|
3543
|
+
hint = f"Use '{lang_id}.share(\"{instance_name}\", instance)' to register the instance first."
|
|
3544
|
+
|
|
3545
|
+
raise self._format_error(
|
|
3546
|
+
node.line if hasattr(node, 'line') else 0,
|
|
3547
|
+
f"Cross-language instance '{lang_id}${instance_name}' not found",
|
|
3548
|
+
hint
|
|
3549
|
+
)
|
|
3550
|
+
|
|
2963
3551
|
if node.type == 'new':
|
|
2964
3552
|
# Create new instance of a class: new ClassName(args)
|
|
2965
3553
|
return self._eval_new(node)
|
|
@@ -3033,6 +3621,14 @@ class CSSLRuntime:
|
|
|
3033
3621
|
if node.type == 'unary':
|
|
3034
3622
|
return self._eval_unary(node)
|
|
3035
3623
|
|
|
3624
|
+
# Increment: ++i or i++
|
|
3625
|
+
if node.type == 'increment':
|
|
3626
|
+
return self._eval_increment(node)
|
|
3627
|
+
|
|
3628
|
+
# Decrement: --i or i--
|
|
3629
|
+
if node.type == 'decrement':
|
|
3630
|
+
return self._eval_decrement(node)
|
|
3631
|
+
|
|
3036
3632
|
if node.type == 'non_null_assert':
|
|
3037
3633
|
# *$var, *@module, *identifier - assert value is not null/None
|
|
3038
3634
|
operand = node.value.get('operand')
|
|
@@ -3076,7 +3672,7 @@ class CSSLRuntime:
|
|
|
3076
3672
|
'json': dict,
|
|
3077
3673
|
}
|
|
3078
3674
|
|
|
3079
|
-
excluded_py_type = type_map.get(exclude_type.lower())
|
|
3675
|
+
excluded_py_type = type_map.get(exclude_type.lower() if isinstance(exclude_type, str) else exclude_type)
|
|
3080
3676
|
if excluded_py_type and isinstance(value, excluded_py_type):
|
|
3081
3677
|
raise self._format_error(
|
|
3082
3678
|
node.line if hasattr(node, 'line') else 0,
|
|
@@ -3318,6 +3914,74 @@ class CSSLRuntime:
|
|
|
3318
3914
|
|
|
3319
3915
|
return None
|
|
3320
3916
|
|
|
3917
|
+
def _eval_increment(self, node: ASTNode) -> Any:
|
|
3918
|
+
"""Evaluate increment operation (++i or i++)"""
|
|
3919
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
3920
|
+
operand = node.value.get('operand')
|
|
3921
|
+
|
|
3922
|
+
# Get variable name
|
|
3923
|
+
var_name = None
|
|
3924
|
+
if isinstance(operand, ASTNode):
|
|
3925
|
+
if operand.type == 'identifier':
|
|
3926
|
+
var_name = operand.value
|
|
3927
|
+
elif operand.type == 'shared_ref':
|
|
3928
|
+
# Handle $var++
|
|
3929
|
+
var_name = operand.value
|
|
3930
|
+
current = self.shared_vars.get(var_name, 0)
|
|
3931
|
+
if op_type == 'prefix':
|
|
3932
|
+
self.shared_vars[var_name] = current + 1
|
|
3933
|
+
return current + 1
|
|
3934
|
+
else: # postfix
|
|
3935
|
+
self.shared_vars[var_name] = current + 1
|
|
3936
|
+
return current
|
|
3937
|
+
|
|
3938
|
+
if var_name:
|
|
3939
|
+
current = self.scope.get(var_name, 0)
|
|
3940
|
+
if op_type == 'prefix':
|
|
3941
|
+
# ++i: increment then return
|
|
3942
|
+
self.scope.set(var_name, current + 1)
|
|
3943
|
+
return current + 1
|
|
3944
|
+
else:
|
|
3945
|
+
# i++: return then increment
|
|
3946
|
+
self.scope.set(var_name, current + 1)
|
|
3947
|
+
return current
|
|
3948
|
+
|
|
3949
|
+
return None
|
|
3950
|
+
|
|
3951
|
+
def _eval_decrement(self, node: ASTNode) -> Any:
|
|
3952
|
+
"""Evaluate decrement operation (--i or i--)"""
|
|
3953
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
3954
|
+
operand = node.value.get('operand')
|
|
3955
|
+
|
|
3956
|
+
# Get variable name
|
|
3957
|
+
var_name = None
|
|
3958
|
+
if isinstance(operand, ASTNode):
|
|
3959
|
+
if operand.type == 'identifier':
|
|
3960
|
+
var_name = operand.value
|
|
3961
|
+
elif operand.type == 'shared_ref':
|
|
3962
|
+
# Handle $var--
|
|
3963
|
+
var_name = operand.value
|
|
3964
|
+
current = self.shared_vars.get(var_name, 0)
|
|
3965
|
+
if op_type == 'prefix':
|
|
3966
|
+
self.shared_vars[var_name] = current - 1
|
|
3967
|
+
return current - 1
|
|
3968
|
+
else: # postfix
|
|
3969
|
+
self.shared_vars[var_name] = current - 1
|
|
3970
|
+
return current
|
|
3971
|
+
|
|
3972
|
+
if var_name:
|
|
3973
|
+
current = self.scope.get(var_name, 0)
|
|
3974
|
+
if op_type == 'prefix':
|
|
3975
|
+
# --i: decrement then return
|
|
3976
|
+
self.scope.set(var_name, current - 1)
|
|
3977
|
+
return current - 1
|
|
3978
|
+
else:
|
|
3979
|
+
# i--: return then decrement
|
|
3980
|
+
self.scope.set(var_name, current - 1)
|
|
3981
|
+
return current
|
|
3982
|
+
|
|
3983
|
+
return None
|
|
3984
|
+
|
|
3321
3985
|
def _eval_call(self, node: ASTNode) -> Any:
|
|
3322
3986
|
"""Evaluate function call with optional named arguments"""
|
|
3323
3987
|
callee_node = node.value.get('callee')
|
|
@@ -3361,7 +4025,33 @@ class CSSLRuntime:
|
|
|
3361
4025
|
if isinstance(callee, ASTNode) and callee.type == 'function':
|
|
3362
4026
|
return self._call_function(callee, args, kwargs)
|
|
3363
4027
|
|
|
3364
|
-
|
|
4028
|
+
# Extract callee name for error messages
|
|
4029
|
+
if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value'):
|
|
4030
|
+
val = callee_node.value
|
|
4031
|
+
if isinstance(val, str):
|
|
4032
|
+
callee_name = val
|
|
4033
|
+
elif isinstance(val, dict):
|
|
4034
|
+
# For member access nodes like obj.method, get the member name
|
|
4035
|
+
if 'member' in val:
|
|
4036
|
+
obj_node = val.get('object')
|
|
4037
|
+
member = val.get('member')
|
|
4038
|
+
obj_name = obj_node.value if isinstance(obj_node, ASTNode) else str(obj_node)
|
|
4039
|
+
callee_name = f"{obj_name}.{member}"
|
|
4040
|
+
# For call nodes, try to get the callee name
|
|
4041
|
+
elif 'callee' in val:
|
|
4042
|
+
callee_val = val.get('callee')
|
|
4043
|
+
if isinstance(callee_val, ASTNode):
|
|
4044
|
+
callee_name = callee_val.value if isinstance(callee_val.value, str) else str(callee_val.value)
|
|
4045
|
+
else:
|
|
4046
|
+
callee_name = str(callee_val)
|
|
4047
|
+
elif 'name' in val:
|
|
4048
|
+
callee_name = str(val.get('name'))
|
|
4049
|
+
else:
|
|
4050
|
+
callee_name = str(val)
|
|
4051
|
+
else:
|
|
4052
|
+
callee_name = str(val)
|
|
4053
|
+
else:
|
|
4054
|
+
callee_name = str(callee_node)
|
|
3365
4055
|
|
|
3366
4056
|
# Build detailed error with suggestions
|
|
3367
4057
|
available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
|
|
@@ -3503,6 +4193,22 @@ class CSSLRuntime:
|
|
|
3503
4193
|
hint
|
|
3504
4194
|
)
|
|
3505
4195
|
|
|
4196
|
+
# If we got a variable that holds a class reference, use that class
|
|
4197
|
+
if not isinstance(class_def, CSSLClass):
|
|
4198
|
+
# Check if it's a variable holding a class (dynamic class instantiation)
|
|
4199
|
+
if hasattr(class_def, '__class__') and isinstance(class_def, CSSLClass):
|
|
4200
|
+
pass # Already a CSSLClass, continue
|
|
4201
|
+
elif isinstance(class_def, dict) and 'class_def' in class_def:
|
|
4202
|
+
# Injected class reference from +<== operator
|
|
4203
|
+
class_def = class_def['class_def']
|
|
4204
|
+
else:
|
|
4205
|
+
# Not a class - show error
|
|
4206
|
+
raise CSSLRuntimeError(
|
|
4207
|
+
f"'{class_name}' is not a class",
|
|
4208
|
+
node.line,
|
|
4209
|
+
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
4210
|
+
)
|
|
4211
|
+
|
|
3506
4212
|
if not isinstance(class_def, CSSLClass):
|
|
3507
4213
|
raise CSSLRuntimeError(
|
|
3508
4214
|
f"'{class_name}' is not a class",
|
|
@@ -3628,12 +4334,34 @@ class CSSLRuntime:
|
|
|
3628
4334
|
|
|
3629
4335
|
# Resolve the class/instance reference
|
|
3630
4336
|
if ref_class.startswith('$'):
|
|
3631
|
-
# Dynamic instance reference: &$instanceVar::member
|
|
4337
|
+
# Dynamic instance reference: &$instanceVar::member or &$PyObject.method
|
|
3632
4338
|
var_name = ref_class[1:]
|
|
3633
|
-
|
|
4339
|
+
|
|
4340
|
+
# First check in _live_objects for Python shared objects
|
|
4341
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
4342
|
+
ref_obj = _live_objects.get(var_name)
|
|
4343
|
+
if ref_obj is None:
|
|
4344
|
+
ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
|
|
4345
|
+
|
|
3634
4346
|
if ref_obj is None:
|
|
3635
4347
|
return # Instance not found, skip silently
|
|
3636
4348
|
|
|
4349
|
+
# Handle Python shared objects
|
|
4350
|
+
if isinstance(ref_obj, SharedObjectProxy):
|
|
4351
|
+
ref_obj = ref_obj._obj
|
|
4352
|
+
|
|
4353
|
+
# If it's a Python object (not CSSL), call the method directly
|
|
4354
|
+
if not isinstance(ref_obj, (CSSLInstance, CSSLClass)):
|
|
4355
|
+
if ref_member and hasattr(ref_obj, ref_member):
|
|
4356
|
+
method = getattr(ref_obj, ref_member)
|
|
4357
|
+
if callable(method):
|
|
4358
|
+
try:
|
|
4359
|
+
method(*args, **kwargs)
|
|
4360
|
+
except TypeError:
|
|
4361
|
+
# Try without args
|
|
4362
|
+
method()
|
|
4363
|
+
return
|
|
4364
|
+
|
|
3637
4365
|
if isinstance(ref_obj, CSSLInstance):
|
|
3638
4366
|
# Get the class definition from the instance
|
|
3639
4367
|
target_class = ref_obj.class_def
|
|
@@ -3778,24 +4506,40 @@ class CSSLRuntime:
|
|
|
3778
4506
|
# Direct this->member access
|
|
3779
4507
|
instance = self._current_instance
|
|
3780
4508
|
|
|
3781
|
-
# Check if
|
|
3782
|
-
|
|
3783
|
-
return instance.get_member(member)
|
|
4509
|
+
# Check if instance is a CSSL instance or a plain Python object
|
|
4510
|
+
is_cssl_instance = hasattr(instance, 'has_member') and hasattr(instance, 'has_method')
|
|
3784
4511
|
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
4512
|
+
if is_cssl_instance:
|
|
4513
|
+
# CSSL instance - use CSSL methods
|
|
4514
|
+
if instance.has_member(member):
|
|
4515
|
+
return instance.get_member(member)
|
|
4516
|
+
|
|
4517
|
+
# Check if it's a method
|
|
4518
|
+
if instance.has_method(member):
|
|
4519
|
+
# Return a callable that will invoke the method with instance context
|
|
4520
|
+
method_node = instance.get_method(member)
|
|
4521
|
+
# Check if this is an inherited Python method
|
|
4522
|
+
if isinstance(method_node, tuple) and method_node[0] == 'python_method':
|
|
4523
|
+
python_method = method_node[1]
|
|
4524
|
+
return lambda *args, **kwargs: python_method(*args, **kwargs)
|
|
4525
|
+
return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
|
|
4526
|
+
else:
|
|
4527
|
+
# Plain Python object - use standard attribute access
|
|
4528
|
+
if hasattr(instance, member):
|
|
4529
|
+
return getattr(instance, member)
|
|
4530
|
+
# Also check __dict__ for dynamic attributes
|
|
4531
|
+
if hasattr(instance, '__dict__') and member in instance.__dict__:
|
|
4532
|
+
return instance.__dict__[member]
|
|
3794
4533
|
|
|
3795
4534
|
# Build helpful error with available members
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
4535
|
+
if is_cssl_instance:
|
|
4536
|
+
class_name = instance._class.name
|
|
4537
|
+
available_members = list(instance._members.keys()) if hasattr(instance, '_members') else []
|
|
4538
|
+
available_methods = list(instance._methods.keys()) if hasattr(instance, '_methods') else []
|
|
4539
|
+
else:
|
|
4540
|
+
class_name = type(instance).__name__
|
|
4541
|
+
available_members = [k for k in dir(instance) if not k.startswith('_')]
|
|
4542
|
+
available_methods = []
|
|
3799
4543
|
all_available = available_members + available_methods
|
|
3800
4544
|
similar = _find_similar_names(member, all_available)
|
|
3801
4545
|
|
|
@@ -3853,9 +4597,19 @@ class CSSLRuntime:
|
|
|
3853
4597
|
self.scope = new_scope
|
|
3854
4598
|
self._current_instance = instance
|
|
3855
4599
|
|
|
4600
|
+
original_return = None
|
|
3856
4601
|
try:
|
|
4602
|
+
# Handle append mode via _append_to_target (stored original)
|
|
4603
|
+
original_method = func_info.get('_original_method')
|
|
4604
|
+
if original_method:
|
|
4605
|
+
# Execute original method first - capture return for fallback
|
|
4606
|
+
try:
|
|
4607
|
+
original_return = self._call_method(instance, original_method, args, kwargs)
|
|
4608
|
+
except CSSLReturn as ret:
|
|
4609
|
+
original_return = ret.value
|
|
4610
|
+
|
|
3857
4611
|
# Handle append mode (++) - execute referenced parent method first
|
|
3858
|
-
|
|
4612
|
+
elif append_mode and append_ref_class:
|
|
3859
4613
|
self._execute_append_reference(
|
|
3860
4614
|
instance, append_ref_class, append_ref_member,
|
|
3861
4615
|
args, kwargs, {}, is_constructor=False
|
|
@@ -3876,7 +4630,8 @@ class CSSLRuntime:
|
|
|
3876
4630
|
self.scope = old_scope
|
|
3877
4631
|
self._current_instance = old_instance
|
|
3878
4632
|
|
|
3879
|
-
return
|
|
4633
|
+
# If no return in appended code, use original's return
|
|
4634
|
+
return original_return
|
|
3880
4635
|
|
|
3881
4636
|
def _eval_member_access(self, node: ASTNode) -> Any:
|
|
3882
4637
|
"""Evaluate member access"""
|
|
@@ -3924,6 +4679,48 @@ class CSSLRuntime:
|
|
|
3924
4679
|
hint
|
|
3925
4680
|
)
|
|
3926
4681
|
|
|
4682
|
+
# === UNIVERSAL INSTANCE METHODS ===
|
|
4683
|
+
from .cssl_types import UniversalInstance
|
|
4684
|
+
if isinstance(obj, UniversalInstance):
|
|
4685
|
+
# Check for member variable
|
|
4686
|
+
if obj.has_member(member):
|
|
4687
|
+
return obj.get_member(member)
|
|
4688
|
+
# Check for method
|
|
4689
|
+
if obj.has_method(member):
|
|
4690
|
+
method_node = obj.get_method(member)
|
|
4691
|
+
# Create a callable that executes the method in context
|
|
4692
|
+
def instance_method_caller(*args, **kwargs):
|
|
4693
|
+
# Set 'this' to refer to the instance
|
|
4694
|
+
old_this = self.scope.get('this')
|
|
4695
|
+
self.scope.set('this', obj)
|
|
4696
|
+
try:
|
|
4697
|
+
return self._call_function(method_node, list(args))
|
|
4698
|
+
finally:
|
|
4699
|
+
if old_this is not None:
|
|
4700
|
+
self.scope.set('this', old_this)
|
|
4701
|
+
else:
|
|
4702
|
+
self.scope.remove('this') if hasattr(self.scope, 'remove') else None
|
|
4703
|
+
return instance_method_caller
|
|
4704
|
+
# Build helpful error with available members
|
|
4705
|
+
instance_name = obj.name
|
|
4706
|
+
available_members = list(obj.get_all_members().keys())
|
|
4707
|
+
available_methods = list(obj.get_all_methods().keys())
|
|
4708
|
+
all_available = available_members + available_methods
|
|
4709
|
+
similar = _find_similar_names(member, all_available)
|
|
4710
|
+
|
|
4711
|
+
if similar:
|
|
4712
|
+
hint = f"Did you mean: {', '.join(similar)}?"
|
|
4713
|
+
elif all_available:
|
|
4714
|
+
hint = f"Available: {', '.join(all_available[:5])}"
|
|
4715
|
+
else:
|
|
4716
|
+
hint = f"Instance '{instance_name}' has no accessible members."
|
|
4717
|
+
|
|
4718
|
+
raise self._format_error(
|
|
4719
|
+
node.line,
|
|
4720
|
+
f"Instance '{instance_name}' has no member or method '{member}'",
|
|
4721
|
+
hint
|
|
4722
|
+
)
|
|
4723
|
+
|
|
3927
4724
|
# === STRING METHODS ===
|
|
3928
4725
|
if isinstance(obj, str):
|
|
3929
4726
|
string_methods = self._get_string_method(obj, member)
|
|
@@ -4162,6 +4959,12 @@ class CSSLRuntime:
|
|
|
4162
4959
|
obj.set_member(member, value)
|
|
4163
4960
|
return
|
|
4164
4961
|
|
|
4962
|
+
# Check for UniversalInstance - use set_member method
|
|
4963
|
+
from .cssl_types import UniversalInstance
|
|
4964
|
+
if isinstance(obj, UniversalInstance):
|
|
4965
|
+
obj.set_member(member, value)
|
|
4966
|
+
return
|
|
4967
|
+
|
|
4165
4968
|
# Check for SharedObjectProxy - directly access underlying object
|
|
4166
4969
|
# This is more robust than relying on the proxy's __setattr__
|
|
4167
4970
|
if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
|