IncludeCPP 4.0.3__py3-none-any.whl → 4.2.1__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 +164 -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 +424 -98
- includecpp/core/cssl/cssl_runtime.py +480 -38
- includecpp/core/cssl/cssl_syntax.py +88 -4
- includecpp/core/cssl/cssl_types.py +31 -2
- includecpp/generator/parser.cpp +121 -4
- includecpp/generator/parser.h +6 -0
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/METADATA +101 -1
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/RECORD +22 -18
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/WHEEL +0 -0
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.3.dist-info → includecpp-4.2.1.dist-info}/top_level.txt +0 -0
|
@@ -883,6 +883,18 @@ class CSSLRuntime:
|
|
|
883
883
|
class_params = class_info.get('class_params', [])
|
|
884
884
|
extends_args = class_info.get('extends_args', [])
|
|
885
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
|
+
|
|
886
898
|
for child in node.children:
|
|
887
899
|
if child.type == 'constructor':
|
|
888
900
|
# New-style constructor from 'constr' keyword
|
|
@@ -1007,6 +1019,119 @@ class CSSLRuntime:
|
|
|
1007
1019
|
self._current_instance = old_instance
|
|
1008
1020
|
return wrapper
|
|
1009
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
|
+
|
|
1010
1135
|
def _exec_function(self, node: ASTNode) -> Any:
|
|
1011
1136
|
"""Execute function definition - registers it and handles extends/overwrites.
|
|
1012
1137
|
|
|
@@ -1172,16 +1297,22 @@ class CSSLRuntime:
|
|
|
1172
1297
|
if ref_member and hasattr(ref_obj, ref_member):
|
|
1173
1298
|
original_method = getattr(ref_obj, ref_member)
|
|
1174
1299
|
runtime = self
|
|
1300
|
+
# Store the original method before wrapping
|
|
1301
|
+
_saved_original = original_method
|
|
1175
1302
|
def appended_wrapper(*args, **kwargs):
|
|
1176
|
-
# Run original first
|
|
1303
|
+
# Run original first (use saved reference to avoid recursion)
|
|
1177
1304
|
result = None
|
|
1178
|
-
if callable(
|
|
1305
|
+
if callable(_saved_original):
|
|
1179
1306
|
try:
|
|
1180
|
-
result =
|
|
1307
|
+
result = _saved_original(*args, **kwargs)
|
|
1181
1308
|
except:
|
|
1182
1309
|
pass
|
|
1183
|
-
# Then run appended code
|
|
1184
|
-
|
|
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
|
|
1185
1316
|
try:
|
|
1186
1317
|
setattr(ref_obj, ref_member, appended_wrapper)
|
|
1187
1318
|
except (AttributeError, TypeError):
|
|
@@ -1546,13 +1677,15 @@ class CSSLRuntime:
|
|
|
1546
1677
|
|
|
1547
1678
|
# Bind parameters - handle both positional and named arguments
|
|
1548
1679
|
for i, param in enumerate(params):
|
|
1549
|
-
# Extract param name and
|
|
1680
|
+
# Extract param name, type, and default from dict format: {'name': 'a', 'type': 'int', 'default': ...}
|
|
1550
1681
|
if isinstance(param, dict):
|
|
1551
1682
|
param_name = param['name']
|
|
1552
1683
|
param_type = param.get('type', '')
|
|
1684
|
+
param_default = param.get('default') # v4.2.0: Default value AST node
|
|
1553
1685
|
else:
|
|
1554
1686
|
param_name = param
|
|
1555
1687
|
param_type = ''
|
|
1688
|
+
param_default = None
|
|
1556
1689
|
|
|
1557
1690
|
# Check if this is an 'open' parameter - receives all args as a list
|
|
1558
1691
|
# The parser sets param['open'] = True for 'open' keyword
|
|
@@ -1575,6 +1708,10 @@ class CSSLRuntime:
|
|
|
1575
1708
|
elif i < len(args):
|
|
1576
1709
|
# Positional argument
|
|
1577
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)
|
|
1578
1715
|
else:
|
|
1579
1716
|
new_scope.set(param_name, None)
|
|
1580
1717
|
|
|
@@ -1594,11 +1731,24 @@ class CSSLRuntime:
|
|
|
1594
1731
|
args, kwargs, {}, is_constructor=False
|
|
1595
1732
|
)
|
|
1596
1733
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
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)
|
|
1602
1752
|
except CSSLReturn as ret:
|
|
1603
1753
|
return_value = ret.value
|
|
1604
1754
|
|
|
@@ -1981,6 +2131,71 @@ class CSSLRuntime:
|
|
|
1981
2131
|
|
|
1982
2132
|
return None
|
|
1983
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
|
+
|
|
1984
2199
|
def _exec_createcmd_inject(self, node: ASTNode) -> Any:
|
|
1985
2200
|
"""Execute createcmd injection: createcmd('cmd') <== { action }"""
|
|
1986
2201
|
command_call = node.value.get('command_call')
|
|
@@ -2549,24 +2764,60 @@ class CSSLRuntime:
|
|
|
2549
2764
|
if filter_info:
|
|
2550
2765
|
source = self._apply_injection_filter(source, filter_info)
|
|
2551
2766
|
|
|
2552
|
-
# Get current target value for add/move modes
|
|
2767
|
+
# Get current target value for add/move/replace modes (needed for UniversalInstance handling)
|
|
2553
2768
|
current_value = None
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
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
|
|
2560
2774
|
|
|
2561
2775
|
# Determine final value based on mode
|
|
2562
2776
|
if mode == 'replace':
|
|
2563
|
-
|
|
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
|
|
2564
2797
|
elif mode == 'add':
|
|
2565
2798
|
# Copy & add - preserve target and add source
|
|
2566
|
-
from .cssl_types import CSSLInstance
|
|
2799
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2567
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
|
|
2568
2819
|
# Special handling for CSSLInstance - merge classes
|
|
2569
|
-
|
|
2820
|
+
elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
|
|
2570
2821
|
# Add the new class instance as a member with class name as key
|
|
2571
2822
|
class_name = source._class.name
|
|
2572
2823
|
current_value._members[class_name] = source
|
|
@@ -2873,7 +3124,7 @@ class CSSLRuntime:
|
|
|
2873
3124
|
func_info = child.value
|
|
2874
3125
|
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2875
3126
|
if func_name:
|
|
2876
|
-
instance.set_method(func_name, child)
|
|
3127
|
+
instance.set_method(func_name, child, self)
|
|
2877
3128
|
elif child.type == 'var_declaration':
|
|
2878
3129
|
# Extract variable and value
|
|
2879
3130
|
var_info = child.value
|
|
@@ -2981,7 +3232,13 @@ class CSSLRuntime:
|
|
|
2981
3232
|
if self._current_instance is None:
|
|
2982
3233
|
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
2983
3234
|
member = target.value.get('member')
|
|
2984
|
-
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)
|
|
2985
3242
|
elif isinstance(target, str):
|
|
2986
3243
|
self.scope.set(target, value)
|
|
2987
3244
|
|
|
@@ -3214,6 +3471,83 @@ class CSSLRuntime:
|
|
|
3214
3471
|
# Return None if instance doesn't exist (can be created via ==>)
|
|
3215
3472
|
return None
|
|
3216
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
|
+
|
|
3217
3551
|
if node.type == 'new':
|
|
3218
3552
|
# Create new instance of a class: new ClassName(args)
|
|
3219
3553
|
return self._eval_new(node)
|
|
@@ -3287,6 +3621,14 @@ class CSSLRuntime:
|
|
|
3287
3621
|
if node.type == 'unary':
|
|
3288
3622
|
return self._eval_unary(node)
|
|
3289
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
|
+
|
|
3290
3632
|
if node.type == 'non_null_assert':
|
|
3291
3633
|
# *$var, *@module, *identifier - assert value is not null/None
|
|
3292
3634
|
operand = node.value.get('operand')
|
|
@@ -3572,6 +3914,74 @@ class CSSLRuntime:
|
|
|
3572
3914
|
|
|
3573
3915
|
return None
|
|
3574
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
|
+
|
|
3575
3985
|
def _eval_call(self, node: ASTNode) -> Any:
|
|
3576
3986
|
"""Evaluate function call with optional named arguments"""
|
|
3577
3987
|
callee_node = node.value.get('callee')
|
|
@@ -3783,6 +4193,22 @@ class CSSLRuntime:
|
|
|
3783
4193
|
hint
|
|
3784
4194
|
)
|
|
3785
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
|
+
|
|
3786
4212
|
if not isinstance(class_def, CSSLClass):
|
|
3787
4213
|
raise CSSLRuntimeError(
|
|
3788
4214
|
f"'{class_name}' is not a class",
|
|
@@ -4080,24 +4506,40 @@ class CSSLRuntime:
|
|
|
4080
4506
|
# Direct this->member access
|
|
4081
4507
|
instance = self._current_instance
|
|
4082
4508
|
|
|
4083
|
-
# Check if
|
|
4084
|
-
|
|
4085
|
-
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')
|
|
4086
4511
|
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
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]
|
|
4096
4533
|
|
|
4097
4534
|
# Build helpful error with available members
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
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 = []
|
|
4101
4543
|
all_available = available_members + available_methods
|
|
4102
4544
|
similar = _find_similar_names(member, all_available)
|
|
4103
4545
|
|