IncludeCPP 3.6.0__py3-none-any.whl → 3.7.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/cli/commands.py +590 -11
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +301 -20
- includecpp/core/cssl/cssl_builtins.py +114 -0
- includecpp/core/cssl/cssl_builtins.pyi +1393 -0
- includecpp/core/cssl/cssl_parser.py +348 -70
- includecpp/core/cssl/cssl_runtime.py +456 -16
- includecpp/core/cssl/cssl_types.py +224 -2
- includecpp/core/cssl_bridge.py +477 -48
- includecpp/vscode/cssl/language-configuration.json +1 -4
- includecpp/vscode/cssl/package.json +24 -4
- includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +298 -17
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/METADATA +1 -1
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/RECORD +19 -17
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/WHEEL +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/entry_points.txt +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.6.0.dist-info → includecpp-3.7.9.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,8 @@ from .cssl_builtins import CSSLBuiltins
|
|
|
14
14
|
from .cssl_modules import get_module_registry, get_standard_module
|
|
15
15
|
from .cssl_types import (
|
|
16
16
|
Parameter, DataStruct, Shuffled, Iterator, Combo,
|
|
17
|
-
Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary
|
|
17
|
+
Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
|
|
18
|
+
CSSLClass, CSSLInstance
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
|
|
@@ -145,6 +146,7 @@ class CSSLRuntime:
|
|
|
145
146
|
self._injection_captures: Dict[str, Dict[str, Any]] = {} # Captured %vars per injection
|
|
146
147
|
self._current_captured_values: Dict[str, Any] = {} # Current captured values during injection execution
|
|
147
148
|
self._promoted_globals: Dict[str, Any] = {} # NEW: Variables promoted via global()
|
|
149
|
+
self._current_instance: Optional[CSSLInstance] = None # Current class instance for this-> access
|
|
148
150
|
self._running = False
|
|
149
151
|
self._exit_code = 0
|
|
150
152
|
self._output_callback = output_callback # Callback for console output (text, level)
|
|
@@ -386,6 +388,8 @@ class CSSLRuntime:
|
|
|
386
388
|
|
|
387
389
|
if child.type == 'struct':
|
|
388
390
|
self._exec_struct(child)
|
|
391
|
+
elif child.type == 'class':
|
|
392
|
+
self._exec_class(child)
|
|
389
393
|
elif child.type == 'function':
|
|
390
394
|
self._exec_function(child)
|
|
391
395
|
elif child.type == 'global_assignment':
|
|
@@ -394,6 +398,12 @@ class CSSLRuntime:
|
|
|
394
398
|
elif child.type == 'typed_declaration':
|
|
395
399
|
# Handle typed variable declaration: type<T> varName = value;
|
|
396
400
|
result = self._exec_typed_declaration(child)
|
|
401
|
+
elif child.type == 'instance_declaration':
|
|
402
|
+
# Handle instance declaration: instance<"name"> varName;
|
|
403
|
+
result = self._exec_instance_declaration(child)
|
|
404
|
+
elif child.type == 'super_func':
|
|
405
|
+
# Super-function for .cssl-pl payload files (#$run, #$exec, #$printl)
|
|
406
|
+
result = self._exec_super_func(child)
|
|
397
407
|
elif child.type in ('assignment', 'expression', 'inject', 'receive', 'flow',
|
|
398
408
|
'if', 'while', 'for', 'c_for', 'foreach', 'switch', 'try'):
|
|
399
409
|
result = self._execute_node(child)
|
|
@@ -661,6 +671,57 @@ class CSSLRuntime:
|
|
|
661
671
|
|
|
662
672
|
return struct_data
|
|
663
673
|
|
|
674
|
+
def _exec_class(self, node: ASTNode) -> CSSLClass:
|
|
675
|
+
"""Execute class definition - registers class in scope.
|
|
676
|
+
|
|
677
|
+
Parses class members and methods, creating a CSSLClass object
|
|
678
|
+
that can be instantiated with 'new'.
|
|
679
|
+
"""
|
|
680
|
+
class_info = node.value
|
|
681
|
+
class_name = class_info.get('name')
|
|
682
|
+
|
|
683
|
+
members = {} # Member variable defaults/types
|
|
684
|
+
methods = {} # Method AST nodes
|
|
685
|
+
constructor = None
|
|
686
|
+
|
|
687
|
+
for child in node.children:
|
|
688
|
+
if child.type == 'function':
|
|
689
|
+
# This is a method
|
|
690
|
+
func_info = child.value
|
|
691
|
+
method_name = func_info.get('name')
|
|
692
|
+
|
|
693
|
+
if func_info.get('is_constructor') or method_name == class_name or method_name == '__init__':
|
|
694
|
+
constructor = child
|
|
695
|
+
else:
|
|
696
|
+
methods[method_name] = child
|
|
697
|
+
|
|
698
|
+
elif child.type == 'typed_declaration':
|
|
699
|
+
# This is a member variable
|
|
700
|
+
decl = child.value
|
|
701
|
+
member_name = decl.get('name')
|
|
702
|
+
member_type = decl.get('type')
|
|
703
|
+
member_value = decl.get('value')
|
|
704
|
+
|
|
705
|
+
# Store member info with type and optional default
|
|
706
|
+
members[member_name] = {
|
|
707
|
+
'type': member_type,
|
|
708
|
+
'default': self._evaluate(member_value) if member_value else None
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
# Create class definition object
|
|
712
|
+
class_def = CSSLClass(
|
|
713
|
+
name=class_name,
|
|
714
|
+
members=members,
|
|
715
|
+
methods=methods,
|
|
716
|
+
constructor=constructor
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
# Register class in scope
|
|
720
|
+
self.scope.set(class_name, class_def)
|
|
721
|
+
self.global_scope.set(class_name, class_def)
|
|
722
|
+
|
|
723
|
+
return class_def
|
|
724
|
+
|
|
664
725
|
def _exec_function(self, node: ASTNode) -> Any:
|
|
665
726
|
"""Execute function definition - just registers it"""
|
|
666
727
|
func_info = node.value
|
|
@@ -714,6 +775,8 @@ class CSSLRuntime:
|
|
|
714
775
|
instance = List(element_type)
|
|
715
776
|
elif type_name in ('dictionary', 'dict'):
|
|
716
777
|
instance = Dictionary(element_type)
|
|
778
|
+
elif type_name == 'map':
|
|
779
|
+
instance = Map(element_type)
|
|
717
780
|
else:
|
|
718
781
|
# Default: evaluate the value or set to None
|
|
719
782
|
instance = self._evaluate(value_node) if value_node else None
|
|
@@ -732,6 +795,110 @@ class CSSLRuntime:
|
|
|
732
795
|
self.scope.set(var_name, instance)
|
|
733
796
|
return instance
|
|
734
797
|
|
|
798
|
+
def _exec_instance_declaration(self, node: ASTNode) -> Any:
|
|
799
|
+
"""Execute instance declaration: instance<"name"> varName;
|
|
800
|
+
|
|
801
|
+
Gets or creates a shared instance by name.
|
|
802
|
+
"""
|
|
803
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
804
|
+
decl = node.value
|
|
805
|
+
instance_name = decl.get('instance_name')
|
|
806
|
+
var_name = decl.get('name')
|
|
807
|
+
value_node = decl.get('value')
|
|
808
|
+
|
|
809
|
+
# Get existing shared instance
|
|
810
|
+
instance = None
|
|
811
|
+
if instance_name in _live_objects:
|
|
812
|
+
instance = SharedObjectProxy(instance_name, _live_objects[instance_name])
|
|
813
|
+
elif self.global_scope.has(f'${instance_name}'):
|
|
814
|
+
instance = self.global_scope.get(f'${instance_name}')
|
|
815
|
+
|
|
816
|
+
# If value is provided, use that and register as shared
|
|
817
|
+
if value_node:
|
|
818
|
+
instance = self._evaluate(value_node)
|
|
819
|
+
# Register in global scope for future access
|
|
820
|
+
self.global_scope.set(f'${instance_name}', instance)
|
|
821
|
+
|
|
822
|
+
# Store in scope
|
|
823
|
+
self.scope.set(var_name, instance)
|
|
824
|
+
return instance
|
|
825
|
+
|
|
826
|
+
def _exec_super_func(self, node: ASTNode) -> Any:
|
|
827
|
+
"""Execute super-function for .cssl-pl payload files.
|
|
828
|
+
|
|
829
|
+
Super-functions are pre-execution hooks that run when payload() loads a file.
|
|
830
|
+
|
|
831
|
+
Supported super-functions:
|
|
832
|
+
#$run(funcName) - Call a function defined in the payload
|
|
833
|
+
#$exec(expression) - Execute an expression immediately
|
|
834
|
+
#$printl(message) - Print a message during load
|
|
835
|
+
|
|
836
|
+
Example .cssl-pl file:
|
|
837
|
+
void initDatabase() {
|
|
838
|
+
printl("DB initialized");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
#$run(initDatabase); // Calls initDatabase when payload loads
|
|
842
|
+
#$printl("Payload loaded"); // Prints during load
|
|
843
|
+
"""
|
|
844
|
+
super_info = node.value
|
|
845
|
+
super_name = super_info.get('name', '') # e.g., "#$run", "#$exec", "#$printl"
|
|
846
|
+
args = super_info.get('args', [])
|
|
847
|
+
|
|
848
|
+
# Extract the function name part (after #$)
|
|
849
|
+
if super_name.startswith('#$'):
|
|
850
|
+
func_type = super_name[2:] # "run", "exec", "printl"
|
|
851
|
+
else:
|
|
852
|
+
func_type = super_name
|
|
853
|
+
|
|
854
|
+
if func_type == 'run':
|
|
855
|
+
# #$run(funcName) - Call a function by name
|
|
856
|
+
if args:
|
|
857
|
+
func_ref = args[0]
|
|
858
|
+
if isinstance(func_ref, ASTNode):
|
|
859
|
+
if func_ref.type == 'identifier':
|
|
860
|
+
func_name = func_ref.value
|
|
861
|
+
elif func_ref.type == 'call':
|
|
862
|
+
# Direct call like #$run(setup())
|
|
863
|
+
return self._eval_call(func_ref)
|
|
864
|
+
else:
|
|
865
|
+
func_name = self._evaluate(func_ref)
|
|
866
|
+
else:
|
|
867
|
+
func_name = str(func_ref)
|
|
868
|
+
|
|
869
|
+
# Look up and call the function
|
|
870
|
+
func_node = self.scope.get(func_name)
|
|
871
|
+
if func_node and isinstance(func_node, ASTNode) and func_node.type == 'function':
|
|
872
|
+
return self._call_function(func_node, [])
|
|
873
|
+
else:
|
|
874
|
+
raise CSSLRuntimeError(f"#$run: Function '{func_name}' not found", node.line)
|
|
875
|
+
|
|
876
|
+
elif func_type == 'exec':
|
|
877
|
+
# #$exec(expression) - Execute an expression
|
|
878
|
+
if args:
|
|
879
|
+
return self._evaluate(args[0])
|
|
880
|
+
|
|
881
|
+
elif func_type == 'printl':
|
|
882
|
+
# #$printl(message) - Print a message
|
|
883
|
+
if args:
|
|
884
|
+
msg = self._evaluate(args[0])
|
|
885
|
+
print(str(msg))
|
|
886
|
+
self.output_buffer.append(str(msg))
|
|
887
|
+
return None
|
|
888
|
+
|
|
889
|
+
elif func_type == 'print':
|
|
890
|
+
# #$print(message) - Print without newline
|
|
891
|
+
if args:
|
|
892
|
+
msg = self._evaluate(args[0])
|
|
893
|
+
print(str(msg), end='')
|
|
894
|
+
self.output_buffer.append(str(msg))
|
|
895
|
+
return None
|
|
896
|
+
|
|
897
|
+
else:
|
|
898
|
+
raise CSSLRuntimeError(f"Unknown super-function: {super_name}", node.line)
|
|
899
|
+
|
|
900
|
+
return None
|
|
901
|
+
|
|
735
902
|
def _exec_global_assignment(self, node: ASTNode) -> Any:
|
|
736
903
|
"""Execute global variable assignment: global Name = value
|
|
737
904
|
|
|
@@ -796,10 +963,20 @@ class CSSLRuntime:
|
|
|
796
963
|
|
|
797
964
|
# Bind parameters - handle both positional and named arguments
|
|
798
965
|
for i, param in enumerate(params):
|
|
799
|
-
# Extract param name from dict format: {'name': 'a', 'type': 'int'}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
966
|
+
# Extract param name and type from dict format: {'name': 'a', 'type': 'int'}
|
|
967
|
+
if isinstance(param, dict):
|
|
968
|
+
param_name = param['name']
|
|
969
|
+
param_type = param.get('type', '')
|
|
970
|
+
else:
|
|
971
|
+
param_name = param
|
|
972
|
+
param_type = ''
|
|
973
|
+
|
|
974
|
+
# Check if this is an 'open' parameter - receives all args as a list
|
|
975
|
+
if param_type == 'open' or param_name == 'Params':
|
|
976
|
+
# 'open Params' receives all arguments as a list
|
|
977
|
+
new_scope.set(param_name, list(args))
|
|
978
|
+
new_scope.set('Params', list(args)) # Also set 'Params' for OpenFind
|
|
979
|
+
elif param_name in kwargs:
|
|
803
980
|
# Named argument takes priority
|
|
804
981
|
new_scope.set(param_name, kwargs[param_name])
|
|
805
982
|
elif i < len(args):
|
|
@@ -1009,8 +1186,20 @@ class CSSLRuntime:
|
|
|
1009
1186
|
return None
|
|
1010
1187
|
|
|
1011
1188
|
def _exec_return(self, node: ASTNode) -> Any:
|
|
1012
|
-
"""Execute return statement
|
|
1013
|
-
|
|
1189
|
+
"""Execute return statement.
|
|
1190
|
+
|
|
1191
|
+
Supports multiple return values for shuffled functions:
|
|
1192
|
+
return a, b, c; // Returns tuple (a, b, c)
|
|
1193
|
+
"""
|
|
1194
|
+
if node.value is None:
|
|
1195
|
+
raise CSSLReturn(None)
|
|
1196
|
+
|
|
1197
|
+
# Check if this is a multiple return value
|
|
1198
|
+
if isinstance(node.value, dict) and node.value.get('multiple'):
|
|
1199
|
+
values = [self._evaluate(v) for v in node.value.get('values', [])]
|
|
1200
|
+
raise CSSLReturn(tuple(values))
|
|
1201
|
+
|
|
1202
|
+
value = self._evaluate(node.value)
|
|
1014
1203
|
raise CSSLReturn(value)
|
|
1015
1204
|
|
|
1016
1205
|
def _exec_break(self, node: ASTNode) -> Any:
|
|
@@ -1471,6 +1660,12 @@ class CSSLRuntime:
|
|
|
1471
1660
|
name = target.value
|
|
1472
1661
|
_live_objects[name] = final_value
|
|
1473
1662
|
self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
|
|
1663
|
+
elif target.type == 'instance_ref':
|
|
1664
|
+
# value ==> instance<"name"> - create/update shared object
|
|
1665
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
1666
|
+
name = target.value
|
|
1667
|
+
_live_objects[name] = final_value
|
|
1668
|
+
self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
|
|
1474
1669
|
elif target.type == 'member_access':
|
|
1475
1670
|
self._set_member(target, final_value)
|
|
1476
1671
|
|
|
@@ -1620,11 +1815,47 @@ class CSSLRuntime:
|
|
|
1620
1815
|
self._set_member(target, value)
|
|
1621
1816
|
elif target.type == 'index_access':
|
|
1622
1817
|
self._set_index(target, value)
|
|
1818
|
+
elif target.type == 'this_access':
|
|
1819
|
+
# this->member = value
|
|
1820
|
+
if self._current_instance is None:
|
|
1821
|
+
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
1822
|
+
member = target.value.get('member')
|
|
1823
|
+
self._current_instance.set_member(member, value)
|
|
1623
1824
|
elif isinstance(target, str):
|
|
1624
1825
|
self.scope.set(target, value)
|
|
1625
1826
|
|
|
1626
1827
|
return value
|
|
1627
1828
|
|
|
1829
|
+
def _exec_tuple_assignment(self, node: ASTNode) -> Any:
|
|
1830
|
+
"""Execute tuple unpacking assignment: a, b, c = shuffled_func()
|
|
1831
|
+
|
|
1832
|
+
Used with shuffled functions that return multiple values.
|
|
1833
|
+
"""
|
|
1834
|
+
targets = node.value.get('targets', [])
|
|
1835
|
+
value = self._evaluate(node.value.get('value'))
|
|
1836
|
+
|
|
1837
|
+
# Convert value to list if it's a tuple or iterable
|
|
1838
|
+
if isinstance(value, (list, tuple)):
|
|
1839
|
+
values = list(value)
|
|
1840
|
+
elif hasattr(value, '__iter__') and not isinstance(value, (str, dict)):
|
|
1841
|
+
values = list(value)
|
|
1842
|
+
else:
|
|
1843
|
+
# Single value - assign to first target only
|
|
1844
|
+
values = [value]
|
|
1845
|
+
|
|
1846
|
+
# Assign values to targets
|
|
1847
|
+
for i, target in enumerate(targets):
|
|
1848
|
+
if i < len(values):
|
|
1849
|
+
var_name = target.value if isinstance(target, ASTNode) else target
|
|
1850
|
+
self.scope.set(var_name, values[i])
|
|
1851
|
+
else:
|
|
1852
|
+
# More targets than values - set to None
|
|
1853
|
+
var_name = target.value if isinstance(target, ASTNode) else target
|
|
1854
|
+
self.scope.set(var_name, None)
|
|
1855
|
+
|
|
1856
|
+
# Assignment statements don't produce a visible result
|
|
1857
|
+
return None
|
|
1858
|
+
|
|
1628
1859
|
def _exec_expression(self, node: ASTNode) -> Any:
|
|
1629
1860
|
"""Execute expression statement"""
|
|
1630
1861
|
return self._evaluate(node.value)
|
|
@@ -1678,9 +1909,12 @@ class CSSLRuntime:
|
|
|
1678
1909
|
|
|
1679
1910
|
if node.type == 'literal':
|
|
1680
1911
|
value = node.value
|
|
1681
|
-
#
|
|
1682
|
-
if isinstance(value, str)
|
|
1683
|
-
|
|
1912
|
+
# String interpolation - replace {var} or <var> with scope values
|
|
1913
|
+
if isinstance(value, str):
|
|
1914
|
+
has_fstring = '{' in value and '}' in value
|
|
1915
|
+
has_legacy = '<' in value and '>' in value
|
|
1916
|
+
if has_fstring or has_legacy:
|
|
1917
|
+
value = self._interpolate_string(value)
|
|
1684
1918
|
return value
|
|
1685
1919
|
|
|
1686
1920
|
# NEW: Type literals (list, dict) - create empty instances
|
|
@@ -1767,10 +2001,34 @@ class CSSLRuntime:
|
|
|
1767
2001
|
return value
|
|
1768
2002
|
raise CSSLRuntimeError(f"Captured reference '%{name}' not found.")
|
|
1769
2003
|
|
|
2004
|
+
if node.type == 'instance_ref':
|
|
2005
|
+
# instance<"name"> - get shared instance by name
|
|
2006
|
+
# Works like $name but with explicit syntax
|
|
2007
|
+
from ..cssl_bridge import _live_objects, SharedObjectProxy
|
|
2008
|
+
name = node.value
|
|
2009
|
+
if name in _live_objects:
|
|
2010
|
+
return SharedObjectProxy(name, _live_objects[name])
|
|
2011
|
+
# Check if stored in runtime's scope
|
|
2012
|
+
scoped_val = self.global_scope.get(f'${name}')
|
|
2013
|
+
if scoped_val is not None:
|
|
2014
|
+
return scoped_val
|
|
2015
|
+
# Return None if instance doesn't exist (can be created via ==>)
|
|
2016
|
+
return None
|
|
2017
|
+
|
|
2018
|
+
if node.type == 'new':
|
|
2019
|
+
# Create new instance of a class: new ClassName(args)
|
|
2020
|
+
return self._eval_new(node)
|
|
2021
|
+
|
|
2022
|
+
if node.type == 'this_access':
|
|
2023
|
+
# this->member access
|
|
2024
|
+
return self._eval_this_access(node)
|
|
2025
|
+
|
|
1770
2026
|
if node.type == 'type_instantiation':
|
|
1771
|
-
# Create new instance of a type: stack<string>, vector<int>, etc.
|
|
2027
|
+
# Create new instance of a type: stack<string>, vector<int>, map<K,V>, etc.
|
|
1772
2028
|
type_name = node.value.get('type')
|
|
1773
2029
|
element_type = node.value.get('element_type', 'dynamic')
|
|
2030
|
+
value_type = node.value.get('value_type') # For map<K, V>
|
|
2031
|
+
init_values = node.value.get('init_values') # For inline init: map<K,V>{...}
|
|
1774
2032
|
|
|
1775
2033
|
if type_name == 'stack':
|
|
1776
2034
|
return Stack(element_type)
|
|
@@ -1794,6 +2052,15 @@ class CSSLRuntime:
|
|
|
1794
2052
|
return List(element_type)
|
|
1795
2053
|
elif type_name in ('dictionary', 'dict'):
|
|
1796
2054
|
return Dictionary(element_type)
|
|
2055
|
+
elif type_name == 'map':
|
|
2056
|
+
# Create Map with key_type and value_type
|
|
2057
|
+
m = Map(element_type, value_type or 'dynamic')
|
|
2058
|
+
# If inline initialization provided, populate the map
|
|
2059
|
+
if init_values:
|
|
2060
|
+
for key, value_node in init_values.items():
|
|
2061
|
+
value = self._evaluate(value_node)
|
|
2062
|
+
m.insert(key, value)
|
|
2063
|
+
return m
|
|
1797
2064
|
else:
|
|
1798
2065
|
return None
|
|
1799
2066
|
|
|
@@ -2136,6 +2403,140 @@ class CSSLRuntime:
|
|
|
2136
2403
|
hint="Typed functions use format: FunctionName<Type>(args)"
|
|
2137
2404
|
)
|
|
2138
2405
|
|
|
2406
|
+
def _eval_new(self, node: ASTNode) -> CSSLInstance:
|
|
2407
|
+
"""Evaluate 'new ClassName(args)' expression.
|
|
2408
|
+
|
|
2409
|
+
Creates a new instance of a CSSL class and calls its constructor.
|
|
2410
|
+
"""
|
|
2411
|
+
class_name = node.value.get('class')
|
|
2412
|
+
args = [self._evaluate(arg) for arg in node.value.get('args', [])]
|
|
2413
|
+
kwargs = {k: self._evaluate(v) for k, v in node.value.get('kwargs', {}).items()}
|
|
2414
|
+
|
|
2415
|
+
# Get class definition from scope
|
|
2416
|
+
class_def = self.scope.get(class_name)
|
|
2417
|
+
if class_def is None:
|
|
2418
|
+
class_def = self.global_scope.get(class_name)
|
|
2419
|
+
|
|
2420
|
+
if class_def is None:
|
|
2421
|
+
raise CSSLRuntimeError(
|
|
2422
|
+
f"Class '{class_name}' not found",
|
|
2423
|
+
node.line,
|
|
2424
|
+
hint="Make sure the class is defined before instantiation"
|
|
2425
|
+
)
|
|
2426
|
+
|
|
2427
|
+
if not isinstance(class_def, CSSLClass):
|
|
2428
|
+
raise CSSLRuntimeError(
|
|
2429
|
+
f"'{class_name}' is not a class",
|
|
2430
|
+
node.line,
|
|
2431
|
+
hint=f"'{class_name}' is of type {type(class_def).__name__}"
|
|
2432
|
+
)
|
|
2433
|
+
|
|
2434
|
+
# Create new instance
|
|
2435
|
+
instance = CSSLInstance(class_def)
|
|
2436
|
+
|
|
2437
|
+
# Call constructor if defined
|
|
2438
|
+
if class_def.constructor:
|
|
2439
|
+
self._call_method(instance, class_def.constructor, args, kwargs)
|
|
2440
|
+
|
|
2441
|
+
return instance
|
|
2442
|
+
|
|
2443
|
+
def _eval_this_access(self, node: ASTNode) -> Any:
|
|
2444
|
+
"""Evaluate 'this->member' access.
|
|
2445
|
+
|
|
2446
|
+
Returns the value of a member from the current class instance.
|
|
2447
|
+
"""
|
|
2448
|
+
if self._current_instance is None:
|
|
2449
|
+
raise CSSLRuntimeError(
|
|
2450
|
+
"'this' used outside of class method context",
|
|
2451
|
+
node.line if hasattr(node, 'line') else 0,
|
|
2452
|
+
hint="'this->' can only be used inside class methods"
|
|
2453
|
+
)
|
|
2454
|
+
|
|
2455
|
+
member = node.value.get('member')
|
|
2456
|
+
|
|
2457
|
+
# Check if it's a chained access (this->a->b)
|
|
2458
|
+
if 'object' in node.value:
|
|
2459
|
+
# First evaluate the object part
|
|
2460
|
+
obj = self._evaluate(node.value.get('object'))
|
|
2461
|
+
if obj is None:
|
|
2462
|
+
return None
|
|
2463
|
+
if hasattr(obj, member):
|
|
2464
|
+
return getattr(obj, member)
|
|
2465
|
+
if isinstance(obj, dict):
|
|
2466
|
+
return obj.get(member)
|
|
2467
|
+
return None
|
|
2468
|
+
|
|
2469
|
+
# Direct this->member access
|
|
2470
|
+
instance = self._current_instance
|
|
2471
|
+
|
|
2472
|
+
# Check if it's a member variable
|
|
2473
|
+
if instance.has_member(member):
|
|
2474
|
+
return instance.get_member(member)
|
|
2475
|
+
|
|
2476
|
+
# Check if it's a method
|
|
2477
|
+
if instance.has_method(member):
|
|
2478
|
+
# Return a callable that will invoke the method with instance context
|
|
2479
|
+
method_node = instance.get_method(member)
|
|
2480
|
+
return lambda *args, **kwargs: self._call_method(instance, method_node, list(args), kwargs)
|
|
2481
|
+
|
|
2482
|
+
raise CSSLRuntimeError(
|
|
2483
|
+
f"'{instance._class.name}' has no member or method '{member}'",
|
|
2484
|
+
node.line if hasattr(node, 'line') else 0
|
|
2485
|
+
)
|
|
2486
|
+
|
|
2487
|
+
def _call_method(self, instance: CSSLInstance, method_node: ASTNode, args: list, kwargs: dict = None) -> Any:
|
|
2488
|
+
"""Call a method on an instance with 'this' context.
|
|
2489
|
+
|
|
2490
|
+
Sets up the instance as the current 'this' context and executes the method.
|
|
2491
|
+
"""
|
|
2492
|
+
kwargs = kwargs or {}
|
|
2493
|
+
func_info = method_node.value
|
|
2494
|
+
params = func_info.get('params', [])
|
|
2495
|
+
modifiers = func_info.get('modifiers', [])
|
|
2496
|
+
|
|
2497
|
+
# Check for undefined modifier
|
|
2498
|
+
is_undefined = 'undefined' in modifiers
|
|
2499
|
+
|
|
2500
|
+
# Create new scope for method
|
|
2501
|
+
new_scope = Scope(parent=self.scope)
|
|
2502
|
+
|
|
2503
|
+
# Bind parameters
|
|
2504
|
+
for i, param in enumerate(params):
|
|
2505
|
+
param_name = param['name'] if isinstance(param, dict) else param
|
|
2506
|
+
|
|
2507
|
+
if param_name in kwargs:
|
|
2508
|
+
new_scope.set(param_name, kwargs[param_name])
|
|
2509
|
+
elif i < len(args):
|
|
2510
|
+
new_scope.set(param_name, args[i])
|
|
2511
|
+
else:
|
|
2512
|
+
new_scope.set(param_name, None)
|
|
2513
|
+
|
|
2514
|
+
# Save current state
|
|
2515
|
+
old_scope = self.scope
|
|
2516
|
+
old_instance = self._current_instance
|
|
2517
|
+
|
|
2518
|
+
# Set up method context
|
|
2519
|
+
self.scope = new_scope
|
|
2520
|
+
self._current_instance = instance
|
|
2521
|
+
|
|
2522
|
+
try:
|
|
2523
|
+
for child in method_node.children:
|
|
2524
|
+
if not self._running:
|
|
2525
|
+
break
|
|
2526
|
+
self._execute_node(child)
|
|
2527
|
+
except CSSLReturn as ret:
|
|
2528
|
+
return ret.value
|
|
2529
|
+
except Exception as e:
|
|
2530
|
+
if is_undefined:
|
|
2531
|
+
return None
|
|
2532
|
+
raise
|
|
2533
|
+
finally:
|
|
2534
|
+
# Restore previous state
|
|
2535
|
+
self.scope = old_scope
|
|
2536
|
+
self._current_instance = old_instance
|
|
2537
|
+
|
|
2538
|
+
return None
|
|
2539
|
+
|
|
2139
2540
|
def _eval_member_access(self, node: ASTNode) -> Any:
|
|
2140
2541
|
"""Evaluate member access"""
|
|
2141
2542
|
obj = self._evaluate(node.value.get('object'))
|
|
@@ -2149,6 +2550,17 @@ class CSSLRuntime:
|
|
|
2149
2550
|
if isinstance(obj, Parameter) and member == 'return':
|
|
2150
2551
|
member = 'return_'
|
|
2151
2552
|
|
|
2553
|
+
# === CSSL CLASS INSTANCE METHODS ===
|
|
2554
|
+
if isinstance(obj, CSSLInstance):
|
|
2555
|
+
# Check for member variable
|
|
2556
|
+
if obj.has_member(member):
|
|
2557
|
+
return obj.get_member(member)
|
|
2558
|
+
# Check for method
|
|
2559
|
+
if obj.has_method(member):
|
|
2560
|
+
method_node = obj.get_method(member)
|
|
2561
|
+
return lambda *args, **kwargs: self._call_method(obj, method_node, list(args), kwargs)
|
|
2562
|
+
raise CSSLRuntimeError(f"'{obj._class.name}' has no member or method '{member}'")
|
|
2563
|
+
|
|
2152
2564
|
# === STRING METHODS ===
|
|
2153
2565
|
if isinstance(obj, str):
|
|
2154
2566
|
string_methods = self._get_string_method(obj, member)
|
|
@@ -2382,6 +2794,11 @@ class CSSLRuntime:
|
|
|
2382
2794
|
if obj is None:
|
|
2383
2795
|
return
|
|
2384
2796
|
|
|
2797
|
+
# Check for CSSLInstance - use set_member method
|
|
2798
|
+
if isinstance(obj, CSSLInstance):
|
|
2799
|
+
obj.set_member(member, value)
|
|
2800
|
+
return
|
|
2801
|
+
|
|
2385
2802
|
# Check for SharedObjectProxy - directly access underlying object
|
|
2386
2803
|
# This is more robust than relying on the proxy's __setattr__
|
|
2387
2804
|
if hasattr(obj, '_direct_object') and hasattr(obj, '_name'):
|
|
@@ -2442,14 +2859,24 @@ class CSSLRuntime:
|
|
|
2442
2859
|
elif isinstance(obj, dict):
|
|
2443
2860
|
obj[final_attr] = value
|
|
2444
2861
|
|
|
2445
|
-
#
|
|
2862
|
+
# String interpolation (supports both <var> and {var} syntax)
|
|
2446
2863
|
def _interpolate_string(self, string: str) -> str:
|
|
2447
|
-
"""Replace <variable> placeholders with values from scope
|
|
2864
|
+
"""Replace {variable} and <variable> placeholders with values from scope.
|
|
2865
|
+
|
|
2866
|
+
Both syntaxes are supported (variables only, not expressions):
|
|
2867
|
+
"Hello {name}!" -> "Hello John!" (f-string style)
|
|
2868
|
+
"Hello <name>!" -> "Hello John!" (legacy CSSL style)
|
|
2448
2869
|
|
|
2449
|
-
|
|
2870
|
+
Examples:
|
|
2871
|
+
string name = "Alice";
|
|
2872
|
+
int age = 30;
|
|
2873
|
+
printl("Hello {name}, you are {age} years old!");
|
|
2874
|
+
printl("Hello <name>, you are <age> years old!");
|
|
2875
|
+
|
|
2876
|
+
Note: Only simple variable names are supported, not expressions.
|
|
2877
|
+
Use string concatenation for complex expressions.
|
|
2450
2878
|
"""
|
|
2451
2879
|
import re
|
|
2452
|
-
pattern = r'<([A-Za-z_][A-Za-z0-9_]*)>'
|
|
2453
2880
|
|
|
2454
2881
|
def replacer(match):
|
|
2455
2882
|
var_name = match.group(1)
|
|
@@ -2461,10 +2888,23 @@ class CSSLRuntime:
|
|
|
2461
2888
|
# Try modules
|
|
2462
2889
|
if value is None:
|
|
2463
2890
|
value = self._modules.get(var_name)
|
|
2891
|
+
# Try global scope
|
|
2892
|
+
if value is None:
|
|
2893
|
+
value = self.global_scope.get(var_name)
|
|
2464
2894
|
# Return string representation or empty string if None
|
|
2465
2895
|
return str(value) if value is not None else ''
|
|
2466
2896
|
|
|
2467
|
-
|
|
2897
|
+
# Support both {var} and <var> syntax - simple variable names only
|
|
2898
|
+
# Pattern: {identifier} or <identifier>
|
|
2899
|
+
patterns = [
|
|
2900
|
+
r'\{([A-Za-z_][A-Za-z0-9_]*)\}', # {name} f-string style
|
|
2901
|
+
r'<([A-Za-z_][A-Za-z0-9_]*)>', # <name> legacy CSSL style
|
|
2902
|
+
]
|
|
2903
|
+
|
|
2904
|
+
result = string
|
|
2905
|
+
for pattern in patterns:
|
|
2906
|
+
result = re.sub(pattern, replacer, result)
|
|
2907
|
+
return result
|
|
2468
2908
|
|
|
2469
2909
|
# NEW: Promote variable to global scope via global()
|
|
2470
2910
|
def promote_to_global(self, s_ref_name: str):
|