IncludeCPP 4.0.2__py3-none-any.whl → 4.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- includecpp/__init__.py +1 -1
- includecpp/__init__.pyi +3 -1
- includecpp/cli/commands.py +3 -3
- includecpp/core/ai_integration.py +46 -13
- includecpp/core/cpp_api_extensions.pyi +622 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +186 -5
- includecpp/core/cssl/cssl_builtins.py +87 -2
- includecpp/core/cssl/cssl_languages.py +836 -0
- includecpp/core/cssl/cssl_parser.py +138 -38
- includecpp/core/cssl/cssl_runtime.py +364 -24
- 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/vscode/cssl/package.json +43 -1
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +140 -17
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.dist-info}/METADATA +101 -1
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.dist-info}/RECORD +22 -20
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.dist-info}/WHEEL +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.dist-info}/entry_points.txt +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.0.2.dist-info → includecpp-4.1.0.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:
|
|
@@ -1310,29 +1330,38 @@ class CSSLRuntime:
|
|
|
1310
1330
|
def _exec_instance_declaration(self, node: ASTNode) -> Any:
|
|
1311
1331
|
"""Execute instance declaration: instance<"name"> varName;
|
|
1312
1332
|
|
|
1313
|
-
Gets or creates a shared instance by name.
|
|
1333
|
+
Gets or creates a universal shared instance by name.
|
|
1334
|
+
Universal instances are accessible from CSSL, Python, and C++.
|
|
1335
|
+
|
|
1336
|
+
Usage:
|
|
1337
|
+
instance<"myContainer"> container; // Creates or gets instance
|
|
1338
|
+
container.member = "value"; // Set member
|
|
1339
|
+
container +<<== { void func() {} } // Inject methods
|
|
1314
1340
|
"""
|
|
1315
|
-
from
|
|
1341
|
+
from .cssl_types import UniversalInstance
|
|
1342
|
+
|
|
1316
1343
|
decl = node.value
|
|
1317
1344
|
instance_name = decl.get('instance_name')
|
|
1318
1345
|
var_name = decl.get('name')
|
|
1319
1346
|
value_node = decl.get('value')
|
|
1320
1347
|
|
|
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}')
|
|
1348
|
+
# Get existing or create new universal instance
|
|
1349
|
+
instance = UniversalInstance.get_or_create(instance_name)
|
|
1327
1350
|
|
|
1328
|
-
# If value is provided,
|
|
1351
|
+
# If value is provided, set it as initial content
|
|
1329
1352
|
if value_node:
|
|
1330
|
-
|
|
1331
|
-
#
|
|
1332
|
-
|
|
1353
|
+
initial_value = self._evaluate(value_node)
|
|
1354
|
+
# If it's a dict, set all keys as members
|
|
1355
|
+
if isinstance(initial_value, dict):
|
|
1356
|
+
for key, val in initial_value.items():
|
|
1357
|
+
instance.set_member(key, val)
|
|
1358
|
+
else:
|
|
1359
|
+
instance.set_member('value', initial_value)
|
|
1333
1360
|
|
|
1334
|
-
# Store in scope
|
|
1361
|
+
# Store in scope and global scope for access
|
|
1335
1362
|
self.scope.set(var_name, instance)
|
|
1363
|
+
self.global_scope.set(f'${instance_name}', instance)
|
|
1364
|
+
|
|
1336
1365
|
return instance
|
|
1337
1366
|
|
|
1338
1367
|
def _exec_super_func(self, node: ASTNode) -> Any:
|
|
@@ -2520,24 +2549,60 @@ class CSSLRuntime:
|
|
|
2520
2549
|
if filter_info:
|
|
2521
2550
|
source = self._apply_injection_filter(source, filter_info)
|
|
2522
2551
|
|
|
2523
|
-
# Get current target value for add/move modes
|
|
2552
|
+
# Get current target value for add/move/replace modes (needed for UniversalInstance handling)
|
|
2524
2553
|
current_value = None
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
current_value = None
|
|
2554
|
+
try:
|
|
2555
|
+
current_value = self._evaluate(target)
|
|
2556
|
+
except Exception:
|
|
2557
|
+
# Target might not exist yet, that's okay for add mode
|
|
2558
|
+
current_value = None
|
|
2531
2559
|
|
|
2532
2560
|
# Determine final value based on mode
|
|
2533
2561
|
if mode == 'replace':
|
|
2534
|
-
|
|
2562
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2563
|
+
# Special handling for UniversalInstance targets - inject instead of replace
|
|
2564
|
+
if isinstance(current_value, UniversalInstance):
|
|
2565
|
+
if isinstance(source, CSSLClass):
|
|
2566
|
+
current_value.set_member(source.name, source)
|
|
2567
|
+
final_value = current_value
|
|
2568
|
+
elif isinstance(source, ASTNode) and source.type == 'function':
|
|
2569
|
+
func_info = source.value
|
|
2570
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2571
|
+
if func_name:
|
|
2572
|
+
current_value.set_method(func_name, source, self)
|
|
2573
|
+
final_value = current_value
|
|
2574
|
+
elif isinstance(source, CSSLInstance):
|
|
2575
|
+
current_value.set_member(source._class.name, source)
|
|
2576
|
+
final_value = current_value
|
|
2577
|
+
else:
|
|
2578
|
+
# For other types, store as member with source type name
|
|
2579
|
+
final_value = source
|
|
2580
|
+
else:
|
|
2581
|
+
final_value = source
|
|
2535
2582
|
elif mode == 'add':
|
|
2536
2583
|
# Copy & add - preserve target and add source
|
|
2537
|
-
from .cssl_types import CSSLInstance
|
|
2584
|
+
from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
|
|
2538
2585
|
|
|
2586
|
+
# Special handling for UniversalInstance + CSSLClass
|
|
2587
|
+
if isinstance(current_value, UniversalInstance) and isinstance(source, CSSLClass):
|
|
2588
|
+
# Inject class definition into universal instance
|
|
2589
|
+
current_value.set_member(source.name, source)
|
|
2590
|
+
final_value = current_value
|
|
2591
|
+
# Special handling for UniversalInstance + Function (AST node)
|
|
2592
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, ASTNode) and source.type == 'function':
|
|
2593
|
+
# Inject function as a method into universal instance
|
|
2594
|
+
func_info = source.value
|
|
2595
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2596
|
+
if func_name:
|
|
2597
|
+
current_value.set_method(func_name, source, self)
|
|
2598
|
+
final_value = current_value
|
|
2599
|
+
# Special handling for UniversalInstance + CSSLInstance
|
|
2600
|
+
elif isinstance(current_value, UniversalInstance) and isinstance(source, CSSLInstance):
|
|
2601
|
+
class_name = source._class.name
|
|
2602
|
+
current_value.set_member(class_name, source)
|
|
2603
|
+
final_value = current_value
|
|
2539
2604
|
# Special handling for CSSLInstance - merge classes
|
|
2540
|
-
|
|
2605
|
+
elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
|
|
2541
2606
|
# Add the new class instance as a member with class name as key
|
|
2542
2607
|
class_name = source._class.name
|
|
2543
2608
|
current_value._members[class_name] = source
|
|
@@ -2736,8 +2801,13 @@ class CSSLRuntime:
|
|
|
2736
2801
|
- add: func +<<== { code } - ADDS code to function (both execute)
|
|
2737
2802
|
- remove: func -<<== { code } - REMOVES matching code from function
|
|
2738
2803
|
|
|
2804
|
+
Also supports instance injection:
|
|
2805
|
+
- instance +<<== { void method() { ... } } - ADDS methods to UniversalInstance
|
|
2806
|
+
|
|
2739
2807
|
Also supports expression form: func <<== %exit() (wraps in action_block)
|
|
2740
2808
|
"""
|
|
2809
|
+
from .cssl_types import UniversalInstance
|
|
2810
|
+
|
|
2741
2811
|
target = node.value.get('target')
|
|
2742
2812
|
code_block = node.value.get('code')
|
|
2743
2813
|
source_expr = node.value.get('source') # For expression form: func <<== expr
|
|
@@ -2749,6 +2819,17 @@ class CSSLRuntime:
|
|
|
2749
2819
|
expr_node = ASTNode('expression', value=source_expr)
|
|
2750
2820
|
code_block = ASTNode('action_block', children=[expr_node])
|
|
2751
2821
|
|
|
2822
|
+
# Check if target is a UniversalInstance
|
|
2823
|
+
target_value = None
|
|
2824
|
+
if isinstance(target, ASTNode) and target.type == 'identifier':
|
|
2825
|
+
target_value = self.scope.get(target.value)
|
|
2826
|
+
if target_value is None:
|
|
2827
|
+
target_value = self.global_scope.get(target.value)
|
|
2828
|
+
|
|
2829
|
+
# Handle UniversalInstance injection
|
|
2830
|
+
if isinstance(target_value, UniversalInstance):
|
|
2831
|
+
return self._inject_into_instance(target_value, code_block, mode)
|
|
2832
|
+
|
|
2752
2833
|
# Get function name from target
|
|
2753
2834
|
func_name = None
|
|
2754
2835
|
if isinstance(target, ASTNode):
|
|
@@ -2796,6 +2877,60 @@ class CSSLRuntime:
|
|
|
2796
2877
|
|
|
2797
2878
|
return None
|
|
2798
2879
|
|
|
2880
|
+
def _inject_into_instance(self, instance: Any, code_block: Any, mode: str) -> Any:
|
|
2881
|
+
"""Inject code/methods into a UniversalInstance.
|
|
2882
|
+
|
|
2883
|
+
Usage:
|
|
2884
|
+
instance<"myContainer"> container;
|
|
2885
|
+
container +<<== {
|
|
2886
|
+
void sayHello() { printl("Hello!"); }
|
|
2887
|
+
int value = 42;
|
|
2888
|
+
}
|
|
2889
|
+
"""
|
|
2890
|
+
from .cssl_types import UniversalInstance
|
|
2891
|
+
|
|
2892
|
+
if not isinstance(instance, UniversalInstance):
|
|
2893
|
+
return None
|
|
2894
|
+
|
|
2895
|
+
if code_block is None:
|
|
2896
|
+
return None
|
|
2897
|
+
|
|
2898
|
+
# Store the raw injection
|
|
2899
|
+
instance.add_injection(code_block)
|
|
2900
|
+
|
|
2901
|
+
# Parse the code block for function definitions and variable declarations
|
|
2902
|
+
if isinstance(code_block, ASTNode):
|
|
2903
|
+
children = code_block.children if hasattr(code_block, 'children') else []
|
|
2904
|
+
|
|
2905
|
+
for child in children:
|
|
2906
|
+
if isinstance(child, ASTNode):
|
|
2907
|
+
if child.type == 'function':
|
|
2908
|
+
# Extract function name and store the AST node
|
|
2909
|
+
func_info = child.value
|
|
2910
|
+
func_name = func_info.get('name') if isinstance(func_info, dict) else None
|
|
2911
|
+
if func_name:
|
|
2912
|
+
instance.set_method(func_name, child, self)
|
|
2913
|
+
elif child.type == 'var_declaration':
|
|
2914
|
+
# Extract variable and value
|
|
2915
|
+
var_info = child.value
|
|
2916
|
+
if isinstance(var_info, dict):
|
|
2917
|
+
var_name = var_info.get('name')
|
|
2918
|
+
value_node = var_info.get('value')
|
|
2919
|
+
if var_name:
|
|
2920
|
+
value = self._evaluate(value_node) if value_node else None
|
|
2921
|
+
instance.set_member(var_name, value)
|
|
2922
|
+
elif child.type == 'typed_var_declaration':
|
|
2923
|
+
# Typed variable declaration
|
|
2924
|
+
var_info = child.value
|
|
2925
|
+
if isinstance(var_info, dict):
|
|
2926
|
+
var_name = var_info.get('name')
|
|
2927
|
+
value_node = var_info.get('value')
|
|
2928
|
+
if var_name:
|
|
2929
|
+
value = self._evaluate(value_node) if value_node else None
|
|
2930
|
+
instance.set_member(var_name, value)
|
|
2931
|
+
|
|
2932
|
+
return instance
|
|
2933
|
+
|
|
2799
2934
|
def _exec_infuse_right(self, node: ASTNode) -> Any:
|
|
2800
2935
|
"""Execute right-side code infusion (==>>)"""
|
|
2801
2936
|
source = node.value.get('source')
|
|
@@ -3115,6 +3250,45 @@ class CSSLRuntime:
|
|
|
3115
3250
|
# Return None if instance doesn't exist (can be created via ==>)
|
|
3116
3251
|
return None
|
|
3117
3252
|
|
|
3253
|
+
# v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
|
|
3254
|
+
if node.type == 'lang_instance_ref':
|
|
3255
|
+
ref = node.value # {'lang': 'cpp', 'instance': 'ClassName'}
|
|
3256
|
+
lang_id = ref['lang']
|
|
3257
|
+
instance_name = ref['instance']
|
|
3258
|
+
|
|
3259
|
+
# First, try to get the language support object from scope
|
|
3260
|
+
lang_support = self.scope.get(lang_id)
|
|
3261
|
+
if lang_support is None:
|
|
3262
|
+
lang_support = self.global_scope.get(lang_id)
|
|
3263
|
+
|
|
3264
|
+
# If not found in scope, try to get from modules
|
|
3265
|
+
if lang_support is None:
|
|
3266
|
+
lang_support = self._modules.get(lang_id)
|
|
3267
|
+
|
|
3268
|
+
# If still not found, try getting default language support
|
|
3269
|
+
if lang_support is None:
|
|
3270
|
+
from .cssl_languages import get_language
|
|
3271
|
+
lang_support = get_language(lang_id)
|
|
3272
|
+
|
|
3273
|
+
if lang_support is not None:
|
|
3274
|
+
# Check if it's a LanguageSupport object with get_instance method
|
|
3275
|
+
if hasattr(lang_support, 'get_instance'):
|
|
3276
|
+
instance = lang_support.get_instance(instance_name)
|
|
3277
|
+
if instance is not None:
|
|
3278
|
+
return instance
|
|
3279
|
+
# Check _instances dict directly
|
|
3280
|
+
if hasattr(lang_support, '_instances'):
|
|
3281
|
+
if instance_name in lang_support._instances:
|
|
3282
|
+
return lang_support._instances[instance_name]
|
|
3283
|
+
|
|
3284
|
+
# Build helpful error message
|
|
3285
|
+
hint = f"Use '{lang_id}.share(\"{instance_name}\", instance)' to register the instance first."
|
|
3286
|
+
raise self._format_error(
|
|
3287
|
+
node.line if hasattr(node, 'line') else 0,
|
|
3288
|
+
f"Cross-language instance '{lang_id}${instance_name}' not found",
|
|
3289
|
+
hint
|
|
3290
|
+
)
|
|
3291
|
+
|
|
3118
3292
|
if node.type == 'new':
|
|
3119
3293
|
# Create new instance of a class: new ClassName(args)
|
|
3120
3294
|
return self._eval_new(node)
|
|
@@ -3188,6 +3362,14 @@ class CSSLRuntime:
|
|
|
3188
3362
|
if node.type == 'unary':
|
|
3189
3363
|
return self._eval_unary(node)
|
|
3190
3364
|
|
|
3365
|
+
# Increment: ++i or i++
|
|
3366
|
+
if node.type == 'increment':
|
|
3367
|
+
return self._eval_increment(node)
|
|
3368
|
+
|
|
3369
|
+
# Decrement: --i or i--
|
|
3370
|
+
if node.type == 'decrement':
|
|
3371
|
+
return self._eval_decrement(node)
|
|
3372
|
+
|
|
3191
3373
|
if node.type == 'non_null_assert':
|
|
3192
3374
|
# *$var, *@module, *identifier - assert value is not null/None
|
|
3193
3375
|
operand = node.value.get('operand')
|
|
@@ -3473,6 +3655,74 @@ class CSSLRuntime:
|
|
|
3473
3655
|
|
|
3474
3656
|
return None
|
|
3475
3657
|
|
|
3658
|
+
def _eval_increment(self, node: ASTNode) -> Any:
|
|
3659
|
+
"""Evaluate increment operation (++i or i++)"""
|
|
3660
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
3661
|
+
operand = node.value.get('operand')
|
|
3662
|
+
|
|
3663
|
+
# Get variable name
|
|
3664
|
+
var_name = None
|
|
3665
|
+
if isinstance(operand, ASTNode):
|
|
3666
|
+
if operand.type == 'identifier':
|
|
3667
|
+
var_name = operand.value
|
|
3668
|
+
elif operand.type == 'shared_ref':
|
|
3669
|
+
# Handle $var++
|
|
3670
|
+
var_name = operand.value
|
|
3671
|
+
current = self.shared_vars.get(var_name, 0)
|
|
3672
|
+
if op_type == 'prefix':
|
|
3673
|
+
self.shared_vars[var_name] = current + 1
|
|
3674
|
+
return current + 1
|
|
3675
|
+
else: # postfix
|
|
3676
|
+
self.shared_vars[var_name] = current + 1
|
|
3677
|
+
return current
|
|
3678
|
+
|
|
3679
|
+
if var_name:
|
|
3680
|
+
current = self.scope.get(var_name, 0)
|
|
3681
|
+
if op_type == 'prefix':
|
|
3682
|
+
# ++i: increment then return
|
|
3683
|
+
self.scope.set(var_name, current + 1)
|
|
3684
|
+
return current + 1
|
|
3685
|
+
else:
|
|
3686
|
+
# i++: return then increment
|
|
3687
|
+
self.scope.set(var_name, current + 1)
|
|
3688
|
+
return current
|
|
3689
|
+
|
|
3690
|
+
return None
|
|
3691
|
+
|
|
3692
|
+
def _eval_decrement(self, node: ASTNode) -> Any:
|
|
3693
|
+
"""Evaluate decrement operation (--i or i--)"""
|
|
3694
|
+
op_type = node.value.get('op') # 'prefix' or 'postfix'
|
|
3695
|
+
operand = node.value.get('operand')
|
|
3696
|
+
|
|
3697
|
+
# Get variable name
|
|
3698
|
+
var_name = None
|
|
3699
|
+
if isinstance(operand, ASTNode):
|
|
3700
|
+
if operand.type == 'identifier':
|
|
3701
|
+
var_name = operand.value
|
|
3702
|
+
elif operand.type == 'shared_ref':
|
|
3703
|
+
# Handle $var--
|
|
3704
|
+
var_name = operand.value
|
|
3705
|
+
current = self.shared_vars.get(var_name, 0)
|
|
3706
|
+
if op_type == 'prefix':
|
|
3707
|
+
self.shared_vars[var_name] = current - 1
|
|
3708
|
+
return current - 1
|
|
3709
|
+
else: # postfix
|
|
3710
|
+
self.shared_vars[var_name] = current - 1
|
|
3711
|
+
return current
|
|
3712
|
+
|
|
3713
|
+
if var_name:
|
|
3714
|
+
current = self.scope.get(var_name, 0)
|
|
3715
|
+
if op_type == 'prefix':
|
|
3716
|
+
# --i: decrement then return
|
|
3717
|
+
self.scope.set(var_name, current - 1)
|
|
3718
|
+
return current - 1
|
|
3719
|
+
else:
|
|
3720
|
+
# i--: return then decrement
|
|
3721
|
+
self.scope.set(var_name, current - 1)
|
|
3722
|
+
return current
|
|
3723
|
+
|
|
3724
|
+
return None
|
|
3725
|
+
|
|
3476
3726
|
def _eval_call(self, node: ASTNode) -> Any:
|
|
3477
3727
|
"""Evaluate function call with optional named arguments"""
|
|
3478
3728
|
callee_node = node.value.get('callee')
|
|
@@ -3516,7 +3766,33 @@ class CSSLRuntime:
|
|
|
3516
3766
|
if isinstance(callee, ASTNode) and callee.type == 'function':
|
|
3517
3767
|
return self._call_function(callee, args, kwargs)
|
|
3518
3768
|
|
|
3519
|
-
|
|
3769
|
+
# Extract callee name for error messages
|
|
3770
|
+
if isinstance(callee_node, ASTNode) and hasattr(callee_node, 'value'):
|
|
3771
|
+
val = callee_node.value
|
|
3772
|
+
if isinstance(val, str):
|
|
3773
|
+
callee_name = val
|
|
3774
|
+
elif isinstance(val, dict):
|
|
3775
|
+
# For member access nodes like obj.method, get the member name
|
|
3776
|
+
if 'member' in val:
|
|
3777
|
+
obj_node = val.get('object')
|
|
3778
|
+
member = val.get('member')
|
|
3779
|
+
obj_name = obj_node.value if isinstance(obj_node, ASTNode) else str(obj_node)
|
|
3780
|
+
callee_name = f"{obj_name}.{member}"
|
|
3781
|
+
# For call nodes, try to get the callee name
|
|
3782
|
+
elif 'callee' in val:
|
|
3783
|
+
callee_val = val.get('callee')
|
|
3784
|
+
if isinstance(callee_val, ASTNode):
|
|
3785
|
+
callee_name = callee_val.value if isinstance(callee_val.value, str) else str(callee_val.value)
|
|
3786
|
+
else:
|
|
3787
|
+
callee_name = str(callee_val)
|
|
3788
|
+
elif 'name' in val:
|
|
3789
|
+
callee_name = str(val.get('name'))
|
|
3790
|
+
else:
|
|
3791
|
+
callee_name = str(val)
|
|
3792
|
+
else:
|
|
3793
|
+
callee_name = str(val)
|
|
3794
|
+
else:
|
|
3795
|
+
callee_name = str(callee_node)
|
|
3520
3796
|
|
|
3521
3797
|
# Build detailed error with suggestions
|
|
3522
3798
|
available_funcs = _get_available_functions(self.scope, self.global_scope, self.builtins)
|
|
@@ -3658,6 +3934,22 @@ class CSSLRuntime:
|
|
|
3658
3934
|
hint
|
|
3659
3935
|
)
|
|
3660
3936
|
|
|
3937
|
+
# If we got a variable that holds a class reference, use that class
|
|
3938
|
+
if not isinstance(class_def, CSSLClass):
|
|
3939
|
+
# Check if it's a variable holding a class (dynamic class instantiation)
|
|
3940
|
+
if hasattr(class_def, '__class__') and isinstance(class_def, CSSLClass):
|
|
3941
|
+
pass # Already a CSSLClass, continue
|
|
3942
|
+
elif isinstance(class_def, dict) and 'class_def' in class_def:
|
|
3943
|
+
# Injected class reference from +<== operator
|
|
3944
|
+
class_def = class_def['class_def']
|
|
3945
|
+
else:
|
|
3946
|
+
# Not a class - show error
|
|
3947
|
+
raise CSSLRuntimeError(
|
|
3948
|
+
f"'{class_name}' is not a class",
|
|
3949
|
+
node.line,
|
|
3950
|
+
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
3951
|
+
)
|
|
3952
|
+
|
|
3661
3953
|
if not isinstance(class_def, CSSLClass):
|
|
3662
3954
|
raise CSSLRuntimeError(
|
|
3663
3955
|
f"'{class_name}' is not a class",
|
|
@@ -4112,6 +4404,48 @@ class CSSLRuntime:
|
|
|
4112
4404
|
hint
|
|
4113
4405
|
)
|
|
4114
4406
|
|
|
4407
|
+
# === UNIVERSAL INSTANCE METHODS ===
|
|
4408
|
+
from .cssl_types import UniversalInstance
|
|
4409
|
+
if isinstance(obj, UniversalInstance):
|
|
4410
|
+
# Check for member variable
|
|
4411
|
+
if obj.has_member(member):
|
|
4412
|
+
return obj.get_member(member)
|
|
4413
|
+
# Check for method
|
|
4414
|
+
if obj.has_method(member):
|
|
4415
|
+
method_node = obj.get_method(member)
|
|
4416
|
+
# Create a callable that executes the method in context
|
|
4417
|
+
def instance_method_caller(*args, **kwargs):
|
|
4418
|
+
# Set 'this' to refer to the instance
|
|
4419
|
+
old_this = self.scope.get('this')
|
|
4420
|
+
self.scope.set('this', obj)
|
|
4421
|
+
try:
|
|
4422
|
+
return self._call_function(method_node, list(args))
|
|
4423
|
+
finally:
|
|
4424
|
+
if old_this is not None:
|
|
4425
|
+
self.scope.set('this', old_this)
|
|
4426
|
+
else:
|
|
4427
|
+
self.scope.remove('this') if hasattr(self.scope, 'remove') else None
|
|
4428
|
+
return instance_method_caller
|
|
4429
|
+
# Build helpful error with available members
|
|
4430
|
+
instance_name = obj.name
|
|
4431
|
+
available_members = list(obj.get_all_members().keys())
|
|
4432
|
+
available_methods = list(obj.get_all_methods().keys())
|
|
4433
|
+
all_available = available_members + available_methods
|
|
4434
|
+
similar = _find_similar_names(member, all_available)
|
|
4435
|
+
|
|
4436
|
+
if similar:
|
|
4437
|
+
hint = f"Did you mean: {', '.join(similar)}?"
|
|
4438
|
+
elif all_available:
|
|
4439
|
+
hint = f"Available: {', '.join(all_available[:5])}"
|
|
4440
|
+
else:
|
|
4441
|
+
hint = f"Instance '{instance_name}' has no accessible members."
|
|
4442
|
+
|
|
4443
|
+
raise self._format_error(
|
|
4444
|
+
node.line,
|
|
4445
|
+
f"Instance '{instance_name}' has no member or method '{member}'",
|
|
4446
|
+
hint
|
|
4447
|
+
)
|
|
4448
|
+
|
|
4115
4449
|
# === STRING METHODS ===
|
|
4116
4450
|
if isinstance(obj, str):
|
|
4117
4451
|
string_methods = self._get_string_method(obj, member)
|
|
@@ -4350,6 +4684,12 @@ class CSSLRuntime:
|
|
|
4350
4684
|
obj.set_member(member, value)
|
|
4351
4685
|
return
|
|
4352
4686
|
|
|
4687
|
+
# Check for UniversalInstance - use set_member method
|
|
4688
|
+
from .cssl_types import UniversalInstance
|
|
4689
|
+
if isinstance(obj, UniversalInstance):
|
|
4690
|
+
obj.set_member(member, value)
|
|
4691
|
+
return
|
|
4692
|
+
|
|
4353
4693
|
# Check for SharedObjectProxy - directly access underlying object
|
|
4354
4694
|
# This is more robust than relying on the proxy's __setattr__
|
|
4355
4695
|
if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
|
|
@@ -31,6 +31,11 @@ class TokenCategory(Enum):
|
|
|
31
31
|
NULL = auto() # null, None
|
|
32
32
|
PACKAGE_KW = auto() # package, package-includes - NEW
|
|
33
33
|
TYPE_LITERAL = auto() # list, dict - NEW
|
|
34
|
+
# v4.1.0: Multi-language support
|
|
35
|
+
SUPPORTS_KW = auto() # supports keyword (magenta)
|
|
36
|
+
LIBINCLUDE_KW = auto() # libinclude (yellow/gold)
|
|
37
|
+
LANG_PREFIX = auto() # Language prefix before $ (cyan): cpp$, py$, java$
|
|
38
|
+
LANG_INSTANCE = auto() # Instance name after $ (orange): cpp$ClassName
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
@dataclass
|
|
@@ -52,9 +57,17 @@ KEYWORDS = {
|
|
|
52
57
|
'start', 'stop', 'wait_for', 'on_event', 'emit_event',
|
|
53
58
|
'await',
|
|
54
59
|
# NEW: Extended keywords
|
|
55
|
-
'package', 'package-includes', 'exec', 'as', 'global'
|
|
60
|
+
'package', 'package-includes', 'exec', 'as', 'global',
|
|
61
|
+
# v4.1.0: Multi-language support (handled separately for special colors)
|
|
62
|
+
# 'supports', 'libinclude' - see MULTI_LANG_KEYWORDS
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
# v4.1.0: Multi-language keywords with special highlighting
|
|
66
|
+
MULTI_LANG_KEYWORDS = {'supports', 'libinclude'}
|
|
67
|
+
|
|
68
|
+
# v4.1.0: Language identifiers for cross-language instance access
|
|
69
|
+
LANGUAGE_IDS = {'cpp', 'py', 'python', 'java', 'csharp', 'js', 'javascript'}
|
|
70
|
+
|
|
58
71
|
# NEW: Package-related keywords for special highlighting
|
|
59
72
|
PACKAGE_KEYWORDS = {'package', 'package-includes'}
|
|
60
73
|
|
|
@@ -145,6 +158,33 @@ class CSSLSyntaxRules:
|
|
|
145
158
|
category=TokenCategory.TYPE_LITERAL
|
|
146
159
|
))
|
|
147
160
|
|
|
161
|
+
# v4.1.0: Multi-language support keywords
|
|
162
|
+
# 'supports' keyword (magenta) - must be before regular keywords
|
|
163
|
+
rules.append(HighlightRule(
|
|
164
|
+
pattern=r'\bsupports\b',
|
|
165
|
+
category=TokenCategory.SUPPORTS_KW
|
|
166
|
+
))
|
|
167
|
+
|
|
168
|
+
# 'libinclude' keyword (yellow/gold)
|
|
169
|
+
rules.append(HighlightRule(
|
|
170
|
+
pattern=r'\blibinclude\b',
|
|
171
|
+
category=TokenCategory.LIBINCLUDE_KW
|
|
172
|
+
))
|
|
173
|
+
|
|
174
|
+
# v4.1.0: Language$Instance patterns (cpp$ClassName, py$Object)
|
|
175
|
+
# Match language prefix before $ (cyan)
|
|
176
|
+
rules.append(HighlightRule(
|
|
177
|
+
pattern=r'\b(cpp|py|python|java|csharp|js|javascript)\$',
|
|
178
|
+
category=TokenCategory.LANG_PREFIX,
|
|
179
|
+
group=1
|
|
180
|
+
))
|
|
181
|
+
# Match instance name after $ (orange)
|
|
182
|
+
rules.append(HighlightRule(
|
|
183
|
+
pattern=r'\b(?:cpp|py|python|java|csharp|js|javascript)\$([A-Za-z_][A-Za-z0-9_]*)',
|
|
184
|
+
category=TokenCategory.LANG_INSTANCE,
|
|
185
|
+
group=1
|
|
186
|
+
))
|
|
187
|
+
|
|
148
188
|
# Self-references (s@Name, s@Backend.Loop)
|
|
149
189
|
rules.append(HighlightRule(
|
|
150
190
|
pattern=r's@[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*',
|
|
@@ -233,6 +273,11 @@ class ColorScheme:
|
|
|
233
273
|
TokenCategory.NULL: '#ff6464', # Red
|
|
234
274
|
TokenCategory.PACKAGE_KW: '#bd93f9', # Purple for package - NEW
|
|
235
275
|
TokenCategory.TYPE_LITERAL: '#8be9fd', # Cyan for type literals - NEW
|
|
276
|
+
# v4.1.0: Multi-language support colors
|
|
277
|
+
TokenCategory.SUPPORTS_KW: '#ff79c6', # Magenta/Pink for 'supports'
|
|
278
|
+
TokenCategory.LIBINCLUDE_KW: '#f1fa8c',# Yellow/Gold for 'libinclude'
|
|
279
|
+
TokenCategory.LANG_PREFIX: '#8be9fd', # Cyan for language prefix (cpp$)
|
|
280
|
+
TokenCategory.LANG_INSTANCE: '#ffb86c',# Orange for instance name ($ClassName)
|
|
236
281
|
}
|
|
237
282
|
|
|
238
283
|
# Light theme variant
|
|
@@ -252,6 +297,11 @@ class ColorScheme:
|
|
|
252
297
|
TokenCategory.NULL: '#ff0000', # Red
|
|
253
298
|
TokenCategory.PACKAGE_KW: '#8b008b', # DarkMagenta for package - NEW
|
|
254
299
|
TokenCategory.TYPE_LITERAL: '#008b8b', # Dark cyan for type literals - NEW
|
|
300
|
+
# v4.1.0: Multi-language support colors
|
|
301
|
+
TokenCategory.SUPPORTS_KW: '#d63384', # Dark Magenta for 'supports'
|
|
302
|
+
TokenCategory.LIBINCLUDE_KW: '#b8860b',# DarkGoldenrod for 'libinclude'
|
|
303
|
+
TokenCategory.LANG_PREFIX: '#0d6efd', # Blue for language prefix (cpp$)
|
|
304
|
+
TokenCategory.LANG_INSTANCE: '#fd7e14',# Orange for instance name ($ClassName)
|
|
255
305
|
}
|
|
256
306
|
|
|
257
307
|
|
|
@@ -334,6 +384,11 @@ def highlight_cssl_ansi(source: str) -> str:
|
|
|
334
384
|
TokenCategory.NULL: '\033[91m', # Red
|
|
335
385
|
TokenCategory.PACKAGE_KW: '\033[95m', # Magenta for package - NEW
|
|
336
386
|
TokenCategory.TYPE_LITERAL: '\033[96m', # Cyan for type literals - NEW
|
|
387
|
+
# v4.1.0: Multi-language support colors
|
|
388
|
+
TokenCategory.SUPPORTS_KW: '\033[95m', # Magenta for 'supports'
|
|
389
|
+
TokenCategory.LIBINCLUDE_KW: '\033[93m',# Yellow for 'libinclude'
|
|
390
|
+
TokenCategory.LANG_PREFIX: '\033[96m', # Cyan for language prefix (cpp$)
|
|
391
|
+
TokenCategory.LANG_INSTANCE: '\033[33m',# Orange/Yellow for instance name
|
|
337
392
|
}
|
|
338
393
|
RESET = '\033[0m'
|
|
339
394
|
|
|
@@ -439,6 +494,10 @@ def export_textmate_grammar() -> dict:
|
|
|
439
494
|
"name": "comment.line.cssl",
|
|
440
495
|
"match": "#.*$"
|
|
441
496
|
},
|
|
497
|
+
{
|
|
498
|
+
"name": "comment.line.double-slash.cssl",
|
|
499
|
+
"match": "//.*$"
|
|
500
|
+
},
|
|
442
501
|
{
|
|
443
502
|
"name": "string.quoted.double.cssl",
|
|
444
503
|
"match": '"(?:[^"\\\\]|\\\\.)*"'
|
|
@@ -447,6 +506,23 @@ def export_textmate_grammar() -> dict:
|
|
|
447
506
|
"name": "string.quoted.single.cssl",
|
|
448
507
|
"match": "'(?:[^'\\\\]|\\\\.)*'"
|
|
449
508
|
},
|
|
509
|
+
# v4.1.0: Multi-language support
|
|
510
|
+
{
|
|
511
|
+
"name": "keyword.control.supports.cssl",
|
|
512
|
+
"match": "\\bsupports\\b"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"name": "support.function.libinclude.cssl",
|
|
516
|
+
"match": "\\blibinclude\\b"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"name": "variable.language.lang-instance.cssl",
|
|
520
|
+
"match": "\\b(cpp|py|python|java|csharp|js|javascript)\\$([A-Za-z_][A-Za-z0-9_]*)",
|
|
521
|
+
"captures": {
|
|
522
|
+
"1": {"name": "entity.name.type.language.cssl"},
|
|
523
|
+
"2": {"name": "variable.other.instance.cssl"}
|
|
524
|
+
}
|
|
525
|
+
},
|
|
450
526
|
{
|
|
451
527
|
"name": "variable.other.self-reference.cssl",
|
|
452
528
|
"match": "s@[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*"
|
|
@@ -455,9 +531,13 @@ def export_textmate_grammar() -> dict:
|
|
|
455
531
|
"name": "variable.other.module-reference.cssl",
|
|
456
532
|
"match": "@[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*"
|
|
457
533
|
},
|
|
534
|
+
{
|
|
535
|
+
"name": "keyword.other.package.cssl",
|
|
536
|
+
"match": "\\b(package|package-includes)\\b"
|
|
537
|
+
},
|
|
458
538
|
{
|
|
459
539
|
"name": "keyword.control.cssl",
|
|
460
|
-
"match": "\\b(service-init|service-run|service-include|struct|define|if|else|elif|while|for|foreach|in|switch|case|default|break|continue|return|try|catch|await)\\b"
|
|
540
|
+
"match": "\\b(service-init|service-run|service-include|struct|define|class|constr|if|else|elif|while|for|foreach|in|switch|case|default|break|continue|return|try|catch|finally|throw|await|extends|overwrites|global|as|exec)\\b"
|
|
461
541
|
},
|
|
462
542
|
{
|
|
463
543
|
"name": "keyword.operator.cssl",
|
|
@@ -465,7 +545,7 @@ def export_textmate_grammar() -> dict:
|
|
|
465
545
|
},
|
|
466
546
|
{
|
|
467
547
|
"name": "constant.language.cssl",
|
|
468
|
-
"match": "\\b(True|False|true|false|null|None)\\b"
|
|
548
|
+
"match": "\\b(True|False|true|false|null|None|none)\\b"
|
|
469
549
|
},
|
|
470
550
|
{
|
|
471
551
|
"name": "constant.numeric.cssl",
|
|
@@ -473,7 +553,11 @@ def export_textmate_grammar() -> dict:
|
|
|
473
553
|
},
|
|
474
554
|
{
|
|
475
555
|
"name": "keyword.operator.assignment.cssl",
|
|
476
|
-
"match": "
|
|
556
|
+
"match": "<==|==>|->|<-|::"
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
"name": "support.type.cssl",
|
|
560
|
+
"match": "\\b(list|dict)\\b(?!\\s*\\()"
|
|
477
561
|
}
|
|
478
562
|
]
|
|
479
563
|
}
|