IncludeCPP 3.8.0__py3-none-any.whl → 3.8.9__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/core/cssl/CSSL_DOCUMENTATION.md +411 -1
- includecpp/core/cssl/cssl_builtins.py +133 -0
- includecpp/core/cssl/cssl_parser.py +480 -8
- includecpp/core/cssl/cssl_runtime.py +616 -16
- includecpp/core/cssl/cssl_types.py +58 -9
- includecpp/vscode/cssl/package.json +1 -1
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +250 -3
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/METADATA +1 -1
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/RECORD +14 -14
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/WHEEL +0 -0
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/entry_points.txt +0 -0
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.8.0.dist-info → includecpp-3.8.9.dist-info}/top_level.txt +0 -0
|
@@ -715,17 +715,60 @@ class CSSLRuntime:
|
|
|
715
715
|
|
|
716
716
|
Parses class members and methods, creating a CSSLClass object
|
|
717
717
|
that can be instantiated with 'new'.
|
|
718
|
+
Supports inheritance via 'extends' keyword and method overwriting via 'overwrites'.
|
|
718
719
|
"""
|
|
719
720
|
class_info = node.value
|
|
720
721
|
class_name = class_info.get('name')
|
|
722
|
+
extends_class_name = class_info.get('extends')
|
|
723
|
+
extends_is_python = class_info.get('extends_is_python', False)
|
|
724
|
+
overwrites_class_name = class_info.get('overwrites')
|
|
725
|
+
overwrites_is_python = class_info.get('overwrites_is_python', False)
|
|
726
|
+
|
|
727
|
+
# Resolve parent class if extends is specified
|
|
728
|
+
parent_class = None
|
|
729
|
+
if extends_class_name:
|
|
730
|
+
if extends_is_python:
|
|
731
|
+
# extends $PythonObject - look up in shared objects
|
|
732
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
733
|
+
if extends_class_name in _live_objects:
|
|
734
|
+
parent_class = _live_objects[extends_class_name]
|
|
735
|
+
# Unwrap SharedObjectProxy if needed
|
|
736
|
+
if isinstance(parent_class, SharedObjectProxy):
|
|
737
|
+
parent_class = parent_class._obj
|
|
738
|
+
else:
|
|
739
|
+
# Also check scope with $ prefix
|
|
740
|
+
parent_class = self.global_scope.get(f'${extends_class_name}')
|
|
741
|
+
else:
|
|
742
|
+
# Try to resolve from scope (could be CSSL class or variable holding Python object)
|
|
743
|
+
parent_class = self.scope.get(extends_class_name)
|
|
744
|
+
if parent_class is None:
|
|
745
|
+
parent_class = self.global_scope.get(extends_class_name)
|
|
746
|
+
|
|
747
|
+
if parent_class is None:
|
|
748
|
+
raise ValueError(f"Cannot extend unknown class '{extends_class_name}'")
|
|
749
|
+
|
|
750
|
+
# Auto-wrap Python objects for inheritance
|
|
751
|
+
from .cssl_builtins import CSSLizedPythonObject
|
|
752
|
+
if not isinstance(parent_class, (CSSLClass, CSSLizedPythonObject)):
|
|
753
|
+
# Wrap raw Python object
|
|
754
|
+
parent_class = CSSLizedPythonObject(parent_class, self)
|
|
721
755
|
|
|
722
756
|
members = {} # Member variable defaults/types
|
|
723
757
|
methods = {} # Method AST nodes
|
|
724
|
-
|
|
758
|
+
constructors = [] # List of constructors (multiple allowed with constr keyword)
|
|
759
|
+
constructor = None # Primary constructor (backward compatibility)
|
|
760
|
+
|
|
761
|
+
# Get class parameters and extends args
|
|
762
|
+
class_params = class_info.get('class_params', [])
|
|
763
|
+
extends_args = class_info.get('extends_args', [])
|
|
725
764
|
|
|
726
765
|
for child in node.children:
|
|
727
|
-
if child.type == '
|
|
728
|
-
#
|
|
766
|
+
if child.type == 'constructor':
|
|
767
|
+
# New-style constructor from 'constr' keyword
|
|
768
|
+
constructors.append(child)
|
|
769
|
+
|
|
770
|
+
elif child.type == 'function':
|
|
771
|
+
# This is a method or old-style constructor
|
|
729
772
|
func_info = child.value
|
|
730
773
|
method_name = func_info.get('name')
|
|
731
774
|
|
|
@@ -752,32 +795,170 @@ class CSSLRuntime:
|
|
|
752
795
|
name=class_name,
|
|
753
796
|
members=members,
|
|
754
797
|
methods=methods,
|
|
755
|
-
constructor=constructor
|
|
798
|
+
constructor=constructor,
|
|
799
|
+
parent=parent_class
|
|
756
800
|
)
|
|
801
|
+
# Store additional constructor info
|
|
802
|
+
class_def.constructors = constructors # Multiple constructors from 'constr' keyword
|
|
803
|
+
class_def.class_params = class_params # Class-level constructor parameters
|
|
804
|
+
class_def.extends_args = extends_args # Arguments to pass to parent constructor
|
|
757
805
|
|
|
758
806
|
# Register class in scope
|
|
759
807
|
self.scope.set(class_name, class_def)
|
|
760
808
|
self.global_scope.set(class_name, class_def)
|
|
761
809
|
|
|
810
|
+
# Handle class overwrites - replace methods in target class
|
|
811
|
+
if overwrites_class_name:
|
|
812
|
+
self._apply_class_overwrites(
|
|
813
|
+
class_def, overwrites_class_name, overwrites_is_python
|
|
814
|
+
)
|
|
815
|
+
|
|
762
816
|
return class_def
|
|
763
817
|
|
|
818
|
+
def _apply_class_overwrites(self, new_class: CSSLClass, target_name: str, is_python: bool):
|
|
819
|
+
"""Apply method overwrites from new_class to target class/object.
|
|
820
|
+
|
|
821
|
+
When a class has 'overwrites' specified, all methods defined in new_class
|
|
822
|
+
will replace the corresponding methods in the target.
|
|
823
|
+
"""
|
|
824
|
+
from .cssl_builtins import CSSLizedPythonObject
|
|
825
|
+
|
|
826
|
+
# Resolve target
|
|
827
|
+
target = None
|
|
828
|
+
if is_python:
|
|
829
|
+
from ..cssl_bridge import _live_objects
|
|
830
|
+
if target_name in _live_objects:
|
|
831
|
+
target = _live_objects[target_name]
|
|
832
|
+
else:
|
|
833
|
+
target = self.scope.get(target_name)
|
|
834
|
+
if target is None:
|
|
835
|
+
target = self.global_scope.get(target_name)
|
|
836
|
+
|
|
837
|
+
if target is None:
|
|
838
|
+
return # Target not found, silently skip
|
|
839
|
+
|
|
840
|
+
# Get methods to overwrite
|
|
841
|
+
methods_to_overwrite = new_class.methods
|
|
842
|
+
|
|
843
|
+
if is_python and hasattr(target, '__class__'):
|
|
844
|
+
# Python object - overwrite methods on the object/class
|
|
845
|
+
for method_name, method_node in methods_to_overwrite.items():
|
|
846
|
+
# Create a Python-callable wrapper for the CSSL method
|
|
847
|
+
wrapper = self._create_method_wrapper(method_node, target)
|
|
848
|
+
# Set on the object
|
|
849
|
+
try:
|
|
850
|
+
setattr(target, method_name, wrapper)
|
|
851
|
+
except AttributeError:
|
|
852
|
+
# Try setting on class instead
|
|
853
|
+
try:
|
|
854
|
+
setattr(target.__class__, method_name, wrapper)
|
|
855
|
+
except:
|
|
856
|
+
pass
|
|
857
|
+
elif isinstance(target, CSSLClass):
|
|
858
|
+
# CSSL class - directly replace methods
|
|
859
|
+
for method_name, method_node in methods_to_overwrite.items():
|
|
860
|
+
target.methods[method_name] = method_node
|
|
861
|
+
elif isinstance(target, CSSLizedPythonObject):
|
|
862
|
+
# CSSLized Python object - get underlying object and overwrite
|
|
863
|
+
py_obj = target.get_python_obj()
|
|
864
|
+
for method_name, method_node in methods_to_overwrite.items():
|
|
865
|
+
wrapper = self._create_method_wrapper(method_node, py_obj)
|
|
866
|
+
try:
|
|
867
|
+
setattr(py_obj, method_name, wrapper)
|
|
868
|
+
except AttributeError:
|
|
869
|
+
try:
|
|
870
|
+
setattr(py_obj.__class__, method_name, wrapper)
|
|
871
|
+
except:
|
|
872
|
+
pass
|
|
873
|
+
|
|
874
|
+
def _create_method_wrapper(self, method_node: ASTNode, instance: Any):
|
|
875
|
+
"""Create a Python-callable wrapper for a CSSL method that works with an instance."""
|
|
876
|
+
def wrapper(*args, **kwargs):
|
|
877
|
+
# Set up instance context for this->
|
|
878
|
+
old_instance = self._current_instance
|
|
879
|
+
# Create a fake CSSLInstance-like wrapper if needed
|
|
880
|
+
self._current_instance = instance
|
|
881
|
+
try:
|
|
882
|
+
return self._call_function(method_node, list(args), kwargs)
|
|
883
|
+
finally:
|
|
884
|
+
self._current_instance = old_instance
|
|
885
|
+
return wrapper
|
|
886
|
+
|
|
764
887
|
def _exec_function(self, node: ASTNode) -> Any:
|
|
765
|
-
"""Execute function definition -
|
|
888
|
+
"""Execute function definition - registers it and handles extends/overwrites.
|
|
889
|
+
|
|
890
|
+
Syntax:
|
|
891
|
+
define func() { ... }
|
|
892
|
+
define func : extends otherFunc() { ... } - Inherit local vars
|
|
893
|
+
define func : overwrites otherFunc() { ... } - Replace otherFunc
|
|
894
|
+
"""
|
|
766
895
|
func_info = node.value
|
|
767
896
|
func_name = func_info.get('name')
|
|
897
|
+
extends_func = func_info.get('extends')
|
|
898
|
+
extends_is_python = func_info.get('extends_is_python', False)
|
|
899
|
+
overwrites_func = func_info.get('overwrites')
|
|
900
|
+
overwrites_is_python = func_info.get('overwrites_is_python', False)
|
|
901
|
+
|
|
902
|
+
# Store function extends info for runtime use
|
|
903
|
+
if extends_func:
|
|
904
|
+
node.value['_extends_resolved'] = self._resolve_function_target(
|
|
905
|
+
extends_func, extends_is_python
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
# Handle overwrites - replace the target function
|
|
909
|
+
if overwrites_func:
|
|
910
|
+
target = self._resolve_function_target(overwrites_func, overwrites_is_python)
|
|
911
|
+
if target is not None:
|
|
912
|
+
# Store original for reference
|
|
913
|
+
node.value['_overwrites_original'] = target
|
|
914
|
+
# Replace the target function with this one
|
|
915
|
+
if overwrites_is_python:
|
|
916
|
+
from ..cssl_bridge import _live_objects
|
|
917
|
+
if overwrites_func in _live_objects:
|
|
918
|
+
# Create a wrapper that calls the CSSL function
|
|
919
|
+
_live_objects[overwrites_func] = self._create_python_wrapper(node)
|
|
920
|
+
else:
|
|
921
|
+
# Replace in CSSL scope
|
|
922
|
+
self.scope.set(overwrites_func, node)
|
|
923
|
+
self.global_scope.set(overwrites_func, node)
|
|
924
|
+
|
|
925
|
+
# Register the function
|
|
768
926
|
self.scope.set(func_name, node)
|
|
769
927
|
return None
|
|
770
928
|
|
|
929
|
+
def _resolve_function_target(self, name: str, is_python: bool) -> Any:
|
|
930
|
+
"""Resolve a function target for extends/overwrites."""
|
|
931
|
+
if is_python:
|
|
932
|
+
from ..cssl_bridge import _live_objects
|
|
933
|
+
if name in _live_objects:
|
|
934
|
+
return _live_objects[name]
|
|
935
|
+
return self.global_scope.get(f'${name}')
|
|
936
|
+
else:
|
|
937
|
+
target = self.scope.get(name)
|
|
938
|
+
if target is None:
|
|
939
|
+
target = self.global_scope.get(name)
|
|
940
|
+
return target
|
|
941
|
+
|
|
942
|
+
def _create_python_wrapper(self, func_node: ASTNode):
|
|
943
|
+
"""Create a Python-callable wrapper for a CSSL function."""
|
|
944
|
+
def wrapper(*args, **kwargs):
|
|
945
|
+
return self._call_function(func_node, list(args), kwargs)
|
|
946
|
+
return wrapper
|
|
947
|
+
|
|
771
948
|
def _exec_typed_declaration(self, node: ASTNode) -> Any:
|
|
772
949
|
"""Execute typed variable declaration: type<T> varName = value;
|
|
773
950
|
|
|
774
951
|
Creates appropriate type instances for stack, vector, datastruct, etc.
|
|
952
|
+
|
|
953
|
+
The * prefix indicates a non-nullable variable (can never be None/null).
|
|
954
|
+
Example: vector<dynamic> *MyVector - can never contain None values.
|
|
775
955
|
"""
|
|
776
956
|
decl = node.value
|
|
777
957
|
type_name = decl.get('type')
|
|
778
958
|
element_type = decl.get('element_type', 'dynamic')
|
|
779
959
|
var_name = decl.get('name')
|
|
780
960
|
value_node = decl.get('value')
|
|
961
|
+
non_null = decl.get('non_null', False)
|
|
781
962
|
|
|
782
963
|
# Create the appropriate type instance
|
|
783
964
|
if type_name == 'stack':
|
|
@@ -825,11 +1006,32 @@ class CSSLRuntime:
|
|
|
825
1006
|
# For container types, the value might be initialization data
|
|
826
1007
|
init_value = self._evaluate(value_node)
|
|
827
1008
|
if isinstance(init_value, (list, tuple)):
|
|
1009
|
+
# For non-null containers, filter out None values
|
|
1010
|
+
if non_null:
|
|
1011
|
+
init_value = [v for v in init_value if v is not None]
|
|
828
1012
|
instance.extend(init_value)
|
|
829
1013
|
elif init_value is not None:
|
|
830
1014
|
if hasattr(instance, 'append'):
|
|
831
1015
|
instance.append(init_value)
|
|
832
1016
|
|
|
1017
|
+
# Non-null enforcement: container types get wrapped to filter None on operations
|
|
1018
|
+
if non_null:
|
|
1019
|
+
# Mark the instance as non-null for runtime checks
|
|
1020
|
+
if hasattr(instance, '_non_null'):
|
|
1021
|
+
instance._non_null = True
|
|
1022
|
+
# Track non-null variables for assignment enforcement
|
|
1023
|
+
if not hasattr(self, '_non_null_vars'):
|
|
1024
|
+
self._non_null_vars = set()
|
|
1025
|
+
self._non_null_vars.add(var_name)
|
|
1026
|
+
|
|
1027
|
+
# Ensure initial value is not None for non-null variables
|
|
1028
|
+
if instance is None:
|
|
1029
|
+
raise CSSLRuntimeError(
|
|
1030
|
+
f"Non-null variable '*{var_name}' cannot be initialized to None",
|
|
1031
|
+
node.line,
|
|
1032
|
+
hint="Use a default value or remove the * prefix"
|
|
1033
|
+
)
|
|
1034
|
+
|
|
833
1035
|
# Check for global modifier
|
|
834
1036
|
modifiers = decl.get('modifiers', [])
|
|
835
1037
|
is_global = 'global' in modifiers
|
|
@@ -1006,6 +1208,9 @@ class CSSLRuntime:
|
|
|
1006
1208
|
func_node: The function AST node
|
|
1007
1209
|
args: List of positional arguments
|
|
1008
1210
|
kwargs: Dict of named arguments (param_name -> value)
|
|
1211
|
+
|
|
1212
|
+
Supports:
|
|
1213
|
+
define func : extends otherFunc() { ... } - Inherit local vars from otherFunc
|
|
1009
1214
|
"""
|
|
1010
1215
|
func_info = func_node.value
|
|
1011
1216
|
params = func_info.get('params', [])
|
|
@@ -1018,6 +1223,34 @@ class CSSLRuntime:
|
|
|
1018
1223
|
# Create new scope
|
|
1019
1224
|
new_scope = Scope(parent=self.scope)
|
|
1020
1225
|
|
|
1226
|
+
# Handle function extends - inherit local vars from extended function
|
|
1227
|
+
extends_resolved = func_info.get('_extends_resolved')
|
|
1228
|
+
if extends_resolved:
|
|
1229
|
+
if callable(extends_resolved):
|
|
1230
|
+
# Python function - call it first to populate any state
|
|
1231
|
+
try:
|
|
1232
|
+
extends_resolved(*args, **kwargs)
|
|
1233
|
+
except:
|
|
1234
|
+
pass
|
|
1235
|
+
elif hasattr(extends_resolved, 'value'):
|
|
1236
|
+
# CSSL function - execute it in a temporary scope to get local vars
|
|
1237
|
+
old_scope = self.scope
|
|
1238
|
+
temp_scope = Scope(parent=self.scope)
|
|
1239
|
+
self.scope = temp_scope
|
|
1240
|
+
try:
|
|
1241
|
+
# Execute extended function body to populate local vars
|
|
1242
|
+
for child in extends_resolved.children:
|
|
1243
|
+
if not self._running:
|
|
1244
|
+
break
|
|
1245
|
+
self._execute_node(child)
|
|
1246
|
+
# Copy all local vars to new scope
|
|
1247
|
+
for name, value in temp_scope._vars.items():
|
|
1248
|
+
new_scope.set(name, value)
|
|
1249
|
+
except:
|
|
1250
|
+
pass
|
|
1251
|
+
finally:
|
|
1252
|
+
self.scope = old_scope
|
|
1253
|
+
|
|
1021
1254
|
# Bind parameters - handle both positional and named arguments
|
|
1022
1255
|
for i, param in enumerate(params):
|
|
1023
1256
|
# Extract param name and type from dict format: {'name': 'a', 'type': 'int'}
|
|
@@ -1029,10 +1262,20 @@ class CSSLRuntime:
|
|
|
1029
1262
|
param_type = ''
|
|
1030
1263
|
|
|
1031
1264
|
# Check if this is an 'open' parameter - receives all args as a list
|
|
1032
|
-
|
|
1265
|
+
# The parser sets param['open'] = True for 'open' keyword
|
|
1266
|
+
is_open_param = (isinstance(param, dict) and param.get('open', False)) or param_name == 'Params'
|
|
1267
|
+
if is_open_param:
|
|
1033
1268
|
# 'open Params' receives all arguments as a list
|
|
1034
|
-
|
|
1035
|
-
|
|
1269
|
+
# Check for non_null flag: open *Params filters out None values
|
|
1270
|
+
is_non_null = isinstance(param, dict) and param.get('non_null', False)
|
|
1271
|
+
args_list = list(args)
|
|
1272
|
+
if is_non_null:
|
|
1273
|
+
args_list = [a for a in args_list if a is not None]
|
|
1274
|
+
# Also filter kwargs
|
|
1275
|
+
kwargs = {k: v for k, v in kwargs.items() if v is not None}
|
|
1276
|
+
new_scope.set(param_name, args_list)
|
|
1277
|
+
new_scope.set('Params', args_list) # Also set 'Params' for OpenFind
|
|
1278
|
+
new_scope.set('_OpenKwargs', kwargs) # Store kwargs for OpenFind<type, "name">
|
|
1036
1279
|
elif param_name in kwargs:
|
|
1037
1280
|
# Named argument takes priority
|
|
1038
1281
|
new_scope.set(param_name, kwargs[param_name])
|
|
@@ -1047,6 +1290,17 @@ class CSSLRuntime:
|
|
|
1047
1290
|
self.scope = new_scope
|
|
1048
1291
|
|
|
1049
1292
|
try:
|
|
1293
|
+
# Handle append mode (++) - execute referenced function first
|
|
1294
|
+
append_mode = func_info.get('append_mode', False)
|
|
1295
|
+
append_ref_class = func_info.get('append_ref_class')
|
|
1296
|
+
append_ref_member = func_info.get('append_ref_member')
|
|
1297
|
+
|
|
1298
|
+
if append_mode and append_ref_class:
|
|
1299
|
+
self._execute_append_reference(
|
|
1300
|
+
None, append_ref_class, append_ref_member,
|
|
1301
|
+
args, kwargs, {}, is_constructor=False
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1050
1304
|
for child in func_node.children:
|
|
1051
1305
|
# Check if exit() was called
|
|
1052
1306
|
if not self._running:
|
|
@@ -1267,6 +1521,82 @@ class CSSLRuntime:
|
|
|
1267
1521
|
"""Execute continue statement"""
|
|
1268
1522
|
raise CSSLContinue()
|
|
1269
1523
|
|
|
1524
|
+
def _exec_constructor(self, node: ASTNode) -> Any:
|
|
1525
|
+
"""Execute constructor node - only called when encountered directly.
|
|
1526
|
+
|
|
1527
|
+
Normally constructors are executed through _call_constructor in _eval_new.
|
|
1528
|
+
This handles cases where a constructor node is executed in other contexts.
|
|
1529
|
+
"""
|
|
1530
|
+
# Constructor nodes should be handled during class instantiation
|
|
1531
|
+
# If we reach here, it's in a context where the constructor is stored but not executed
|
|
1532
|
+
return None
|
|
1533
|
+
|
|
1534
|
+
def _exec_super_call(self, node: ASTNode) -> Any:
|
|
1535
|
+
"""Execute super() call to invoke parent constructor or method.
|
|
1536
|
+
|
|
1537
|
+
Syntax:
|
|
1538
|
+
super() - Call parent constructor with no args
|
|
1539
|
+
super(arg1, arg2) - Call parent constructor with args
|
|
1540
|
+
super::method() - Call specific parent method
|
|
1541
|
+
super::method(args) - Call specific parent method with args
|
|
1542
|
+
"""
|
|
1543
|
+
if self._current_instance is None:
|
|
1544
|
+
raise CSSLRuntimeError(
|
|
1545
|
+
"super() called outside of class context",
|
|
1546
|
+
node.line if hasattr(node, 'line') else 0,
|
|
1547
|
+
hint="super() can only be used inside class constructors and methods"
|
|
1548
|
+
)
|
|
1549
|
+
|
|
1550
|
+
instance = self._current_instance
|
|
1551
|
+
|
|
1552
|
+
# Try to get parent from instance first, then from class definition
|
|
1553
|
+
parent = getattr(instance, '_parent_class', None)
|
|
1554
|
+
if parent is None and hasattr(instance, '_class') and instance._class:
|
|
1555
|
+
parent = getattr(instance._class, 'parent', None)
|
|
1556
|
+
|
|
1557
|
+
if parent is None:
|
|
1558
|
+
raise CSSLRuntimeError(
|
|
1559
|
+
"super() called but class has no parent",
|
|
1560
|
+
node.line if hasattr(node, 'line') else 0,
|
|
1561
|
+
hint="super() requires the class to extend another class"
|
|
1562
|
+
)
|
|
1563
|
+
|
|
1564
|
+
method_name = node.value.get('method')
|
|
1565
|
+
args = [self._evaluate(arg) for arg in node.value.get('args', [])]
|
|
1566
|
+
|
|
1567
|
+
from .cssl_builtins import CSSLizedPythonObject
|
|
1568
|
+
|
|
1569
|
+
if method_name:
|
|
1570
|
+
# super::method() - call specific parent method
|
|
1571
|
+
if isinstance(parent, CSSLClass):
|
|
1572
|
+
method = parent.methods.get(method_name)
|
|
1573
|
+
if method:
|
|
1574
|
+
return self._call_method(instance, method, args, {})
|
|
1575
|
+
else:
|
|
1576
|
+
raise CSSLRuntimeError(
|
|
1577
|
+
f"Parent class has no method '{method_name}'",
|
|
1578
|
+
node.line if hasattr(node, 'line') else 0
|
|
1579
|
+
)
|
|
1580
|
+
elif isinstance(parent, CSSLizedPythonObject):
|
|
1581
|
+
py_obj = parent.get_python_obj()
|
|
1582
|
+
if hasattr(py_obj, method_name):
|
|
1583
|
+
method = getattr(py_obj, method_name)
|
|
1584
|
+
return method(*args)
|
|
1585
|
+
else:
|
|
1586
|
+
raise CSSLRuntimeError(
|
|
1587
|
+
f"Parent Python object has no method '{method_name}'",
|
|
1588
|
+
node.line if hasattr(node, 'line') else 0
|
|
1589
|
+
)
|
|
1590
|
+
elif hasattr(parent, method_name):
|
|
1591
|
+
method = getattr(parent, method_name)
|
|
1592
|
+
return method(*args)
|
|
1593
|
+
else:
|
|
1594
|
+
# super() - call parent constructor
|
|
1595
|
+
self._call_parent_constructor(instance, args)
|
|
1596
|
+
instance._parent_constructor_called = True
|
|
1597
|
+
|
|
1598
|
+
return None
|
|
1599
|
+
|
|
1270
1600
|
def _exec_try(self, node: ASTNode) -> Any:
|
|
1271
1601
|
"""Execute try/catch block"""
|
|
1272
1602
|
try:
|
|
@@ -2308,6 +2638,9 @@ class CSSLRuntime:
|
|
|
2308
2638
|
|
|
2309
2639
|
if node.type == 'literal':
|
|
2310
2640
|
value = node.value
|
|
2641
|
+
# Handle dict-format literals from parser: {'type': 'int', 'value': 0}
|
|
2642
|
+
if isinstance(value, dict) and 'value' in value:
|
|
2643
|
+
value = value['value']
|
|
2311
2644
|
# String interpolation - replace {var} or <var> with scope values
|
|
2312
2645
|
if isinstance(value, str):
|
|
2313
2646
|
has_fstring = '{' in value and '}' in value
|
|
@@ -2783,19 +3116,20 @@ class CSSLRuntime:
|
|
|
2783
3116
|
)
|
|
2784
3117
|
|
|
2785
3118
|
def _eval_typed_call(self, node: ASTNode) -> Any:
|
|
2786
|
-
"""Evaluate typed function call like OpenFind<string>(0)"""
|
|
3119
|
+
"""Evaluate typed function call like OpenFind<string>(0) or OpenFind<dynamic, "name">"""
|
|
2787
3120
|
name = node.value.get('name')
|
|
2788
3121
|
type_param = node.value.get('type_param', 'dynamic')
|
|
3122
|
+
param_name = node.value.get('param_name') # For OpenFind<type, "name">
|
|
2789
3123
|
args = [self._evaluate(a) for a in node.value.get('args', [])]
|
|
2790
3124
|
|
|
2791
|
-
# Handle OpenFind<type>(index)
|
|
3125
|
+
# Handle OpenFind<type>(index) or OpenFind<type, "name">
|
|
2792
3126
|
if name == 'OpenFind':
|
|
2793
3127
|
# OpenFind searches for a value of the specified type
|
|
2794
3128
|
# from the open parameters in scope
|
|
2795
3129
|
open_params = self.scope.get('Params') or []
|
|
2796
|
-
|
|
3130
|
+
open_kwargs = self.scope.get('_OpenKwargs') or {}
|
|
2797
3131
|
|
|
2798
|
-
#
|
|
3132
|
+
# Type mapping for type checking
|
|
2799
3133
|
type_map = {
|
|
2800
3134
|
'string': str, 'str': str,
|
|
2801
3135
|
'int': int, 'integer': int,
|
|
@@ -2803,10 +3137,23 @@ class CSSLRuntime:
|
|
|
2803
3137
|
'bool': bool, 'boolean': bool,
|
|
2804
3138
|
'list': list, 'array': list,
|
|
2805
3139
|
'dict': dict, 'json': dict,
|
|
3140
|
+
'dynamic': None, # Accept any type
|
|
2806
3141
|
}
|
|
2807
|
-
|
|
2808
3142
|
target_type = type_map.get(type_param.lower())
|
|
2809
3143
|
|
|
3144
|
+
# If param_name is specified, search by name in kwargs
|
|
3145
|
+
# OpenFind<dynamic, "tasks"> -> searches for MyFunc(tasks="value")
|
|
3146
|
+
if param_name:
|
|
3147
|
+
if param_name in open_kwargs:
|
|
3148
|
+
value = open_kwargs[param_name]
|
|
3149
|
+
# Type check if not dynamic
|
|
3150
|
+
if target_type is None or isinstance(value, target_type):
|
|
3151
|
+
return value
|
|
3152
|
+
return None
|
|
3153
|
+
|
|
3154
|
+
# Otherwise, search by index in positional args
|
|
3155
|
+
index = args[0] if args else 0
|
|
3156
|
+
|
|
2810
3157
|
if isinstance(open_params, (list, tuple)):
|
|
2811
3158
|
# Find first matching type starting from index
|
|
2812
3159
|
for i in range(index, len(open_params)):
|
|
@@ -2827,14 +3174,16 @@ class CSSLRuntime:
|
|
|
2827
3174
|
raise CSSLRuntimeError(
|
|
2828
3175
|
f"Unknown typed function: {name}<{type_param}>",
|
|
2829
3176
|
node.line,
|
|
2830
|
-
context=f"Available typed functions: OpenFind<type>",
|
|
2831
|
-
hint="
|
|
3177
|
+
context=f"Available typed functions: OpenFind<type>, OpenFind<type, \"name\">",
|
|
3178
|
+
hint="Use OpenFind<type>(index) for positional or OpenFind<type, \"name\"> for named params"
|
|
2832
3179
|
)
|
|
2833
3180
|
|
|
2834
3181
|
def _eval_new(self, node: ASTNode) -> CSSLInstance:
|
|
2835
3182
|
"""Evaluate 'new ClassName(args)' expression.
|
|
2836
3183
|
|
|
2837
3184
|
Creates a new instance of a CSSL class and calls its constructor.
|
|
3185
|
+
Supports multiple constructors (constr keyword), class parameters,
|
|
3186
|
+
and automatic parent constructor calling.
|
|
2838
3187
|
"""
|
|
2839
3188
|
class_name = node.value.get('class')
|
|
2840
3189
|
args = [self._evaluate(arg) for arg in node.value.get('args', [])]
|
|
@@ -2862,12 +3211,242 @@ class CSSLRuntime:
|
|
|
2862
3211
|
# Create new instance
|
|
2863
3212
|
instance = CSSLInstance(class_def)
|
|
2864
3213
|
|
|
2865
|
-
#
|
|
3214
|
+
# Store parent class reference for super() calls
|
|
3215
|
+
instance._parent_class = class_def.parent
|
|
3216
|
+
instance._parent_constructor_called = False
|
|
3217
|
+
|
|
3218
|
+
# Get class params and extends args
|
|
3219
|
+
class_params = getattr(class_def, 'class_params', [])
|
|
3220
|
+
extends_args = getattr(class_def, 'extends_args', [])
|
|
3221
|
+
constructors = getattr(class_def, 'constructors', [])
|
|
3222
|
+
|
|
3223
|
+
# Bind class_params to instance scope (they receive values from constructor args)
|
|
3224
|
+
# These are the implicit constructor parameters defined in class declaration
|
|
3225
|
+
param_values = {}
|
|
3226
|
+
for i, param in enumerate(class_params):
|
|
3227
|
+
param_name = param.get('name') if isinstance(param, dict) else param
|
|
3228
|
+
if i < len(args):
|
|
3229
|
+
param_values[param_name] = args[i]
|
|
3230
|
+
else:
|
|
3231
|
+
param_values[param_name] = None
|
|
3232
|
+
|
|
3233
|
+
# Call parent constructor with extends_args if parent exists and args specified
|
|
3234
|
+
if class_def.parent and extends_args:
|
|
3235
|
+
evaluated_extends_args = [self._evaluate(arg) for arg in extends_args]
|
|
3236
|
+
self._call_parent_constructor(instance, evaluated_extends_args)
|
|
3237
|
+
instance._parent_constructor_called = True
|
|
3238
|
+
|
|
3239
|
+
# Execute all constructors defined with 'constr' keyword (in order)
|
|
3240
|
+
for constr in constructors:
|
|
3241
|
+
self._call_constructor(instance, constr, args, kwargs, param_values)
|
|
3242
|
+
|
|
3243
|
+
# Call primary constructor (old-style) if defined
|
|
2866
3244
|
if class_def.constructor:
|
|
2867
3245
|
self._call_method(instance, class_def.constructor, args, kwargs)
|
|
2868
3246
|
|
|
2869
3247
|
return instance
|
|
2870
3248
|
|
|
3249
|
+
def _call_parent_constructor(self, instance: CSSLInstance, args: list, kwargs: dict = None):
|
|
3250
|
+
"""Call the parent class constructor on an instance.
|
|
3251
|
+
|
|
3252
|
+
Used for automatic parent constructor calling and super() calls.
|
|
3253
|
+
"""
|
|
3254
|
+
kwargs = kwargs or {}
|
|
3255
|
+
parent = instance._parent_class
|
|
3256
|
+
|
|
3257
|
+
if parent is None:
|
|
3258
|
+
return
|
|
3259
|
+
|
|
3260
|
+
from .cssl_builtins import CSSLizedPythonObject
|
|
3261
|
+
|
|
3262
|
+
if isinstance(parent, CSSLClass):
|
|
3263
|
+
# CSSL parent class
|
|
3264
|
+
if parent.constructor:
|
|
3265
|
+
self._call_method(instance, parent.constructor, args, kwargs)
|
|
3266
|
+
# Also call parent's constr constructors
|
|
3267
|
+
for constr in getattr(parent, 'constructors', []):
|
|
3268
|
+
self._call_constructor(instance, constr, args, kwargs, {})
|
|
3269
|
+
elif isinstance(parent, CSSLizedPythonObject):
|
|
3270
|
+
# Python parent - call __init__ if it's a class
|
|
3271
|
+
py_obj = parent.get_python_obj()
|
|
3272
|
+
if isinstance(py_obj, type):
|
|
3273
|
+
# It's a class - we need to initialize it
|
|
3274
|
+
try:
|
|
3275
|
+
py_obj.__init__(instance, *args, **kwargs)
|
|
3276
|
+
except TypeError:
|
|
3277
|
+
pass # Initialization might not be needed
|
|
3278
|
+
elif isinstance(parent, type):
|
|
3279
|
+
# Raw Python class
|
|
3280
|
+
try:
|
|
3281
|
+
parent.__init__(instance, *args, **kwargs)
|
|
3282
|
+
except TypeError:
|
|
3283
|
+
pass
|
|
3284
|
+
|
|
3285
|
+
def _execute_append_reference(self, instance: CSSLInstance, ref_class: str, ref_member: str,
|
|
3286
|
+
args: list, kwargs: dict, param_values: dict, is_constructor: bool = True):
|
|
3287
|
+
"""Execute referenced parent constructor/method for append mode (++).
|
|
3288
|
+
|
|
3289
|
+
Resolves the class/instance reference and executes the specified member.
|
|
3290
|
+
Supports:
|
|
3291
|
+
- Static class references: &ClassName::member
|
|
3292
|
+
- Dynamic instance references: &$instanceVar::member
|
|
3293
|
+
- Direct function references: &FunctionName (for define functions)
|
|
3294
|
+
|
|
3295
|
+
Args:
|
|
3296
|
+
instance: Current class instance (can be None for standalone functions)
|
|
3297
|
+
ref_class: Referenced class name, $instanceVar, or function name
|
|
3298
|
+
ref_member: Member name (constructor name, 'constructors', or method name) - can be None
|
|
3299
|
+
args: Arguments to pass
|
|
3300
|
+
kwargs: Keyword arguments to pass
|
|
3301
|
+
param_values: Parameter values from class params
|
|
3302
|
+
is_constructor: True if looking for constructor, False for method
|
|
3303
|
+
"""
|
|
3304
|
+
from .cssl_builtins import CSSLizedPythonObject
|
|
3305
|
+
|
|
3306
|
+
# Handle direct function reference: &FunctionName ++ (no ::member part)
|
|
3307
|
+
if ref_member is None and not is_constructor:
|
|
3308
|
+
# ref_class is actually a function name
|
|
3309
|
+
func_name = ref_class
|
|
3310
|
+
if func_name.startswith('$'):
|
|
3311
|
+
func_name = func_name[1:]
|
|
3312
|
+
|
|
3313
|
+
# Look for the function in scope
|
|
3314
|
+
func = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
3315
|
+
if func is not None:
|
|
3316
|
+
if isinstance(func, ASTNode) and func.type in ('function', 'FUNCTION'):
|
|
3317
|
+
# Execute the referenced function
|
|
3318
|
+
if instance:
|
|
3319
|
+
self._call_method(instance, func, args, kwargs)
|
|
3320
|
+
else:
|
|
3321
|
+
self._call_function(func, args, kwargs)
|
|
3322
|
+
elif callable(func):
|
|
3323
|
+
# It's a callable (Python function or lambda)
|
|
3324
|
+
func(*args, **kwargs)
|
|
3325
|
+
return
|
|
3326
|
+
|
|
3327
|
+
# Resolve the class/instance reference
|
|
3328
|
+
if ref_class.startswith('$'):
|
|
3329
|
+
# Dynamic instance reference: &$instanceVar::member
|
|
3330
|
+
var_name = ref_class[1:]
|
|
3331
|
+
ref_obj = self.scope.get(var_name) or self.global_scope.get(var_name)
|
|
3332
|
+
if ref_obj is None:
|
|
3333
|
+
return # Instance not found, skip silently
|
|
3334
|
+
|
|
3335
|
+
if isinstance(ref_obj, CSSLInstance):
|
|
3336
|
+
# Get the class definition from the instance
|
|
3337
|
+
target_class = ref_obj.class_def
|
|
3338
|
+
elif isinstance(ref_obj, CSSLClass):
|
|
3339
|
+
target_class = ref_obj
|
|
3340
|
+
else:
|
|
3341
|
+
return # Not a valid reference
|
|
3342
|
+
else:
|
|
3343
|
+
# Static class reference: &ClassName::member
|
|
3344
|
+
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
3345
|
+
|
|
3346
|
+
if target_class is None:
|
|
3347
|
+
return # Class not found, skip silently
|
|
3348
|
+
|
|
3349
|
+
# Handle different target types
|
|
3350
|
+
if isinstance(target_class, CSSLInstance):
|
|
3351
|
+
# Referenced an instance variable directly
|
|
3352
|
+
target_class = target_class.class_def
|
|
3353
|
+
|
|
3354
|
+
if not isinstance(target_class, CSSLClass):
|
|
3355
|
+
return # Not a CSSL class
|
|
3356
|
+
|
|
3357
|
+
# Find and execute the referenced member
|
|
3358
|
+
if is_constructor:
|
|
3359
|
+
# Looking for a constructor
|
|
3360
|
+
if ref_member == 'constructors' or ref_member is None:
|
|
3361
|
+
# Execute all constructors from the referenced class
|
|
3362
|
+
for constr in getattr(target_class, 'constructors', []):
|
|
3363
|
+
self._call_constructor(instance, constr, args, kwargs, param_values)
|
|
3364
|
+
else:
|
|
3365
|
+
# Execute specific constructor by name
|
|
3366
|
+
for constr in getattr(target_class, 'constructors', []):
|
|
3367
|
+
if constr.value.get('name') == ref_member:
|
|
3368
|
+
self._call_constructor(instance, constr, args, kwargs, param_values)
|
|
3369
|
+
break
|
|
3370
|
+
else:
|
|
3371
|
+
# Looking for a method (define function)
|
|
3372
|
+
if ref_member:
|
|
3373
|
+
# Find method in class
|
|
3374
|
+
for member in getattr(target_class, 'members', []):
|
|
3375
|
+
if member.type in ('function', 'FUNCTION') and member.value.get('name') == ref_member:
|
|
3376
|
+
self._call_method(instance, member, args, kwargs)
|
|
3377
|
+
break
|
|
3378
|
+
# Also check in methods dict if available
|
|
3379
|
+
methods = getattr(target_class, 'methods', {})
|
|
3380
|
+
if ref_member in methods:
|
|
3381
|
+
method_node = methods[ref_member]
|
|
3382
|
+
self._call_method(instance, method_node, args, kwargs)
|
|
3383
|
+
|
|
3384
|
+
def _call_constructor(self, instance: CSSLInstance, constr_node: ASTNode,
|
|
3385
|
+
args: list, kwargs: dict, param_values: dict):
|
|
3386
|
+
"""Call a constructor defined with 'constr' keyword.
|
|
3387
|
+
|
|
3388
|
+
Handles constructor extends/overwrites and sets up the instance scope.
|
|
3389
|
+
Supports append mode (++) for keeping parent code and adding new code.
|
|
3390
|
+
"""
|
|
3391
|
+
constr_info = constr_node.value
|
|
3392
|
+
constr_params = constr_info.get('params', [])
|
|
3393
|
+
extends_class = constr_info.get('extends_class')
|
|
3394
|
+
extends_method = constr_info.get('extends_method')
|
|
3395
|
+
|
|
3396
|
+
# Append mode: ++ operator for keeping parent code + adding new
|
|
3397
|
+
append_mode = constr_info.get('append_mode', False)
|
|
3398
|
+
append_ref_class = constr_info.get('append_ref_class') # &ClassName or &$instanceVar
|
|
3399
|
+
append_ref_member = constr_info.get('append_ref_member') # ::constructorName or ::methodName
|
|
3400
|
+
|
|
3401
|
+
# Save previous instance context
|
|
3402
|
+
prev_instance = self._current_instance
|
|
3403
|
+
self._current_instance = instance
|
|
3404
|
+
|
|
3405
|
+
# Create new scope for constructor
|
|
3406
|
+
new_scope = Scope(parent=self.scope)
|
|
3407
|
+
|
|
3408
|
+
# Bind param_values (from class params) to constructor scope
|
|
3409
|
+
for name, value in param_values.items():
|
|
3410
|
+
new_scope.set(name, value)
|
|
3411
|
+
|
|
3412
|
+
# Bind constructor parameters
|
|
3413
|
+
for i, param in enumerate(constr_params):
|
|
3414
|
+
param_name = param.get('name') if isinstance(param, dict) else param
|
|
3415
|
+
if i < len(args):
|
|
3416
|
+
new_scope.set(param_name, args[i])
|
|
3417
|
+
elif param_name in kwargs:
|
|
3418
|
+
new_scope.set(param_name, kwargs[param_name])
|
|
3419
|
+
else:
|
|
3420
|
+
new_scope.set(param_name, None)
|
|
3421
|
+
|
|
3422
|
+
# If constructor extends another constructor, inherit its local vars
|
|
3423
|
+
if extends_class and extends_method:
|
|
3424
|
+
parent_class = self.scope.get(extends_class) or self.global_scope.get(extends_class)
|
|
3425
|
+
if parent_class and isinstance(parent_class, CSSLClass):
|
|
3426
|
+
for constr in getattr(parent_class, 'constructors', []):
|
|
3427
|
+
if constr.value.get('name') == extends_method:
|
|
3428
|
+
# Execute parent constructor first to get local vars
|
|
3429
|
+
self._call_constructor(instance, constr, args, kwargs, param_values)
|
|
3430
|
+
break
|
|
3431
|
+
|
|
3432
|
+
# Handle append mode (++) - execute referenced parent member first
|
|
3433
|
+
if append_mode and append_ref_class:
|
|
3434
|
+
self._execute_append_reference(
|
|
3435
|
+
instance, append_ref_class, append_ref_member,
|
|
3436
|
+
args, kwargs, param_values, is_constructor=True
|
|
3437
|
+
)
|
|
3438
|
+
|
|
3439
|
+
# Execute constructor body
|
|
3440
|
+
prev_scope = self.scope
|
|
3441
|
+
self.scope = new_scope
|
|
3442
|
+
|
|
3443
|
+
try:
|
|
3444
|
+
for stmt in constr_node.children:
|
|
3445
|
+
self._execute_node(stmt)
|
|
3446
|
+
finally:
|
|
3447
|
+
self.scope = prev_scope
|
|
3448
|
+
self._current_instance = prev_instance
|
|
3449
|
+
|
|
2871
3450
|
def _eval_this_access(self, node: ASTNode) -> Any:
|
|
2872
3451
|
"""Evaluate 'this->member' access.
|
|
2873
3452
|
|
|
@@ -2905,6 +3484,10 @@ class CSSLRuntime:
|
|
|
2905
3484
|
if instance.has_method(member):
|
|
2906
3485
|
# Return a callable that will invoke the method with instance context
|
|
2907
3486
|
method_node = instance.get_method(member)
|
|
3487
|
+
# Check if this is an inherited Python method
|
|
3488
|
+
if isinstance(method_node, tuple) and method_node[0] == 'python_method':
|
|
3489
|
+
python_method = method_node[1]
|
|
3490
|
+
return lambda *args, **kwargs: python_method(*args, **kwargs)
|
|
2908
3491
|
return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
|
|
2909
3492
|
|
|
2910
3493
|
raise CSSLRuntimeError(
|
|
@@ -2916,12 +3499,18 @@ class CSSLRuntime:
|
|
|
2916
3499
|
"""Call a method on an instance with 'this' context.
|
|
2917
3500
|
|
|
2918
3501
|
Sets up the instance as the current 'this' context and executes the method.
|
|
3502
|
+
Supports append mode (++) for keeping parent code and adding new code.
|
|
2919
3503
|
"""
|
|
2920
3504
|
kwargs = kwargs or {}
|
|
2921
3505
|
func_info = method_node.value
|
|
2922
3506
|
params = func_info.get('params', [])
|
|
2923
3507
|
modifiers = func_info.get('modifiers', [])
|
|
2924
3508
|
|
|
3509
|
+
# Append mode: ++ operator for keeping parent code + adding new
|
|
3510
|
+
append_mode = func_info.get('append_mode', False)
|
|
3511
|
+
append_ref_class = func_info.get('append_ref_class') # &ClassName or &$instanceVar
|
|
3512
|
+
append_ref_member = func_info.get('append_ref_member') # ::methodName
|
|
3513
|
+
|
|
2925
3514
|
# Check for undefined modifier
|
|
2926
3515
|
is_undefined = 'undefined' in modifiers
|
|
2927
3516
|
|
|
@@ -2948,6 +3537,13 @@ class CSSLRuntime:
|
|
|
2948
3537
|
self._current_instance = instance
|
|
2949
3538
|
|
|
2950
3539
|
try:
|
|
3540
|
+
# Handle append mode (++) - execute referenced parent method first
|
|
3541
|
+
if append_mode and append_ref_class:
|
|
3542
|
+
self._execute_append_reference(
|
|
3543
|
+
instance, append_ref_class, append_ref_member,
|
|
3544
|
+
args, kwargs, {}, is_constructor=False
|
|
3545
|
+
)
|
|
3546
|
+
|
|
2951
3547
|
for child in method_node.children:
|
|
2952
3548
|
if not self._running:
|
|
2953
3549
|
break
|
|
@@ -2986,6 +3582,10 @@ class CSSLRuntime:
|
|
|
2986
3582
|
# Check for method
|
|
2987
3583
|
if obj.has_method(member):
|
|
2988
3584
|
method_node = obj.get_method(member)
|
|
3585
|
+
# Check if this is an inherited Python method
|
|
3586
|
+
if isinstance(method_node, tuple) and method_node[0] == 'python_method':
|
|
3587
|
+
python_method = method_node[1]
|
|
3588
|
+
return lambda *args, **kwargs: python_method(*args, **kwargs)
|
|
2989
3589
|
return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
|
|
2990
3590
|
raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")
|
|
2991
3591
|
|