IncludeCPP 4.0.2__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 +698 -84
- 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 +429 -98
- includecpp/core/cssl/cssl_runtime.py +666 -51
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +172 -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 +140 -17
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/METADATA +101 -1
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/RECORD +26 -22
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/WHEEL +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.2.2.dist-info}/top_level.txt +0 -0
|
@@ -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:
|
|
@@ -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
|
|
|
@@ -1152,16 +1297,22 @@ class CSSLRuntime:
|
|
|
1152
1297
|
if ref_member and hasattr(ref_obj, ref_member):
|
|
1153
1298
|
original_method = getattr(ref_obj, ref_member)
|
|
1154
1299
|
runtime = self
|
|
1300
|
+
# Store the original method before wrapping
|
|
1301
|
+
_saved_original = original_method
|
|
1155
1302
|
def appended_wrapper(*args, **kwargs):
|
|
1156
|
-
# Run original first
|
|
1303
|
+
# Run original first (use saved reference to avoid recursion)
|
|
1157
1304
|
result = None
|
|
1158
|
-
if callable(
|
|
1305
|
+
if callable(_saved_original):
|
|
1159
1306
|
try:
|
|
1160
|
-
result =
|
|
1307
|
+
result = _saved_original(*args, **kwargs)
|
|
1161
1308
|
except:
|
|
1162
1309
|
pass
|
|
1163
|
-
# Then run appended code
|
|
1164
|
-
|
|
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
|
|
1165
1316
|
try:
|
|
1166
1317
|
setattr(ref_obj, ref_member, appended_wrapper)
|
|
1167
1318
|
except (AttributeError, TypeError):
|
|
@@ -1310,29 +1461,38 @@ class CSSLRuntime:
|
|
|
1310
1461
|
def _exec_instance_declaration(self, node: ASTNode) -> Any:
|
|
1311
1462
|
"""Execute instance declaration: instance<"name"> varName;
|
|
1312
1463
|
|
|
1313
|
-
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
|
|
1314
1471
|
"""
|
|
1315
|
-
from
|
|
1472
|
+
from .cssl_types import UniversalInstance
|
|
1473
|
+
|
|
1316
1474
|
decl = node.value
|
|
1317
1475
|
instance_name = decl.get('instance_name')
|
|
1318
1476
|
var_name = decl.get('name')
|
|
1319
1477
|
value_node = decl.get('value')
|
|
1320
1478
|
|
|
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}')
|
|
1479
|
+
# Get existing or create new universal instance
|
|
1480
|
+
instance = UniversalInstance.get_or_create(instance_name)
|
|
1327
1481
|
|
|
1328
|
-
# If value is provided,
|
|
1482
|
+
# If value is provided, set it as initial content
|
|
1329
1483
|
if value_node:
|
|
1330
|
-
|
|
1331
|
-
#
|
|
1332
|
-
|
|
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)
|
|
1333
1491
|
|
|
1334
|
-
# Store in scope
|
|
1492
|
+
# Store in scope and global scope for access
|
|
1335
1493
|
self.scope.set(var_name, instance)
|
|
1494
|
+
self.global_scope.set(f'${instance_name}', instance)
|
|
1495
|
+
|
|
1336
1496
|
return instance
|
|
1337
1497
|
|
|
1338
1498
|
def _exec_super_func(self, node: ASTNode) -> Any:
|
|
@@ -1517,13 +1677,15 @@ class CSSLRuntime:
|
|
|
1517
1677
|
|
|
1518
1678
|
# Bind parameters - handle both positional and named arguments
|
|
1519
1679
|
for i, param in enumerate(params):
|
|
1520
|
-
# Extract param name and
|
|
1680
|
+
# Extract param name, type, and default from dict format: {'name': 'a', 'type': 'int', 'default': ...}
|
|
1521
1681
|
if isinstance(param, dict):
|
|
1522
1682
|
param_name = param['name']
|
|
1523
1683
|
param_type = param.get('type', '')
|
|
1684
|
+
param_default = param.get('default') # v4.2.0: Default value AST node
|
|
1524
1685
|
else:
|
|
1525
1686
|
param_name = param
|
|
1526
1687
|
param_type = ''
|
|
1688
|
+
param_default = None
|
|
1527
1689
|
|
|
1528
1690
|
# Check if this is an 'open' parameter - receives all args as a list
|
|
1529
1691
|
# The parser sets param['open'] = True for 'open' keyword
|
|
@@ -1546,6 +1708,10 @@ class CSSLRuntime:
|
|
|
1546
1708
|
elif i < len(args):
|
|
1547
1709
|
# Positional argument
|
|
1548
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)
|
|
1549
1715
|
else:
|
|
1550
1716
|
new_scope.set(param_name, None)
|
|
1551
1717
|
|
|
@@ -1565,11 +1731,24 @@ class CSSLRuntime:
|
|
|
1565
1731
|
args, kwargs, {}, is_constructor=False
|
|
1566
1732
|
)
|
|
1567
1733
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
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)
|
|
1573
1752
|
except CSSLReturn as ret:
|
|
1574
1753
|
return_value = ret.value
|
|
1575
1754
|
|
|
@@ -1952,6 +2131,71 @@ class CSSLRuntime:
|
|
|
1952
2131
|
|
|
1953
2132
|
return None
|
|
1954
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
|
+
|
|
1955
2199
|
def _exec_createcmd_inject(self, node: ASTNode) -> Any:
|
|
1956
2200
|
"""Execute createcmd injection: createcmd('cmd') <== { action }"""
|
|
1957
2201
|
command_call = node.value.get('command_call')
|
|
@@ -2520,24 +2764,60 @@ class CSSLRuntime:
|
|
|
2520
2764
|
if filter_info:
|
|
2521
2765
|
source = self._apply_injection_filter(source, filter_info)
|
|
2522
2766
|
|
|
2523
|
-
# Get current target value for add/move modes
|
|
2767
|
+
# Get current target value for add/move/replace modes (needed for UniversalInstance handling)
|
|
2524
2768
|
current_value = None
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
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
|
|
2531
2774
|
|
|
2532
2775
|
# Determine final value based on mode
|
|
2533
2776
|
if mode == 'replace':
|
|
2534
|
-
|
|
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
|
|
2535
2797
|
elif mode == 'add':
|
|
2536
2798
|
# Copy & add - preserve target and add source
|
|
2537
|
-
from .cssl_types import CSSLInstance
|
|
2799
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2538
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
|
|
2539
2819
|
# Special handling for CSSLInstance - merge classes
|
|
2540
|
-
|
|
2820
|
+
elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
|
|
2541
2821
|
# Add the new class instance as a member with class name as key
|
|
2542
2822
|
class_name = source._class.name
|
|
2543
2823
|
current_value._members[class_name] = source
|
|
@@ -2736,8 +3016,13 @@ class CSSLRuntime:
|
|
|
2736
3016
|
- add: func +<<== { code } - ADDS code to function (both execute)
|
|
2737
3017
|
- remove: func -<<== { code } - REMOVES matching code from function
|
|
2738
3018
|
|
|
3019
|
+
Also supports instance injection:
|
|
3020
|
+
- instance +<<== { void method() { ... } } - ADDS methods to UniversalInstance
|
|
3021
|
+
|
|
2739
3022
|
Also supports expression form: func <<== %exit() (wraps in action_block)
|
|
2740
3023
|
"""
|
|
3024
|
+
from .cssl_types import UniversalInstance
|
|
3025
|
+
|
|
2741
3026
|
target = node.value.get('target')
|
|
2742
3027
|
code_block = node.value.get('code')
|
|
2743
3028
|
source_expr = node.value.get('source') # For expression form: func <<== expr
|
|
@@ -2749,6 +3034,17 @@ class CSSLRuntime:
|
|
|
2749
3034
|
expr_node = ASTNode('expression', value=source_expr)
|
|
2750
3035
|
code_block = ASTNode('action_block', children=[expr_node])
|
|
2751
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
|
+
|
|
2752
3048
|
# Get function name from target
|
|
2753
3049
|
func_name = None
|
|
2754
3050
|
if isinstance(target, ASTNode):
|
|
@@ -2796,6 +3092,60 @@ class CSSLRuntime:
|
|
|
2796
3092
|
|
|
2797
3093
|
return None
|
|
2798
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
|
+
|
|
2799
3149
|
def _exec_infuse_right(self, node: ASTNode) -> Any:
|
|
2800
3150
|
"""Execute right-side code infusion (==>>)"""
|
|
2801
3151
|
source = node.value.get('source')
|
|
@@ -2882,7 +3232,13 @@ class CSSLRuntime:
|
|
|
2882
3232
|
if self._current_instance is None:
|
|
2883
3233
|
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
2884
3234
|
member = target.value.get('member')
|
|
2885
|
-
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)
|
|
2886
3242
|
elif isinstance(target, str):
|
|
2887
3243
|
self.scope.set(target, value)
|
|
2888
3244
|
|
|
@@ -3115,6 +3471,83 @@ class CSSLRuntime:
|
|
|
3115
3471
|
# Return None if instance doesn't exist (can be created via ==>)
|
|
3116
3472
|
return None
|
|
3117
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
|
+
|
|
3118
3551
|
if node.type == 'new':
|
|
3119
3552
|
# Create new instance of a class: new ClassName(args)
|
|
3120
3553
|
return self._eval_new(node)
|
|
@@ -3188,6 +3621,14 @@ class CSSLRuntime:
|
|
|
3188
3621
|
if node.type == 'unary':
|
|
3189
3622
|
return self._eval_unary(node)
|
|
3190
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
|
+
|
|
3191
3632
|
if node.type == 'non_null_assert':
|
|
3192
3633
|
# *$var, *@module, *identifier - assert value is not null/None
|
|
3193
3634
|
operand = node.value.get('operand')
|
|
@@ -3473,6 +3914,74 @@ class CSSLRuntime:
|
|
|
3473
3914
|
|
|
3474
3915
|
return None
|
|
3475
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
|
+
|
|
3476
3985
|
def _eval_call(self, node: ASTNode) -> Any:
|
|
3477
3986
|
"""Evaluate function call with optional named arguments"""
|
|
3478
3987
|
callee_node = node.value.get('callee')
|
|
@@ -3516,7 +4025,33 @@ class CSSLRuntime:
|
|
|
3516
4025
|
if isinstance(callee, ASTNode) and callee.type == 'function':
|
|
3517
4026
|
return self._call_function(callee, args, kwargs)
|
|
3518
4027
|
|
|
3519
|
-
|
|
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)
|
|
3520
4055
|
|
|
3521
4056
|
# Build detailed error with suggestions
|
|
3522
4057
|
available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
|
|
@@ -3658,6 +4193,22 @@ class CSSLRuntime:
|
|
|
3658
4193
|
hint
|
|
3659
4194
|
)
|
|
3660
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
|
+
|
|
3661
4212
|
if not isinstance(class_def, CSSLClass):
|
|
3662
4213
|
raise CSSLRuntimeError(
|
|
3663
4214
|
f"'{class_name}' is not a class",
|
|
@@ -3955,24 +4506,40 @@ class CSSLRuntime:
|
|
|
3955
4506
|
# Direct this->member access
|
|
3956
4507
|
instance = self._current_instance
|
|
3957
4508
|
|
|
3958
|
-
# Check if
|
|
3959
|
-
|
|
3960
|
-
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')
|
|
3961
4511
|
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
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]
|
|
3971
4533
|
|
|
3972
4534
|
# Build helpful error with available members
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
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 = []
|
|
3976
4543
|
all_available = available_members + available_methods
|
|
3977
4544
|
similar = _find_similar_names(member, all_available)
|
|
3978
4545
|
|
|
@@ -4112,6 +4679,48 @@ class CSSLRuntime:
|
|
|
4112
4679
|
hint
|
|
4113
4680
|
)
|
|
4114
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
|
+
|
|
4115
4724
|
# === STRING METHODS ===
|
|
4116
4725
|
if isinstance(obj, str):
|
|
4117
4726
|
string_methods = self._get_string_method(obj, member)
|
|
@@ -4350,6 +4959,12 @@ class CSSLRuntime:
|
|
|
4350
4959
|
obj.set_member(member, value)
|
|
4351
4960
|
return
|
|
4352
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
|
+
|
|
4353
4968
|
# Check for SharedObjectProxy - directly access underlying object
|
|
4354
4969
|
# This is more robust than relying on the proxy's __setattr__
|
|
4355
4970
|
if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
|