IncludeCPP 4.5.2__py3-none-any.whl → 4.9.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- includecpp/CHANGELOG.md +241 -0
- includecpp/__init__.py +89 -3
- includecpp/__init__.pyi +2 -1
- includecpp/cli/commands.py +1747 -266
- includecpp/cli/config_parser.py +1 -1
- includecpp/core/build_manager.py +64 -13
- includecpp/core/cpp_api_extensions.pyi +43 -270
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +1799 -1445
- includecpp/core/cssl/cpp/build/api.pyd +0 -0
- includecpp/core/cssl/cpp/build/api.pyi +274 -0
- includecpp/core/cssl/cpp/build/cssl_core.pyi +0 -99
- includecpp/core/cssl/cpp/cssl_core.cp +2 -23
- includecpp/core/cssl/cssl_builtins.py +2116 -171
- includecpp/core/cssl/cssl_builtins.pyi +1324 -104
- includecpp/core/cssl/cssl_compiler.py +4 -1
- includecpp/core/cssl/cssl_modules.py +605 -6
- includecpp/core/cssl/cssl_optimizer.py +12 -1
- includecpp/core/cssl/cssl_parser.py +1048 -52
- includecpp/core/cssl/cssl_runtime.py +2041 -131
- includecpp/core/cssl/cssl_syntax.py +405 -277
- includecpp/core/cssl/cssl_types.py +5891 -1655
- includecpp/core/cssl_bridge.py +429 -3
- includecpp/core/error_catalog.py +54 -10
- includecpp/core/homeserver.py +1037 -0
- includecpp/generator/parser.cpp +203 -39
- includecpp/generator/parser.h +15 -1
- includecpp/templates/cpp.proj.template +1 -1
- includecpp/vscode/cssl/snippets/cssl.snippets.json +163 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +87 -12
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/METADATA +81 -10
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/RECORD +35 -33
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/WHEEL +1 -1
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/entry_points.txt +0 -0
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/licenses/LICENSE +0 -0
- {includecpp-4.5.2.dist-info → includecpp-4.9.3.dist-info}/top_level.txt +0 -0
|
@@ -15,7 +15,11 @@ from .cssl_modules import get_module_registry, get_standard_module
|
|
|
15
15
|
from .cssl_types import (
|
|
16
16
|
Parameter, DataStruct, Shuffled, Iterator, Combo,
|
|
17
17
|
Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary, Map,
|
|
18
|
-
|
|
18
|
+
Queue, # v4.7: Thread-safe queue
|
|
19
|
+
CSSLClass, CSSLInstance, ByteArrayed,
|
|
20
|
+
CSSLNamespace, # v4.8: Custom namespace support
|
|
21
|
+
Bit, Byte, Address, # v4.9.0: Binary types and address pointer
|
|
22
|
+
CSSLFuture, CSSLGenerator, CSSLAsyncFunction, AsyncModule # v4.9.3: Async support
|
|
19
23
|
)
|
|
20
24
|
|
|
21
25
|
|
|
@@ -199,13 +203,78 @@ class CSSLReturn(Exception):
|
|
|
199
203
|
super().__init__()
|
|
200
204
|
|
|
201
205
|
|
|
206
|
+
class CSSLYield(Exception):
|
|
207
|
+
"""Yield statement - v4.9.3: Generator yield that pauses execution."""
|
|
208
|
+
def __init__(self, value: Any = None):
|
|
209
|
+
self.value = value
|
|
210
|
+
super().__init__()
|
|
211
|
+
|
|
212
|
+
|
|
202
213
|
class CSSLThrow(Exception):
|
|
203
|
-
"""Throw statement - v4.5.1: User-thrown exceptions that propagate to catch blocks
|
|
204
|
-
|
|
214
|
+
"""Throw statement - v4.5.1: User-thrown exceptions that propagate to catch blocks
|
|
215
|
+
v4.8: Extended to support Python exception types via raise statement.
|
|
216
|
+
"""
|
|
217
|
+
def __init__(self, message: Any = None, underlying_exception: Exception = None):
|
|
205
218
|
self.message = message
|
|
219
|
+
self.underlying_exception = underlying_exception
|
|
220
|
+
self.exception_type = type(underlying_exception).__name__ if underlying_exception else 'Error'
|
|
206
221
|
super().__init__(str(message) if message else "")
|
|
207
222
|
|
|
208
223
|
|
|
224
|
+
class SuperProxy:
|
|
225
|
+
"""v4.8.8: Proxy for super->method() calls in child classes.
|
|
226
|
+
|
|
227
|
+
Provides access to parent class methods from within a child class method.
|
|
228
|
+
|
|
229
|
+
Usage in CSSL:
|
|
230
|
+
class Parent {
|
|
231
|
+
define greet() { println("Hello from Parent"); }
|
|
232
|
+
}
|
|
233
|
+
class Child extends Parent {
|
|
234
|
+
define greet() {
|
|
235
|
+
super->greet(); // Calls Parent.greet()
|
|
236
|
+
println("Hello from Child");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
"""
|
|
240
|
+
def __init__(self, instance: 'CSSLInstance', parent_class: 'CSSLClass', runtime: 'CSSLRuntime'):
|
|
241
|
+
self._instance = instance
|
|
242
|
+
self._parent_class = parent_class
|
|
243
|
+
self._runtime = runtime
|
|
244
|
+
|
|
245
|
+
def get_method(self, name: str):
|
|
246
|
+
"""Get a method from the parent class."""
|
|
247
|
+
if self._parent_class is None:
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
# Check parent class methods (methods is a dict with name -> AST node)
|
|
251
|
+
if hasattr(self._parent_class, 'methods') and isinstance(self._parent_class.methods, dict):
|
|
252
|
+
if name in self._parent_class.methods:
|
|
253
|
+
return self._parent_class.methods[name]
|
|
254
|
+
|
|
255
|
+
# Check if parent has its own parent (grandparent) - recursive lookup
|
|
256
|
+
if hasattr(self._parent_class, 'parent') and self._parent_class.parent:
|
|
257
|
+
grandparent_proxy = SuperProxy(self._instance, self._parent_class.parent, self._runtime)
|
|
258
|
+
return grandparent_proxy.get_method(name)
|
|
259
|
+
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
def get_member(self, name: str):
|
|
263
|
+
"""Get a member variable from parent class defaults."""
|
|
264
|
+
if self._parent_class is None:
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
# Check parent class member defaults (members is a dict with name -> type/default)
|
|
268
|
+
if hasattr(self._parent_class, 'members') and isinstance(self._parent_class.members, dict):
|
|
269
|
+
if name in self._parent_class.members:
|
|
270
|
+
member_info = self._parent_class.members[name]
|
|
271
|
+
if isinstance(member_info, dict) and 'default' in member_info:
|
|
272
|
+
return self._runtime._evaluate(member_info['default'])
|
|
273
|
+
return member_info
|
|
274
|
+
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
|
|
209
278
|
@dataclass
|
|
210
279
|
class Scope:
|
|
211
280
|
"""Variable scope"""
|
|
@@ -240,7 +309,11 @@ class Scope:
|
|
|
240
309
|
|
|
241
310
|
@dataclass
|
|
242
311
|
class ServiceDefinition:
|
|
243
|
-
"""Parsed service definition
|
|
312
|
+
"""Parsed service definition
|
|
313
|
+
|
|
314
|
+
v4.8.6: Added __getattr__ and get() for accessing exported functions/structs/classes.
|
|
315
|
+
This allows include() modules to be used like: mod.myFunc() or mod.get('myFunc')
|
|
316
|
+
"""
|
|
244
317
|
name: str = ""
|
|
245
318
|
version: str = "1.0"
|
|
246
319
|
author: str = ""
|
|
@@ -250,7 +323,56 @@ class ServiceDefinition:
|
|
|
250
323
|
priority: int = 0
|
|
251
324
|
structs: Dict[str, ASTNode] = field(default_factory=dict)
|
|
252
325
|
functions: Dict[str, ASTNode] = field(default_factory=dict)
|
|
326
|
+
classes: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Class definitions
|
|
327
|
+
enums: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Enum definitions
|
|
328
|
+
namespaces: Dict[str, Any] = field(default_factory=dict) # v4.8.6: Namespace definitions
|
|
253
329
|
event_handlers: Dict[str, List[ASTNode]] = field(default_factory=dict)
|
|
330
|
+
_runtime: Any = field(default=None, repr=False) # Reference to runtime for calling functions
|
|
331
|
+
|
|
332
|
+
def get(self, name: str, default: Any = None) -> Any:
|
|
333
|
+
"""Get an exported function, struct, class, enum, or namespace by name."""
|
|
334
|
+
if name in self.functions:
|
|
335
|
+
return self.functions[name]
|
|
336
|
+
if name in self.classes:
|
|
337
|
+
return self.classes[name]
|
|
338
|
+
if name in self.structs:
|
|
339
|
+
return self.structs[name]
|
|
340
|
+
if name in self.enums:
|
|
341
|
+
return self.enums[name]
|
|
342
|
+
if name in self.namespaces:
|
|
343
|
+
return self.namespaces[name]
|
|
344
|
+
return default
|
|
345
|
+
|
|
346
|
+
def __getattr__(self, name: str) -> Any:
|
|
347
|
+
"""Allow direct attribute access to functions, classes, structs, etc.
|
|
348
|
+
|
|
349
|
+
Usage: mod.myFunc (returns the function AST node)
|
|
350
|
+
mod.MyClass (returns the class definition)
|
|
351
|
+
"""
|
|
352
|
+
# Avoid infinite recursion with dataclass fields
|
|
353
|
+
if name.startswith('_') or name in ('name', 'version', 'author', 'description',
|
|
354
|
+
'dependencies', 'autostart', 'priority',
|
|
355
|
+
'structs', 'functions', 'classes', 'enums',
|
|
356
|
+
'namespaces', 'event_handlers'):
|
|
357
|
+
raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
|
|
358
|
+
|
|
359
|
+
# Check classes first (most common case after functions)
|
|
360
|
+
if hasattr(self, 'classes') and name in self.classes:
|
|
361
|
+
return self.classes[name]
|
|
362
|
+
# Check functions
|
|
363
|
+
if hasattr(self, 'functions') and name in self.functions:
|
|
364
|
+
return self.functions[name]
|
|
365
|
+
# Check structs
|
|
366
|
+
if hasattr(self, 'structs') and name in self.structs:
|
|
367
|
+
return self.structs[name]
|
|
368
|
+
# Check enums
|
|
369
|
+
if hasattr(self, 'enums') and name in self.enums:
|
|
370
|
+
return self.enums[name]
|
|
371
|
+
# Check namespaces
|
|
372
|
+
if hasattr(self, 'namespaces') and name in self.namespaces:
|
|
373
|
+
return self.namespaces[name]
|
|
374
|
+
|
|
375
|
+
raise AttributeError(f"Module '{self.name}' has no export '{name}'")
|
|
254
376
|
|
|
255
377
|
|
|
256
378
|
class CSSLRuntime:
|
|
@@ -272,6 +394,8 @@ class CSSLRuntime:
|
|
|
272
394
|
self._function_injections: Dict[str, List[tuple]] = {} # List of (code_block, captured_values_dict)
|
|
273
395
|
self._function_replaced: Dict[str, bool] = {} # NEW: Track replaced functions (<<==)
|
|
274
396
|
self._original_functions: Dict[str, Any] = {} # Store originals before replacement
|
|
397
|
+
self._hook_executing: set = set() # v4.9.2: Track currently executing hooks to prevent recursion
|
|
398
|
+
self._hook_locals: dict = {} # v4.9.2: Local variables from hooked function for local:: access
|
|
275
399
|
self._injection_captures: Dict[str, Dict[str, Any]] = {} # Captured %vars per injection
|
|
276
400
|
self._current_captured_values: Dict[str, Any] = {} # Current captured values during injection execution
|
|
277
401
|
self._promoted_globals: Dict[str, Any] = {} # NEW: Variables promoted via global()
|
|
@@ -346,6 +470,15 @@ class CSSLRuntime:
|
|
|
346
470
|
for name in self.builtins.list_functions():
|
|
347
471
|
self.global_scope.set(name, self.builtins.get_function(name))
|
|
348
472
|
|
|
473
|
+
# v4.8.5: Setup C++ style stream variables (not functions)
|
|
474
|
+
# This enables: cout << "Hello" << endl; (without parentheses)
|
|
475
|
+
from .cssl_types import OutputStream, InputStream
|
|
476
|
+
self.global_scope.set('cout', OutputStream('stdout'))
|
|
477
|
+
self.global_scope.set('cerr', OutputStream('stderr'))
|
|
478
|
+
self.global_scope.set('clog', OutputStream('clog'))
|
|
479
|
+
self.global_scope.set('cin', InputStream('stdin'))
|
|
480
|
+
self.global_scope.set('endl', '\n') # endl as newline marker
|
|
481
|
+
|
|
349
482
|
def get_module(self, path: str) -> Any:
|
|
350
483
|
"""Get a module by path like 'KernelClient.VSRam'"""
|
|
351
484
|
parts = path.split('.')
|
|
@@ -429,6 +562,40 @@ class CSSLRuntime:
|
|
|
429
562
|
return self._source_lines[line - 1]
|
|
430
563
|
return ""
|
|
431
564
|
|
|
565
|
+
def _get_empty_value_for_type(self, value: Any) -> Any:
|
|
566
|
+
"""v4.8.6: Get an empty value of the same type for move operations.
|
|
567
|
+
|
|
568
|
+
Used by -<== operator to clear source after move without destroying it.
|
|
569
|
+
Returns empty container for containers, None for primitives.
|
|
570
|
+
"""
|
|
571
|
+
from .cssl_types import Stack, Vector, Array, DataStruct, List, Dictionary, Map, Queue
|
|
572
|
+
|
|
573
|
+
if isinstance(value, Stack):
|
|
574
|
+
return Stack(getattr(value, '_element_type', 'dynamic'))
|
|
575
|
+
elif isinstance(value, Vector):
|
|
576
|
+
return Vector(getattr(value, '_element_type', 'dynamic'))
|
|
577
|
+
elif isinstance(value, Array):
|
|
578
|
+
return Array(getattr(value, '_element_type', 'dynamic'))
|
|
579
|
+
elif isinstance(value, DataStruct):
|
|
580
|
+
return DataStruct(getattr(value, '_element_type', 'dynamic'))
|
|
581
|
+
elif isinstance(value, List):
|
|
582
|
+
return List(getattr(value, '_element_type', 'dynamic'))
|
|
583
|
+
elif isinstance(value, Dictionary):
|
|
584
|
+
return Dictionary(getattr(value, '_element_type', 'dynamic'))
|
|
585
|
+
elif isinstance(value, Map):
|
|
586
|
+
return Map(getattr(value, '_element_type', 'dynamic'))
|
|
587
|
+
elif isinstance(value, Queue):
|
|
588
|
+
return Queue(getattr(value, '_element_type', 'dynamic'))
|
|
589
|
+
elif isinstance(value, list):
|
|
590
|
+
return []
|
|
591
|
+
elif isinstance(value, dict):
|
|
592
|
+
return {}
|
|
593
|
+
elif isinstance(value, str):
|
|
594
|
+
return ""
|
|
595
|
+
else:
|
|
596
|
+
# For other types (int, float, objects), return None
|
|
597
|
+
return None
|
|
598
|
+
|
|
432
599
|
def execute(self, source: str) -> Any:
|
|
433
600
|
"""Execute CSSL service source code"""
|
|
434
601
|
self._source_lines = source.splitlines()
|
|
@@ -456,17 +623,28 @@ class CSSLRuntime:
|
|
|
456
623
|
return self._execute_node(ast)
|
|
457
624
|
|
|
458
625
|
def execute_file(self, filepath: str) -> Any:
|
|
459
|
-
"""Execute a CSSL service
|
|
626
|
+
"""Execute a CSSL file (auto-detects service vs program format)"""
|
|
460
627
|
import os
|
|
461
628
|
self._current_file = os.path.basename(filepath)
|
|
629
|
+
# v4.8.8: Track full path for relative payload resolution
|
|
630
|
+
self._current_file_path = os.path.abspath(filepath)
|
|
462
631
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
463
632
|
source = f.read()
|
|
464
|
-
|
|
633
|
+
|
|
634
|
+
# Auto-detect: if file starts with service-init, use service parser
|
|
635
|
+
# Otherwise use program parser (supports class, struct, define at top level)
|
|
636
|
+
stripped = source.lstrip()
|
|
637
|
+
if stripped.startswith('service-init') or stripped.startswith('service-run') or stripped.startswith('service-include'):
|
|
638
|
+
return self.execute(source)
|
|
639
|
+
else:
|
|
640
|
+
return self.execute_program(source)
|
|
465
641
|
|
|
466
642
|
def execute_program_file(self, filepath: str) -> Any:
|
|
467
643
|
"""Execute a standalone CSSL program file"""
|
|
468
644
|
import os
|
|
469
645
|
self._current_file = os.path.basename(filepath)
|
|
646
|
+
# v4.8.8: Track full path for relative payload resolution
|
|
647
|
+
self._current_file_path = os.path.abspath(filepath)
|
|
470
648
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
471
649
|
source = f.read()
|
|
472
650
|
return self.execute_program(source)
|
|
@@ -516,6 +694,28 @@ class CSSLRuntime:
|
|
|
516
694
|
func_name = func_info.get('name')
|
|
517
695
|
service.functions[func_name] = child
|
|
518
696
|
self.scope.set(func_name, child)
|
|
697
|
+
# v4.8.6: class at top level
|
|
698
|
+
elif child.type == 'class':
|
|
699
|
+
class_info = child.value
|
|
700
|
+
class_name = class_info.get('name') if isinstance(class_info, dict) else class_info
|
|
701
|
+
# Execute class definition to register it
|
|
702
|
+
class_def = self._exec_class(child)
|
|
703
|
+
if class_def:
|
|
704
|
+
service.classes[class_name] = class_def
|
|
705
|
+
# v4.8.6: enum at top level
|
|
706
|
+
elif child.type == 'enum':
|
|
707
|
+
enum_info = child.value
|
|
708
|
+
enum_name = enum_info.get('name') if isinstance(enum_info, dict) else enum_info
|
|
709
|
+
enum_def = self._exec_enum(child)
|
|
710
|
+
if enum_def:
|
|
711
|
+
service.enums[enum_name] = enum_def
|
|
712
|
+
# v4.8.6: namespace at top level
|
|
713
|
+
elif child.type == 'namespace':
|
|
714
|
+
ns_info = child.value
|
|
715
|
+
ns_name = ns_info.get('name') if isinstance(ns_info, dict) else ns_info
|
|
716
|
+
ns_def = self._exec_namespace(child)
|
|
717
|
+
if ns_def:
|
|
718
|
+
service.namespaces[ns_name] = ns_def
|
|
519
719
|
|
|
520
720
|
return service
|
|
521
721
|
|
|
@@ -541,6 +741,8 @@ class CSSLRuntime:
|
|
|
541
741
|
self._exec_class(child)
|
|
542
742
|
elif child.type == 'enum':
|
|
543
743
|
self._exec_enum(child)
|
|
744
|
+
elif child.type == 'namespace':
|
|
745
|
+
self._exec_namespace(child)
|
|
544
746
|
elif child.type == 'bytearrayed':
|
|
545
747
|
self._exec_bytearrayed(child)
|
|
546
748
|
elif child.type == 'function':
|
|
@@ -941,6 +1143,196 @@ class CSSLRuntime:
|
|
|
941
1143
|
|
|
942
1144
|
return bytearrayed_obj
|
|
943
1145
|
|
|
1146
|
+
def _execute_bytearrayed_function(self, func_node: ASTNode) -> Any:
|
|
1147
|
+
"""Execute a function with bytearrayed modifier (v4.7).
|
|
1148
|
+
|
|
1149
|
+
Bytearrayed functions call &FuncRef() to get a value, then match
|
|
1150
|
+
against case patterns to determine which body to execute.
|
|
1151
|
+
|
|
1152
|
+
Syntax:
|
|
1153
|
+
bytearrayed define Localize() {
|
|
1154
|
+
case "en": &GetLang() {
|
|
1155
|
+
return "Hello";
|
|
1156
|
+
}
|
|
1157
|
+
case "de": &GetLang() {
|
|
1158
|
+
return "Hallo";
|
|
1159
|
+
}
|
|
1160
|
+
default: {
|
|
1161
|
+
return "Unknown";
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
"""
|
|
1165
|
+
case_blocks = []
|
|
1166
|
+
default_node = None
|
|
1167
|
+
func_ref_stmts = [] # v4.8.8: Collect &func; statements before case/default
|
|
1168
|
+
|
|
1169
|
+
# Collect case/default blocks and func_ref statements
|
|
1170
|
+
for child in func_node.children:
|
|
1171
|
+
if not self._running:
|
|
1172
|
+
break
|
|
1173
|
+
if child.type == 'case':
|
|
1174
|
+
case_blocks.append(child)
|
|
1175
|
+
elif child.type == 'default':
|
|
1176
|
+
default_node = child
|
|
1177
|
+
elif child.type == 'func_ref':
|
|
1178
|
+
# v4.8.8: Collect func_ref statements (&n; &b(100);)
|
|
1179
|
+
func_ref_stmts.append(child)
|
|
1180
|
+
elif child.type == 'expression' and hasattr(child, 'value'):
|
|
1181
|
+
# Check if expression is a func_ref (ampersand expression)
|
|
1182
|
+
expr_val = child.value
|
|
1183
|
+
if hasattr(expr_val, 'type') and expr_val.type == 'func_ref':
|
|
1184
|
+
func_ref_stmts.append(expr_val)
|
|
1185
|
+
|
|
1186
|
+
# v4.8.8: Execute all func_ref statements and collect results
|
|
1187
|
+
collected_results = []
|
|
1188
|
+
for ref_stmt in func_ref_stmts:
|
|
1189
|
+
ref_info = ref_stmt.value if hasattr(ref_stmt, 'value') else ref_stmt
|
|
1190
|
+
if isinstance(ref_info, dict):
|
|
1191
|
+
func_name = ref_info.get('name')
|
|
1192
|
+
func_args = ref_info.get('args', [])
|
|
1193
|
+
else:
|
|
1194
|
+
func_name = str(ref_info)
|
|
1195
|
+
func_args = []
|
|
1196
|
+
|
|
1197
|
+
eval_args = [self._evaluate(a) for a in func_args] if func_args else []
|
|
1198
|
+
|
|
1199
|
+
# Call the function
|
|
1200
|
+
try:
|
|
1201
|
+
result = None
|
|
1202
|
+
func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
1203
|
+
if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
|
|
1204
|
+
result = self._call_function(func_def, eval_args)
|
|
1205
|
+
elif callable(func_def):
|
|
1206
|
+
result = func_def(*eval_args)
|
|
1207
|
+
except Exception as e:
|
|
1208
|
+
result = None
|
|
1209
|
+
|
|
1210
|
+
collected_results.append(result)
|
|
1211
|
+
|
|
1212
|
+
# Process each case block
|
|
1213
|
+
for case_node in case_blocks:
|
|
1214
|
+
case_value = case_node.value
|
|
1215
|
+
if not isinstance(case_value, dict):
|
|
1216
|
+
continue
|
|
1217
|
+
|
|
1218
|
+
patterns = case_value.get('patterns', [])
|
|
1219
|
+
func_refs = case_value.get('func_refs', [])
|
|
1220
|
+
body = case_value.get('body', [])
|
|
1221
|
+
|
|
1222
|
+
# v4.8.8: If we have collected results from &func; statements,
|
|
1223
|
+
# match patterns against those results (tuple pattern matching)
|
|
1224
|
+
if collected_results and not func_refs:
|
|
1225
|
+
# Match collected_results against patterns
|
|
1226
|
+
all_match = True
|
|
1227
|
+
if isinstance(patterns, list) and len(patterns) == 1 and isinstance(patterns[0], tuple):
|
|
1228
|
+
# Tuple pattern: case {0, 0}: -> patterns = [(0, 0)]
|
|
1229
|
+
expected = list(patterns[0])
|
|
1230
|
+
if len(expected) == len(collected_results):
|
|
1231
|
+
for i, (exp, got) in enumerate(zip(expected, collected_results)):
|
|
1232
|
+
if exp != got:
|
|
1233
|
+
all_match = False
|
|
1234
|
+
break
|
|
1235
|
+
else:
|
|
1236
|
+
all_match = False
|
|
1237
|
+
elif len(patterns) == len(collected_results):
|
|
1238
|
+
# Comma-separated patterns: case 0, 0: -> patterns = [0, 0]
|
|
1239
|
+
for i, (exp, got) in enumerate(zip(patterns, collected_results)):
|
|
1240
|
+
if exp != got:
|
|
1241
|
+
all_match = False
|
|
1242
|
+
break
|
|
1243
|
+
else:
|
|
1244
|
+
all_match = False
|
|
1245
|
+
else:
|
|
1246
|
+
# Original behavior: execute func_refs from case and match
|
|
1247
|
+
all_match = True
|
|
1248
|
+
for i, func_ref in enumerate(func_refs):
|
|
1249
|
+
func_name = func_ref.get('name')
|
|
1250
|
+
func_args = func_ref.get('args', [])
|
|
1251
|
+
eval_args = [self._evaluate(a) for a in func_args]
|
|
1252
|
+
|
|
1253
|
+
try:
|
|
1254
|
+
result = None
|
|
1255
|
+
if self._current_instance and hasattr(self._current_instance, 'get_method'):
|
|
1256
|
+
method = self._current_instance.get_method(func_name)
|
|
1257
|
+
if method:
|
|
1258
|
+
result = self._call_method(self._current_instance, method, eval_args)
|
|
1259
|
+
else:
|
|
1260
|
+
func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
1261
|
+
if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
|
|
1262
|
+
result = self._call_function(func_def, eval_args)
|
|
1263
|
+
elif callable(func_def):
|
|
1264
|
+
result = func_def(*eval_args)
|
|
1265
|
+
else:
|
|
1266
|
+
func_def = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
1267
|
+
if func_def and hasattr(func_def, 'type') and func_def.type == 'function':
|
|
1268
|
+
result = self._call_function(func_def, eval_args)
|
|
1269
|
+
elif callable(func_def):
|
|
1270
|
+
result = func_def(*eval_args)
|
|
1271
|
+
except Exception as e:
|
|
1272
|
+
result = None
|
|
1273
|
+
|
|
1274
|
+
if i < len(patterns):
|
|
1275
|
+
pattern = patterns[i]
|
|
1276
|
+
if result != pattern:
|
|
1277
|
+
all_match = False
|
|
1278
|
+
break
|
|
1279
|
+
|
|
1280
|
+
if all_match:
|
|
1281
|
+
# Execute the case body
|
|
1282
|
+
for stmt in body:
|
|
1283
|
+
try:
|
|
1284
|
+
self._execute_node(stmt)
|
|
1285
|
+
except CSSLReturn as ret:
|
|
1286
|
+
return ret.value
|
|
1287
|
+
return None
|
|
1288
|
+
|
|
1289
|
+
# No case matched - execute default
|
|
1290
|
+
if default_node and isinstance(default_node.value, dict):
|
|
1291
|
+
body = default_node.value.get('body', [])
|
|
1292
|
+
for stmt in body:
|
|
1293
|
+
try:
|
|
1294
|
+
self._execute_node(stmt)
|
|
1295
|
+
except CSSLReturn as ret:
|
|
1296
|
+
return ret.value
|
|
1297
|
+
|
|
1298
|
+
return None
|
|
1299
|
+
|
|
1300
|
+
def _match_bytearrayed_pattern(self, pattern: Any, values: List[Any]) -> bool:
|
|
1301
|
+
"""Match a pattern against collected bytearrayed values.
|
|
1302
|
+
|
|
1303
|
+
Patterns can be:
|
|
1304
|
+
- List/tuple: [10, 7] matches if values[0]==10 and values[1]==7
|
|
1305
|
+
- Dict with values: evaluates expressions
|
|
1306
|
+
- Single value: matches first collected value
|
|
1307
|
+
- Wildcard '_': matches any value
|
|
1308
|
+
"""
|
|
1309
|
+
if pattern is None:
|
|
1310
|
+
return False
|
|
1311
|
+
|
|
1312
|
+
# Convert pattern to list if needed
|
|
1313
|
+
if isinstance(pattern, (list, tuple)):
|
|
1314
|
+
pattern_list = list(pattern)
|
|
1315
|
+
elif isinstance(pattern, dict):
|
|
1316
|
+
# Dict patterns - extract values
|
|
1317
|
+
pattern_list = list(pattern.values())
|
|
1318
|
+
else:
|
|
1319
|
+
pattern_list = [pattern]
|
|
1320
|
+
|
|
1321
|
+
# Match each pattern element against collected values
|
|
1322
|
+
for i, pat_val in enumerate(pattern_list):
|
|
1323
|
+
if i >= len(values):
|
|
1324
|
+
return False
|
|
1325
|
+
|
|
1326
|
+
# Wildcard matches anything
|
|
1327
|
+
if pat_val == '_' or pat_val is None:
|
|
1328
|
+
continue
|
|
1329
|
+
|
|
1330
|
+
# Exact match
|
|
1331
|
+
if values[i] != pat_val:
|
|
1332
|
+
return False
|
|
1333
|
+
|
|
1334
|
+
return True
|
|
1335
|
+
|
|
944
1336
|
def _exec_class(self, node: ASTNode) -> CSSLClass:
|
|
945
1337
|
"""Execute class definition - registers class in scope.
|
|
946
1338
|
|
|
@@ -957,6 +1349,26 @@ class CSSLRuntime:
|
|
|
957
1349
|
extends_is_python = class_info.get('extends_is_python', False)
|
|
958
1350
|
overwrites_class_name = class_info.get('overwrites')
|
|
959
1351
|
overwrites_is_python = class_info.get('overwrites_is_python', False)
|
|
1352
|
+
uses_memory = class_info.get('uses_memory') # v4.9.0
|
|
1353
|
+
|
|
1354
|
+
# v4.9.0: Handle ': uses memory(address)' - deferred execution binding
|
|
1355
|
+
# Class constructor is hooked to the host's execution
|
|
1356
|
+
if uses_memory:
|
|
1357
|
+
host_obj = self._evaluate(uses_memory)
|
|
1358
|
+
# Convert to hashable key - use name for functions, id() for others
|
|
1359
|
+
if isinstance(host_obj, ASTNode):
|
|
1360
|
+
host_key = host_obj.value.get('name') if isinstance(host_obj.value, dict) else str(id(host_obj))
|
|
1361
|
+
elif isinstance(host_obj, str):
|
|
1362
|
+
host_key = host_obj
|
|
1363
|
+
else:
|
|
1364
|
+
host_key = str(id(host_obj))
|
|
1365
|
+
if not hasattr(self, '_memory_hooks'):
|
|
1366
|
+
self._memory_hooks = {}
|
|
1367
|
+
if host_key not in self._memory_hooks:
|
|
1368
|
+
self._memory_hooks[host_key] = []
|
|
1369
|
+
self._memory_hooks[host_key].append(('class', node))
|
|
1370
|
+
# Continue to register the class normally so it can be referenced
|
|
1371
|
+
# Hooks execute when the host is called
|
|
960
1372
|
|
|
961
1373
|
# Resolve parent class if extends is specified
|
|
962
1374
|
parent_class = None
|
|
@@ -1023,10 +1435,16 @@ class CSSLRuntime:
|
|
|
1023
1435
|
# Add transformed children to node's children
|
|
1024
1436
|
node.children = transformed_children
|
|
1025
1437
|
|
|
1438
|
+
destructors = [] # v4.8.8: Store destructors (constr ~Name())
|
|
1439
|
+
|
|
1026
1440
|
for child in node.children:
|
|
1027
1441
|
if child.type == 'constructor':
|
|
1028
|
-
#
|
|
1029
|
-
|
|
1442
|
+
# v4.8.8: Check if this is a destructor (~Name)
|
|
1443
|
+
if child.value.get('is_destructor'):
|
|
1444
|
+
destructors.append(child)
|
|
1445
|
+
else:
|
|
1446
|
+
# New-style constructor from 'constr' keyword
|
|
1447
|
+
constructors.append(child)
|
|
1030
1448
|
|
|
1031
1449
|
elif child.type == 'function':
|
|
1032
1450
|
# This is a method or old-style constructor
|
|
@@ -1061,6 +1479,7 @@ class CSSLRuntime:
|
|
|
1061
1479
|
)
|
|
1062
1480
|
# Store additional constructor info
|
|
1063
1481
|
class_def.constructors = constructors # Multiple constructors from 'constr' keyword
|
|
1482
|
+
class_def.destructors = destructors # v4.8.8: Destructors (constr ~Name())
|
|
1064
1483
|
class_def.class_params = class_params # Class-level constructor parameters
|
|
1065
1484
|
class_def.extends_args = extends_args # Arguments to pass to parent constructor
|
|
1066
1485
|
|
|
@@ -1131,8 +1550,8 @@ class CSSLRuntime:
|
|
|
1131
1550
|
# Try setting on class instead
|
|
1132
1551
|
try:
|
|
1133
1552
|
setattr(target.__class__, method_name, wrapper)
|
|
1134
|
-
except:
|
|
1135
|
-
pass
|
|
1553
|
+
except (AttributeError, TypeError):
|
|
1554
|
+
pass # Can't set attribute on immutable type
|
|
1136
1555
|
elif isinstance(target, CSSLClass):
|
|
1137
1556
|
# CSSL class - directly replace methods
|
|
1138
1557
|
for method_name, method_node in methods_to_overwrite.items():
|
|
@@ -1147,8 +1566,8 @@ class CSSLRuntime:
|
|
|
1147
1566
|
except AttributeError:
|
|
1148
1567
|
try:
|
|
1149
1568
|
setattr(py_obj.__class__, method_name, wrapper)
|
|
1150
|
-
except:
|
|
1151
|
-
pass
|
|
1569
|
+
except (AttributeError, TypeError):
|
|
1570
|
+
pass # Can't set attribute on immutable type
|
|
1152
1571
|
|
|
1153
1572
|
def _create_method_wrapper(self, method_node: ASTNode, instance: Any):
|
|
1154
1573
|
"""Create a Python-callable wrapper for a CSSL method that works with an instance."""
|
|
@@ -1163,6 +1582,64 @@ class CSSLRuntime:
|
|
|
1163
1582
|
self._current_instance = old_instance
|
|
1164
1583
|
return wrapper
|
|
1165
1584
|
|
|
1585
|
+
def _exec_namespace(self, node: ASTNode) -> 'CSSLNamespace':
|
|
1586
|
+
"""Execute namespace definition - registers namespace in scope.
|
|
1587
|
+
|
|
1588
|
+
Namespaces group functions, classes, and nested namespaces together,
|
|
1589
|
+
accessible via the :: operator (e.g., mylib::myFunc()).
|
|
1590
|
+
|
|
1591
|
+
Syntax:
|
|
1592
|
+
namespace mylib {
|
|
1593
|
+
void myFunc() { ... }
|
|
1594
|
+
class MyClass { ... }
|
|
1595
|
+
namespace nested { ... }
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
Access: mylib::myFunc(), mylib::MyClass, mylib::nested::innerFunc()
|
|
1599
|
+
"""
|
|
1600
|
+
ns_name = node.value.get('name') if isinstance(node.value, dict) else node.value
|
|
1601
|
+
|
|
1602
|
+
# Create namespace object
|
|
1603
|
+
namespace = CSSLNamespace(ns_name)
|
|
1604
|
+
|
|
1605
|
+
# Process namespace members
|
|
1606
|
+
for child in node.children:
|
|
1607
|
+
if child.type == 'function':
|
|
1608
|
+
# Register function in namespace
|
|
1609
|
+
func_info = child.value
|
|
1610
|
+
func_name = func_info.get('name')
|
|
1611
|
+
namespace.functions[func_name] = child
|
|
1612
|
+
elif child.type == 'class':
|
|
1613
|
+
# Register class in namespace
|
|
1614
|
+
class_info = child.value
|
|
1615
|
+
class_name = class_info.get('name')
|
|
1616
|
+
cssl_class = self._exec_class(child)
|
|
1617
|
+
namespace.classes[class_name] = cssl_class
|
|
1618
|
+
elif child.type == 'enum':
|
|
1619
|
+
# Register enum in namespace
|
|
1620
|
+
enum_info = child.value
|
|
1621
|
+
enum_name = enum_info.get('name')
|
|
1622
|
+
self._exec_enum(child)
|
|
1623
|
+
namespace.enums[enum_name] = self.scope.get(enum_name)
|
|
1624
|
+
elif child.type == 'struct':
|
|
1625
|
+
# Register struct in namespace
|
|
1626
|
+
struct_info = child.value
|
|
1627
|
+
if isinstance(struct_info, dict):
|
|
1628
|
+
struct_name = struct_info.get('name', '')
|
|
1629
|
+
else:
|
|
1630
|
+
struct_name = struct_info
|
|
1631
|
+
namespace.structs[struct_name] = child
|
|
1632
|
+
elif child.type == 'namespace':
|
|
1633
|
+
# Nested namespace
|
|
1634
|
+
nested_ns = self._exec_namespace(child)
|
|
1635
|
+
namespace.namespaces[nested_ns.name] = nested_ns
|
|
1636
|
+
|
|
1637
|
+
# Register namespace in both local and global scope for :: access
|
|
1638
|
+
self.scope.set(ns_name, namespace)
|
|
1639
|
+
self.global_scope.set(ns_name, namespace)
|
|
1640
|
+
|
|
1641
|
+
return namespace
|
|
1642
|
+
|
|
1166
1643
|
def _transform_and_parse_class_body(self, raw_body: str, language: str, class_name: str) -> list:
|
|
1167
1644
|
"""Transform source code from another language to CSSL and parse as class body.
|
|
1168
1645
|
|
|
@@ -1293,6 +1770,48 @@ class CSSLRuntime:
|
|
|
1293
1770
|
extends_is_python = func_info.get('extends_is_python', False)
|
|
1294
1771
|
overwrites_func = func_info.get('overwrites')
|
|
1295
1772
|
overwrites_is_python = func_info.get('overwrites_is_python', False)
|
|
1773
|
+
uses_memory = func_info.get('uses_memory') # v4.9.0
|
|
1774
|
+
|
|
1775
|
+
# v4.9.0: Handle ': uses memory(address)' - deferred execution binding
|
|
1776
|
+
# Function is not executed immediately but deferred until the host is called
|
|
1777
|
+
# Calling this function directly is a no-op - it only triggers when the host is called
|
|
1778
|
+
if uses_memory:
|
|
1779
|
+
# Evaluate the address expression (can be function ref, address string, etc.)
|
|
1780
|
+
host_obj = self._evaluate(uses_memory)
|
|
1781
|
+
|
|
1782
|
+
# Resolve the actual target function to get its name for hook lookup
|
|
1783
|
+
host_key = None
|
|
1784
|
+
from .cssl_types import Address
|
|
1785
|
+
if isinstance(host_obj, Address):
|
|
1786
|
+
# Address type - reflect to get the original object
|
|
1787
|
+
reflected = host_obj.reflect()
|
|
1788
|
+
if isinstance(reflected, ASTNode) and reflected.type == 'function':
|
|
1789
|
+
host_key = reflected.value.get('name')
|
|
1790
|
+
elif reflected is not None:
|
|
1791
|
+
host_key = str(id(reflected))
|
|
1792
|
+
elif isinstance(host_obj, ASTNode):
|
|
1793
|
+
# Direct function reference
|
|
1794
|
+
host_key = host_obj.value.get('name') if isinstance(host_obj.value, dict) else str(id(host_obj))
|
|
1795
|
+
elif isinstance(host_obj, str):
|
|
1796
|
+
host_key = host_obj # Address string like "0x..."
|
|
1797
|
+
else:
|
|
1798
|
+
host_key = str(id(host_obj))
|
|
1799
|
+
|
|
1800
|
+
if host_key:
|
|
1801
|
+
# Store the function as a memory hook
|
|
1802
|
+
if not hasattr(self, '_memory_hooks'):
|
|
1803
|
+
self._memory_hooks = {} # key -> list of hooked functions
|
|
1804
|
+
if host_key not in self._memory_hooks:
|
|
1805
|
+
self._memory_hooks[host_key] = []
|
|
1806
|
+
self._memory_hooks[host_key].append(node)
|
|
1807
|
+
|
|
1808
|
+
# Mark function as a hook (calling it directly is a no-op)
|
|
1809
|
+
node.value['_is_memory_hook'] = True
|
|
1810
|
+
# Register the function by name for reference
|
|
1811
|
+
self.scope.set(func_name, node)
|
|
1812
|
+
if is_global:
|
|
1813
|
+
self.global_scope.set(func_name, node)
|
|
1814
|
+
return None
|
|
1296
1815
|
|
|
1297
1816
|
# Get append/overwrite reference info (&Class::method syntax)
|
|
1298
1817
|
append_mode = func_info.get('append_mode', False)
|
|
@@ -1338,6 +1857,24 @@ class CSSLRuntime:
|
|
|
1338
1857
|
self.scope.set(overwrites_func, node)
|
|
1339
1858
|
self.global_scope.set(overwrites_func, node)
|
|
1340
1859
|
|
|
1860
|
+
# v4.9.3: Check for async modifier - wrap function in CSSLAsyncFunction
|
|
1861
|
+
modifiers = func_info.get('modifiers', [])
|
|
1862
|
+
if 'async' in modifiers:
|
|
1863
|
+
# Create async wrapper that returns a Future when called
|
|
1864
|
+
async_func = CSSLAsyncFunction(func_name, node, self)
|
|
1865
|
+
self.scope.set(func_name, async_func)
|
|
1866
|
+
if is_global:
|
|
1867
|
+
self.global_scope.set(func_name, async_func)
|
|
1868
|
+
self._promoted_globals[func_name] = async_func
|
|
1869
|
+
return None
|
|
1870
|
+
|
|
1871
|
+
# v4.9.3: Check if function is a generator (contains yield statements)
|
|
1872
|
+
# Return type 'generator' or body contains yield
|
|
1873
|
+
return_type = func_info.get('return_type', '')
|
|
1874
|
+
if return_type == 'generator' or self._contains_yield(node):
|
|
1875
|
+
# Mark as generator function - will create CSSLGenerator on call
|
|
1876
|
+
node.value['_is_generator'] = True
|
|
1877
|
+
|
|
1341
1878
|
# Register the function (local by default, global if marked)
|
|
1342
1879
|
self.scope.set(func_name, node)
|
|
1343
1880
|
if is_global:
|
|
@@ -1345,6 +1882,38 @@ class CSSLRuntime:
|
|
|
1345
1882
|
self._promoted_globals[func_name] = node
|
|
1346
1883
|
return None
|
|
1347
1884
|
|
|
1885
|
+
def _contains_yield(self, node: ASTNode) -> bool:
|
|
1886
|
+
"""Check if a function node contains yield statements."""
|
|
1887
|
+
if node is None:
|
|
1888
|
+
return False
|
|
1889
|
+
|
|
1890
|
+
def _search_yield(n):
|
|
1891
|
+
if n is None:
|
|
1892
|
+
return False
|
|
1893
|
+
if hasattr(n, 'type') and n.type == 'yield':
|
|
1894
|
+
return True
|
|
1895
|
+
if hasattr(n, 'children'):
|
|
1896
|
+
for child in n.children:
|
|
1897
|
+
if _search_yield(child):
|
|
1898
|
+
return True
|
|
1899
|
+
if hasattr(n, 'value'):
|
|
1900
|
+
# Check if value is an ASTNode (nested structure)
|
|
1901
|
+
if isinstance(n.value, dict):
|
|
1902
|
+
for v in n.value.values():
|
|
1903
|
+
if isinstance(v, ASTNode) and _search_yield(v):
|
|
1904
|
+
return True
|
|
1905
|
+
if isinstance(v, list):
|
|
1906
|
+
for item in v:
|
|
1907
|
+
if isinstance(item, ASTNode) and _search_yield(item):
|
|
1908
|
+
return True
|
|
1909
|
+
return False
|
|
1910
|
+
|
|
1911
|
+
# Search in function body (children)
|
|
1912
|
+
for child in node.children:
|
|
1913
|
+
if _search_yield(child):
|
|
1914
|
+
return True
|
|
1915
|
+
return False
|
|
1916
|
+
|
|
1348
1917
|
def _resolve_function_target(self, name: str, is_python: bool) -> Any:
|
|
1349
1918
|
"""Resolve a function target for extends/overwrites."""
|
|
1350
1919
|
if is_python:
|
|
@@ -1392,6 +1961,265 @@ class CSSLRuntime:
|
|
|
1392
1961
|
|
|
1393
1962
|
return wrapper
|
|
1394
1963
|
|
|
1964
|
+
def _create_generator(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None) -> CSSLGenerator:
|
|
1965
|
+
"""Create a CSSLGenerator for a generator function - v4.9.3
|
|
1966
|
+
|
|
1967
|
+
Generator functions are functions that contain yield statements.
|
|
1968
|
+
When called, they return a CSSLGenerator object that can be iterated.
|
|
1969
|
+
|
|
1970
|
+
Example CSSL:
|
|
1971
|
+
generator<int> define Range(int n) {
|
|
1972
|
+
int i = 0;
|
|
1973
|
+
while (i < n) {
|
|
1974
|
+
yield i;
|
|
1975
|
+
i = i + 1;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
gen = Range(5);
|
|
1980
|
+
while (gen.has_next()) {
|
|
1981
|
+
printl(gen.next());
|
|
1982
|
+
}
|
|
1983
|
+
"""
|
|
1984
|
+
func_info = func_node.value
|
|
1985
|
+
params = func_info.get('params', [])
|
|
1986
|
+
kwargs = kwargs or {}
|
|
1987
|
+
func_name = func_info.get('name', 'anonymous')
|
|
1988
|
+
|
|
1989
|
+
# Set up scope with parameters first (before creating generator)
|
|
1990
|
+
old_scope = self.scope
|
|
1991
|
+
gen_scope = Scope(parent=self.scope)
|
|
1992
|
+
|
|
1993
|
+
# Bind parameters
|
|
1994
|
+
for i, param in enumerate(params):
|
|
1995
|
+
if isinstance(param, dict):
|
|
1996
|
+
param_name = param['name']
|
|
1997
|
+
param_default = param.get('default')
|
|
1998
|
+
else:
|
|
1999
|
+
param_name = param
|
|
2000
|
+
param_default = None
|
|
2001
|
+
|
|
2002
|
+
# Get value from kwargs, args, or default
|
|
2003
|
+
if param_name in kwargs:
|
|
2004
|
+
value = kwargs[param_name]
|
|
2005
|
+
elif i < len(args):
|
|
2006
|
+
value = args[i]
|
|
2007
|
+
elif param_default is not None:
|
|
2008
|
+
self.scope = gen_scope
|
|
2009
|
+
value = self._evaluate(param_default)
|
|
2010
|
+
self.scope = old_scope
|
|
2011
|
+
else:
|
|
2012
|
+
value = None
|
|
2013
|
+
|
|
2014
|
+
gen_scope.set(param_name, value)
|
|
2015
|
+
|
|
2016
|
+
# Generator execution using a Python generator with proper state management
|
|
2017
|
+
runtime = self
|
|
2018
|
+
|
|
2019
|
+
def cssl_generator_func():
|
|
2020
|
+
# Execute generator body, properly managing scope around each yield
|
|
2021
|
+
for child in func_node.children:
|
|
2022
|
+
if not runtime._running:
|
|
2023
|
+
return
|
|
2024
|
+
|
|
2025
|
+
# Save caller's scope before entering generator scope
|
|
2026
|
+
caller_scope = runtime.scope
|
|
2027
|
+
runtime.scope = gen_scope
|
|
2028
|
+
|
|
2029
|
+
try:
|
|
2030
|
+
if child.type == 'yield':
|
|
2031
|
+
# Direct yield node
|
|
2032
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2033
|
+
runtime.scope = caller_scope # Restore before yield
|
|
2034
|
+
yield value
|
|
2035
|
+
elif child.type == 'while':
|
|
2036
|
+
# While loop - handle yield inside
|
|
2037
|
+
for val in _execute_generator_while(child):
|
|
2038
|
+
runtime.scope = caller_scope
|
|
2039
|
+
yield val
|
|
2040
|
+
elif child.type in ('for', 'c_for'):
|
|
2041
|
+
for val in _execute_generator_for(child):
|
|
2042
|
+
runtime.scope = caller_scope
|
|
2043
|
+
yield val
|
|
2044
|
+
elif child.type == 'foreach':
|
|
2045
|
+
for val in _execute_generator_foreach(child):
|
|
2046
|
+
runtime.scope = caller_scope
|
|
2047
|
+
yield val
|
|
2048
|
+
elif child.type == 'if':
|
|
2049
|
+
for val in _execute_generator_if(child):
|
|
2050
|
+
runtime.scope = caller_scope
|
|
2051
|
+
yield val
|
|
2052
|
+
elif child.type == 'return':
|
|
2053
|
+
runtime.scope = caller_scope
|
|
2054
|
+
return
|
|
2055
|
+
else:
|
|
2056
|
+
# Regular statement
|
|
2057
|
+
try:
|
|
2058
|
+
runtime._execute_node(child)
|
|
2059
|
+
except CSSLYield as y:
|
|
2060
|
+
runtime.scope = caller_scope
|
|
2061
|
+
yield y.value
|
|
2062
|
+
except CSSLReturn:
|
|
2063
|
+
runtime.scope = caller_scope
|
|
2064
|
+
return
|
|
2065
|
+
finally:
|
|
2066
|
+
runtime.scope = caller_scope
|
|
2067
|
+
|
|
2068
|
+
def _execute_generator_while(node):
|
|
2069
|
+
while runtime._running:
|
|
2070
|
+
# Always restore gen_scope at start of each iteration (may have been changed by yield)
|
|
2071
|
+
runtime.scope = gen_scope
|
|
2072
|
+
if not runtime._evaluate(node.value.get('condition')):
|
|
2073
|
+
break
|
|
2074
|
+
try:
|
|
2075
|
+
for child in node.children:
|
|
2076
|
+
runtime.scope = gen_scope # Ensure gen_scope for all operations
|
|
2077
|
+
if child.type == 'yield':
|
|
2078
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2079
|
+
yield value
|
|
2080
|
+
else:
|
|
2081
|
+
try:
|
|
2082
|
+
runtime._execute_node(child)
|
|
2083
|
+
except CSSLYield as y:
|
|
2084
|
+
yield y.value
|
|
2085
|
+
except CSSLReturn:
|
|
2086
|
+
return
|
|
2087
|
+
except CSSLBreak:
|
|
2088
|
+
break
|
|
2089
|
+
except CSSLContinue:
|
|
2090
|
+
continue
|
|
2091
|
+
|
|
2092
|
+
def _execute_generator_for(node):
|
|
2093
|
+
if node.type == 'c_for':
|
|
2094
|
+
init = node.value.get('init')
|
|
2095
|
+
condition = node.value.get('condition')
|
|
2096
|
+
update = node.value.get('update')
|
|
2097
|
+
|
|
2098
|
+
runtime.scope = gen_scope
|
|
2099
|
+
if init:
|
|
2100
|
+
runtime._execute_node(init)
|
|
2101
|
+
|
|
2102
|
+
while runtime._running:
|
|
2103
|
+
# Restore gen_scope at start of each iteration
|
|
2104
|
+
runtime.scope = gen_scope
|
|
2105
|
+
if condition is not None and not runtime._evaluate(condition):
|
|
2106
|
+
break
|
|
2107
|
+
try:
|
|
2108
|
+
for child in node.children:
|
|
2109
|
+
runtime.scope = gen_scope
|
|
2110
|
+
if child.type == 'yield':
|
|
2111
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2112
|
+
yield value
|
|
2113
|
+
else:
|
|
2114
|
+
try:
|
|
2115
|
+
runtime._execute_node(child)
|
|
2116
|
+
except CSSLYield as y:
|
|
2117
|
+
yield y.value
|
|
2118
|
+
except CSSLReturn:
|
|
2119
|
+
return
|
|
2120
|
+
except CSSLBreak:
|
|
2121
|
+
break
|
|
2122
|
+
except CSSLContinue:
|
|
2123
|
+
pass
|
|
2124
|
+
|
|
2125
|
+
runtime.scope = gen_scope
|
|
2126
|
+
if update:
|
|
2127
|
+
runtime._evaluate(update)
|
|
2128
|
+
else:
|
|
2129
|
+
runtime.scope = gen_scope
|
|
2130
|
+
var_name = node.value.get('var')
|
|
2131
|
+
start = int(runtime._evaluate(node.value.get('start')))
|
|
2132
|
+
end = int(runtime._evaluate(node.value.get('end')))
|
|
2133
|
+
step_node = node.value.get('step')
|
|
2134
|
+
step = int(runtime._evaluate(step_node)) if step_node else 1
|
|
2135
|
+
|
|
2136
|
+
for i in range(start, end, step):
|
|
2137
|
+
if not runtime._running:
|
|
2138
|
+
break
|
|
2139
|
+
runtime.scope = gen_scope # Restore at start of each iteration
|
|
2140
|
+
gen_scope.set(var_name, i)
|
|
2141
|
+
try:
|
|
2142
|
+
for child in node.children:
|
|
2143
|
+
runtime.scope = gen_scope
|
|
2144
|
+
if child.type == 'yield':
|
|
2145
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2146
|
+
yield value
|
|
2147
|
+
else:
|
|
2148
|
+
try:
|
|
2149
|
+
runtime._execute_node(child)
|
|
2150
|
+
except CSSLYield as y:
|
|
2151
|
+
yield y.value
|
|
2152
|
+
except CSSLReturn:
|
|
2153
|
+
return
|
|
2154
|
+
except CSSLBreak:
|
|
2155
|
+
break
|
|
2156
|
+
except CSSLContinue:
|
|
2157
|
+
continue
|
|
2158
|
+
|
|
2159
|
+
def _execute_generator_foreach(node):
|
|
2160
|
+
runtime.scope = gen_scope
|
|
2161
|
+
var_name = node.value.get('var')
|
|
2162
|
+
iterable = runtime._evaluate(node.value.get('iterable'))
|
|
2163
|
+
|
|
2164
|
+
if hasattr(iterable, '__iter__'):
|
|
2165
|
+
for item in iterable:
|
|
2166
|
+
if not runtime._running:
|
|
2167
|
+
break
|
|
2168
|
+
runtime.scope = gen_scope # Restore at start of each iteration
|
|
2169
|
+
gen_scope.set(var_name, item)
|
|
2170
|
+
try:
|
|
2171
|
+
for child in node.children:
|
|
2172
|
+
runtime.scope = gen_scope
|
|
2173
|
+
if child.type == 'yield':
|
|
2174
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2175
|
+
yield value
|
|
2176
|
+
else:
|
|
2177
|
+
try:
|
|
2178
|
+
runtime._execute_node(child)
|
|
2179
|
+
except CSSLYield as y:
|
|
2180
|
+
yield y.value
|
|
2181
|
+
except CSSLReturn:
|
|
2182
|
+
return
|
|
2183
|
+
except CSSLBreak:
|
|
2184
|
+
break
|
|
2185
|
+
except CSSLContinue:
|
|
2186
|
+
continue
|
|
2187
|
+
|
|
2188
|
+
def _execute_generator_if(node):
|
|
2189
|
+
runtime.scope = gen_scope
|
|
2190
|
+
condition = runtime._evaluate(node.value.get('condition'))
|
|
2191
|
+
if condition:
|
|
2192
|
+
for child in node.children:
|
|
2193
|
+
runtime.scope = gen_scope
|
|
2194
|
+
if child.type == 'yield':
|
|
2195
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2196
|
+
yield value
|
|
2197
|
+
else:
|
|
2198
|
+
try:
|
|
2199
|
+
runtime._execute_node(child)
|
|
2200
|
+
except CSSLYield as y:
|
|
2201
|
+
yield y.value
|
|
2202
|
+
except CSSLReturn:
|
|
2203
|
+
return
|
|
2204
|
+
else:
|
|
2205
|
+
else_branch = node.value.get('else_branch')
|
|
2206
|
+
if else_branch:
|
|
2207
|
+
branches = else_branch if isinstance(else_branch, list) else [else_branch]
|
|
2208
|
+
for child in branches:
|
|
2209
|
+
runtime.scope = gen_scope
|
|
2210
|
+
if hasattr(child, 'type') and child.type == 'yield':
|
|
2211
|
+
value = runtime._evaluate(child.value) if child.value else None
|
|
2212
|
+
yield value
|
|
2213
|
+
elif hasattr(child, 'type'):
|
|
2214
|
+
try:
|
|
2215
|
+
runtime._execute_node(child)
|
|
2216
|
+
except CSSLYield as y:
|
|
2217
|
+
yield y.value
|
|
2218
|
+
except CSSLReturn:
|
|
2219
|
+
return
|
|
2220
|
+
|
|
2221
|
+
return CSSLGenerator(func_name, cssl_generator_func())
|
|
2222
|
+
|
|
1395
2223
|
def _overwrite_target(self, ref_class: str, ref_member: str, replacement_node: ASTNode):
|
|
1396
2224
|
"""Overwrite a class method or function with the replacement.
|
|
1397
2225
|
|
|
@@ -1470,6 +2298,13 @@ class CSSLRuntime:
|
|
|
1470
2298
|
# Handle CSSL class method overwrite
|
|
1471
2299
|
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1472
2300
|
|
|
2301
|
+
# v4.9.2: Handle &builtinName syntax (stored as __builtins__::builtinName)
|
|
2302
|
+
if ref_class == '__builtins__' and ref_member:
|
|
2303
|
+
# Treat as standalone builtin function overwrite
|
|
2304
|
+
ref_class = ref_member
|
|
2305
|
+
ref_member = None
|
|
2306
|
+
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
2307
|
+
|
|
1473
2308
|
# v4.2.3: Handle standalone function reference (&functionName) including builtins
|
|
1474
2309
|
if ref_member is None:
|
|
1475
2310
|
# Check if this is a builtin function
|
|
@@ -1586,8 +2421,8 @@ class CSSLRuntime:
|
|
|
1586
2421
|
if callable(_saved_original):
|
|
1587
2422
|
try:
|
|
1588
2423
|
result = _saved_original(*args, **kwargs)
|
|
1589
|
-
except:
|
|
1590
|
-
pass
|
|
2424
|
+
except Exception:
|
|
2425
|
+
pass # Continue to appended code even if original fails
|
|
1591
2426
|
# Then run appended code - disable append_mode to prevent recursion
|
|
1592
2427
|
append_node.value['append_mode'] = False
|
|
1593
2428
|
try:
|
|
@@ -1602,15 +2437,106 @@ class CSSLRuntime:
|
|
|
1602
2437
|
|
|
1603
2438
|
# Handle CSSL class method append
|
|
1604
2439
|
target_class = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
1605
|
-
|
|
1606
|
-
|
|
2440
|
+
|
|
2441
|
+
# v4.9.2: Handle &builtinName ++ syntax (stored as __builtins__::builtinName)
|
|
2442
|
+
is_builtin_hook = False
|
|
2443
|
+
if ref_class == '__builtins__' and ref_member:
|
|
2444
|
+
# Treat as standalone builtin function append
|
|
2445
|
+
ref_class = ref_member
|
|
2446
|
+
ref_member = None
|
|
2447
|
+
is_builtin_hook = True
|
|
2448
|
+
# For builtin hooks, force standalone path (don't use target_class lookup)
|
|
2449
|
+
target_class = None
|
|
2450
|
+
# v4.9.2: Also check if ref_class is a direct builtin name (not via __builtins__)
|
|
2451
|
+
# This handles cases like &address ++ where address is a builtin
|
|
2452
|
+
elif ref_member is None and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
|
|
2453
|
+
is_builtin_hook = True
|
|
2454
|
+
# Force standalone path for builtin hooks
|
|
2455
|
+
target_class = None
|
|
2456
|
+
|
|
2457
|
+
if target_class is None or is_builtin_hook:
|
|
2458
|
+
# Standalone function append (or builtin hook)
|
|
1607
2459
|
if ref_member is None:
|
|
1608
|
-
|
|
2460
|
+
# v4.9.2: For builtin hooks, get from builtins._functions directly
|
|
2461
|
+
if is_builtin_hook and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
|
|
2462
|
+
original_func = self.builtins._functions.get(ref_class)
|
|
2463
|
+
# Store original before we overwrite
|
|
2464
|
+
if original_func and ref_class not in self._original_functions:
|
|
2465
|
+
self._original_functions[ref_class] = original_func
|
|
2466
|
+
else:
|
|
2467
|
+
original_func = self.scope.get(ref_class) or self.global_scope.get(ref_class)
|
|
2468
|
+
# For non-builtin, check _original_functions as fallback
|
|
2469
|
+
if original_func is None:
|
|
2470
|
+
if hasattr(self, '_original_functions'):
|
|
2471
|
+
original_func = self._original_functions.get(ref_class)
|
|
2472
|
+
if original_func is None and hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
|
|
2473
|
+
original_func = self.builtins._functions.get(ref_class)
|
|
2474
|
+
if original_func and ref_class not in self._original_functions:
|
|
2475
|
+
self._original_functions[ref_class] = original_func
|
|
2476
|
+
|
|
1609
2477
|
if original_func:
|
|
1610
2478
|
# Store original in append_node so it can run first
|
|
1611
2479
|
append_node.value['_original_func'] = original_func
|
|
1612
2480
|
self.scope.set(ref_class, append_node)
|
|
1613
2481
|
self.global_scope.set(ref_class, append_node)
|
|
2482
|
+
|
|
2483
|
+
# v4.9.2: Also update builtins._functions for builtin hooks
|
|
2484
|
+
# For builtin hooks, DON'T store in scope - only use the wrapper in builtins._functions
|
|
2485
|
+
# This ensures all calls go through the wrapper which has proper re-entry protection
|
|
2486
|
+
if hasattr(self, 'builtins') and self.builtins.has_function(ref_class):
|
|
2487
|
+
# Remove from scope so all lookups go through builtins
|
|
2488
|
+
self.scope.set(ref_class, None)
|
|
2489
|
+
self.global_scope.set(ref_class, None)
|
|
2490
|
+
# v4.9.2: Also clear from _promoted_globals since @var=builtin stores there too
|
|
2491
|
+
if ref_class in self._promoted_globals:
|
|
2492
|
+
del self._promoted_globals[ref_class]
|
|
2493
|
+
|
|
2494
|
+
def make_wrapper(node, runtime, builtin_name, orig_func):
|
|
2495
|
+
def wrapper(*args, **kwargs):
|
|
2496
|
+
# v4.9.2: Prevent infinite recursion - if hook is already executing,
|
|
2497
|
+
# call the original builtin directly instead of the hook
|
|
2498
|
+
if builtin_name in runtime._hook_executing:
|
|
2499
|
+
# Already in hook, call original directly
|
|
2500
|
+
if callable(orig_func):
|
|
2501
|
+
return orig_func(*args, **kwargs)
|
|
2502
|
+
return None
|
|
2503
|
+
# Mark hook as executing
|
|
2504
|
+
runtime._hook_executing.add(builtin_name)
|
|
2505
|
+
try:
|
|
2506
|
+
# v4.9.2: Append mode - run ORIGINAL first, capture result
|
|
2507
|
+
original_result = None
|
|
2508
|
+
if callable(orig_func):
|
|
2509
|
+
try:
|
|
2510
|
+
original_result = orig_func(*args, **kwargs)
|
|
2511
|
+
except Exception:
|
|
2512
|
+
pass # Continue to hook even if original fails
|
|
2513
|
+
# Store original result so hook can access it via %<name>_result or _result
|
|
2514
|
+
runtime._original_functions[f'{builtin_name}_result'] = original_result
|
|
2515
|
+
# Also set _result in scope for easy access inside hook
|
|
2516
|
+
runtime.scope.set('_result', original_result)
|
|
2517
|
+
# v4.9.2: Populate _hook_locals with args for local:: access
|
|
2518
|
+
old_hook_locals = runtime._hook_locals
|
|
2519
|
+
runtime._hook_locals = {'_result': original_result, '_args': args, '_kwargs': kwargs}
|
|
2520
|
+
# Add numbered args (local::0, local::1, etc.)
|
|
2521
|
+
for i, arg in enumerate(args):
|
|
2522
|
+
runtime._hook_locals[str(i)] = arg
|
|
2523
|
+
# Run hook body with same args
|
|
2524
|
+
hook_result = runtime._call_function(node, list(args), kwargs)
|
|
2525
|
+
# Restore previous hook locals
|
|
2526
|
+
runtime._hook_locals = old_hook_locals
|
|
2527
|
+
# Clean up _result
|
|
2528
|
+
runtime.scope.set('_result', None)
|
|
2529
|
+
# Return original result unless hook explicitly returned something
|
|
2530
|
+
if hook_result is not None:
|
|
2531
|
+
return hook_result
|
|
2532
|
+
return original_result
|
|
2533
|
+
finally:
|
|
2534
|
+
runtime._hook_executing.discard(builtin_name)
|
|
2535
|
+
# Clean up result
|
|
2536
|
+
runtime._original_functions.pop(f'{builtin_name}_result', None)
|
|
2537
|
+
return wrapper
|
|
2538
|
+
self.builtins._functions[ref_class] = make_wrapper(append_node, self, ref_class, original_func)
|
|
2539
|
+
return # Don't store in scope for builtin hooks
|
|
1614
2540
|
return
|
|
1615
2541
|
|
|
1616
2542
|
if isinstance(target_class, CSSLClass) and ref_member:
|
|
@@ -1742,12 +2668,68 @@ class CSSLRuntime:
|
|
|
1742
2668
|
instance = Dictionary(element_type)
|
|
1743
2669
|
elif type_name == 'map':
|
|
1744
2670
|
instance = Map(element_type)
|
|
2671
|
+
elif type_name == 'queue':
|
|
2672
|
+
# v4.7: Queue with optional size from element_type (queue<type, size>)
|
|
2673
|
+
queue_size = decl.get('size', 'dynamic')
|
|
2674
|
+
instance = Queue(element_type, queue_size)
|
|
2675
|
+
elif type_name == 'bit':
|
|
2676
|
+
# v4.9.0: Single bit value (0 or 1)
|
|
2677
|
+
from .cssl_types import Bit
|
|
2678
|
+
if value_node is None:
|
|
2679
|
+
instance = Bit(0)
|
|
2680
|
+
else:
|
|
2681
|
+
val = self._evaluate(value_node)
|
|
2682
|
+
if isinstance(val, Bit):
|
|
2683
|
+
# Already a Bit object (e.g., from .copy())
|
|
2684
|
+
instance = val
|
|
2685
|
+
elif isinstance(val, int) and val in (0, 1):
|
|
2686
|
+
instance = Bit(val)
|
|
2687
|
+
else:
|
|
2688
|
+
raise CSSLRuntimeError(f"Bit must be 0 or 1, got {val}", node.line)
|
|
2689
|
+
elif type_name == 'byte':
|
|
2690
|
+
# v4.9.0: Byte value (x^y notation where x=0/1, y=0-255)
|
|
2691
|
+
from .cssl_types import Byte
|
|
2692
|
+
if value_node is None:
|
|
2693
|
+
instance = Byte(0, 0)
|
|
2694
|
+
else:
|
|
2695
|
+
val = self._evaluate(value_node)
|
|
2696
|
+
if isinstance(val, Byte):
|
|
2697
|
+
instance = val
|
|
2698
|
+
elif isinstance(val, tuple) and len(val) == 2:
|
|
2699
|
+
# Tuple from byte literal parsing (base, weight)
|
|
2700
|
+
instance = Byte(val[0], val[1])
|
|
2701
|
+
elif isinstance(val, int):
|
|
2702
|
+
# Plain int - store as 1^val
|
|
2703
|
+
instance = Byte(1, val % 256)
|
|
2704
|
+
else:
|
|
2705
|
+
raise CSSLRuntimeError(f"Invalid byte value: {val}", node.line)
|
|
2706
|
+
elif type_name in ('address', 'ptr', 'pointer'):
|
|
2707
|
+
# v4.9.0: Memory address/pointer type
|
|
2708
|
+
# v4.9.3: Added 'ptr' and 'pointer' as aliases for 'address'
|
|
2709
|
+
from .cssl_types import Address
|
|
2710
|
+
if value_node is None:
|
|
2711
|
+
instance = Address() # Null address
|
|
2712
|
+
else:
|
|
2713
|
+
val = self._evaluate(value_node)
|
|
2714
|
+
if isinstance(val, Address):
|
|
2715
|
+
# Already an Address object
|
|
2716
|
+
instance = val
|
|
2717
|
+
elif isinstance(val, str):
|
|
2718
|
+
# Address string from memory().get("address")
|
|
2719
|
+
instance = Address(val)
|
|
2720
|
+
else:
|
|
2721
|
+
# Create address from object
|
|
2722
|
+
instance = Address(obj=val)
|
|
1745
2723
|
else:
|
|
1746
2724
|
# Default: evaluate the value or set to None
|
|
1747
2725
|
instance = self._evaluate(value_node) if value_node else None
|
|
1748
2726
|
|
|
1749
2727
|
# If there's an explicit value, use it instead
|
|
1750
|
-
|
|
2728
|
+
# v4.8.7: Removed 'array' from exclusion - arrays should also get init values
|
|
2729
|
+
# v4.8.8: Added 'instance' to exclusion - instance type already evaluates value above
|
|
2730
|
+
# v4.9.0: Added 'bit', 'byte', 'address' to exclusion - they already handle values above
|
|
2731
|
+
# v4.9.2: Added 'ptr', 'pointer' to exclusion - they already evaluate value above
|
|
2732
|
+
if value_node and type_name not in ('int', 'integer', 'string', 'str', 'float', 'double', 'bool', 'dynamic', 'json', 'instance', 'bit', 'byte', 'address', 'ptr', 'pointer'):
|
|
1751
2733
|
# For container types, the value might be initialization data
|
|
1752
2734
|
init_value = self._evaluate(value_node)
|
|
1753
2735
|
if isinstance(init_value, (list, tuple)):
|
|
@@ -1755,6 +2737,13 @@ class CSSLRuntime:
|
|
|
1755
2737
|
if non_null:
|
|
1756
2738
|
init_value = [v for v in init_value if v is not None]
|
|
1757
2739
|
instance.extend(init_value)
|
|
2740
|
+
elif isinstance(init_value, dict):
|
|
2741
|
+
# v4.8.7: Handle dict initialization: dict d = {"key": "value"}
|
|
2742
|
+
if hasattr(instance, 'update'):
|
|
2743
|
+
instance.update(init_value)
|
|
2744
|
+
elif hasattr(instance, '__setitem__'):
|
|
2745
|
+
for k, v in init_value.items():
|
|
2746
|
+
instance[k] = v
|
|
1758
2747
|
elif init_value is not None:
|
|
1759
2748
|
if hasattr(instance, 'append'):
|
|
1760
2749
|
instance.append(init_value)
|
|
@@ -1781,8 +2770,21 @@ class CSSLRuntime:
|
|
|
1781
2770
|
modifiers = decl.get('modifiers', [])
|
|
1782
2771
|
is_global = 'global' in modifiers
|
|
1783
2772
|
|
|
1784
|
-
#
|
|
1785
|
-
|
|
2773
|
+
# v4.9.2: Check for this->member declaration
|
|
2774
|
+
is_this_member = decl.get('is_this_member', False)
|
|
2775
|
+
|
|
2776
|
+
# Store in appropriate location
|
|
2777
|
+
if is_this_member:
|
|
2778
|
+
# this->member declaration - store on current instance
|
|
2779
|
+
if self._current_instance is None:
|
|
2780
|
+
raise CSSLRuntimeError("'this' used outside of class method context", node.line)
|
|
2781
|
+
if hasattr(self._current_instance, 'set_member'):
|
|
2782
|
+
self._current_instance.set_member(var_name, instance)
|
|
2783
|
+
else:
|
|
2784
|
+
setattr(self._current_instance, var_name, instance)
|
|
2785
|
+
else:
|
|
2786
|
+
# Normal variable - store in scope
|
|
2787
|
+
self.scope.set(var_name, instance)
|
|
1786
2788
|
|
|
1787
2789
|
# If global, also store in promoted_globals and global_scope
|
|
1788
2790
|
if is_global:
|
|
@@ -1955,13 +2957,14 @@ class CSSLRuntime:
|
|
|
1955
2957
|
# Fallback: execute normally
|
|
1956
2958
|
return self._execute_node(inner)
|
|
1957
2959
|
|
|
1958
|
-
def _call_function(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None) -> Any:
|
|
2960
|
+
def _call_function(self, func_node: ASTNode, args: List[Any], kwargs: Dict[str, Any] = None, _is_hook_trigger: bool = False) -> Any:
|
|
1959
2961
|
"""Call a function node with arguments (positional and named)
|
|
1960
2962
|
|
|
1961
2963
|
Args:
|
|
1962
2964
|
func_node: The function AST node
|
|
1963
2965
|
args: List of positional arguments
|
|
1964
2966
|
kwargs: Dict of named arguments (param_name -> value)
|
|
2967
|
+
_is_hook_trigger: Internal flag - True when called as a hook trigger
|
|
1965
2968
|
|
|
1966
2969
|
Supports:
|
|
1967
2970
|
define func : extends otherFunc() { ... } - Inherit local vars from otherFunc
|
|
@@ -1971,6 +2974,33 @@ class CSSLRuntime:
|
|
|
1971
2974
|
modifiers = func_info.get('modifiers', [])
|
|
1972
2975
|
kwargs = kwargs or {}
|
|
1973
2976
|
|
|
2977
|
+
# v4.9.3: If this is a generator function, return a CSSLGenerator instead of executing
|
|
2978
|
+
if func_info.get('_is_generator'):
|
|
2979
|
+
return self._create_generator(func_node, args, kwargs)
|
|
2980
|
+
|
|
2981
|
+
# v4.9.0: If this is a memory hook function and it's being called directly
|
|
2982
|
+
# (not as a hook trigger), skip execution - it's just a hook registration
|
|
2983
|
+
if func_info.get('_is_memory_hook') and not _is_hook_trigger:
|
|
2984
|
+
return None # Silent no-op for direct calls to hook functions
|
|
2985
|
+
|
|
2986
|
+
# v4.9.0: Execute memory-hooked functions first
|
|
2987
|
+
# Functions with ': uses memory(address)' are executed when their host is called
|
|
2988
|
+
if hasattr(self, '_memory_hooks'):
|
|
2989
|
+
# Look up by function name (how hooks are stored)
|
|
2990
|
+
func_name = func_info.get('name', '')
|
|
2991
|
+
if func_name in self._memory_hooks:
|
|
2992
|
+
for hooked_item in self._memory_hooks[func_name]:
|
|
2993
|
+
try:
|
|
2994
|
+
# hooked_item can be ASTNode or ('class', ASTNode) tuple
|
|
2995
|
+
if isinstance(hooked_item, tuple) and hooked_item[0] == 'class':
|
|
2996
|
+
# Class hook - instantiate the class
|
|
2997
|
+
pass # Class instantiation happens separately
|
|
2998
|
+
elif isinstance(hooked_item, ASTNode) and hooked_item.type == 'function':
|
|
2999
|
+
# Call with _is_hook_trigger=True so hook body executes
|
|
3000
|
+
self._call_function(hooked_item, args, kwargs, _is_hook_trigger=True)
|
|
3001
|
+
except Exception:
|
|
3002
|
+
pass # Continue with host even if hook fails
|
|
3003
|
+
|
|
1974
3004
|
# v4.2.5: Deferred &target replacement for non-embedded functions
|
|
1975
3005
|
# If function has &target and hasn't been applied yet, apply now on first call
|
|
1976
3006
|
append_ref_class = func_info.get('append_ref_class')
|
|
@@ -1996,8 +3026,8 @@ class CSSLRuntime:
|
|
|
1996
3026
|
# Python function - call it first to populate any state
|
|
1997
3027
|
try:
|
|
1998
3028
|
extends_resolved(*args, **kwargs)
|
|
1999
|
-
except:
|
|
2000
|
-
pass
|
|
3029
|
+
except Exception:
|
|
3030
|
+
pass # Extended function failed - continue anyway
|
|
2001
3031
|
elif hasattr(extends_resolved, 'value'):
|
|
2002
3032
|
# CSSL function - execute it in a temporary scope to get local vars
|
|
2003
3033
|
old_scope = self.scope
|
|
@@ -2012,8 +3042,8 @@ class CSSLRuntime:
|
|
|
2012
3042
|
except CSSLReturn:
|
|
2013
3043
|
# Parent returned - that's fine, we just want the local vars
|
|
2014
3044
|
pass
|
|
2015
|
-
except:
|
|
2016
|
-
pass
|
|
3045
|
+
except Exception:
|
|
3046
|
+
pass # Extended function failed - still copy local vars
|
|
2017
3047
|
finally:
|
|
2018
3048
|
# ALWAYS copy local vars (even if parent returned)
|
|
2019
3049
|
for name, value in temp_scope.variables.items():
|
|
@@ -2066,16 +3096,23 @@ class CSSLRuntime:
|
|
|
2066
3096
|
|
|
2067
3097
|
try:
|
|
2068
3098
|
# Handle append mode (++) - execute referenced function first
|
|
3099
|
+
# v4.9.2: Skip for builtin hooks - the wrapper handles original execution
|
|
2069
3100
|
append_mode = func_info.get('append_mode', False)
|
|
2070
3101
|
append_ref_class = func_info.get('append_ref_class')
|
|
2071
3102
|
append_ref_member = func_info.get('append_ref_member')
|
|
2072
3103
|
|
|
2073
|
-
|
|
3104
|
+
# v4.9.2: Don't execute original for builtin hooks - wrapper already does that
|
|
3105
|
+
is_builtin_hook = append_ref_class == '__builtins__'
|
|
3106
|
+
if append_mode and append_ref_class and not is_builtin_hook:
|
|
2074
3107
|
self._execute_append_reference(
|
|
2075
3108
|
None, append_ref_class, append_ref_member,
|
|
2076
3109
|
args, kwargs, {}, is_constructor=False
|
|
2077
3110
|
)
|
|
2078
3111
|
|
|
3112
|
+
# v4.7: Handle bytearrayed function modifier
|
|
3113
|
+
if 'bytearrayed' in modifiers:
|
|
3114
|
+
return self._execute_bytearrayed_function(func_node)
|
|
3115
|
+
|
|
2079
3116
|
# v4.2.0: Handle raw_body with supports_language (multi-language support)
|
|
2080
3117
|
raw_body = func_info.get('raw_body')
|
|
2081
3118
|
supports_language = func_info.get('supports_language')
|
|
@@ -2498,16 +3535,92 @@ class CSSLRuntime:
|
|
|
2498
3535
|
Supports multiple return values for shuffled functions:
|
|
2499
3536
|
return a, b, c; // Returns tuple (a, b, c)
|
|
2500
3537
|
"""
|
|
2501
|
-
if node.value is None:
|
|
2502
|
-
raise CSSLReturn(None)
|
|
3538
|
+
if node.value is None:
|
|
3539
|
+
raise CSSLReturn(None)
|
|
3540
|
+
|
|
3541
|
+
# Check if this is a multiple return value
|
|
3542
|
+
if isinstance(node.value, dict) and node.value.get('multiple'):
|
|
3543
|
+
values = [self._evaluate(v) for v in node.value.get('values', [])]
|
|
3544
|
+
raise CSSLReturn(tuple(values))
|
|
3545
|
+
|
|
3546
|
+
value = self._evaluate(node.value)
|
|
3547
|
+
raise CSSLReturn(value)
|
|
3548
|
+
|
|
3549
|
+
def _exec_yield(self, node: ASTNode) -> Any:
|
|
3550
|
+
"""Execute yield statement - v4.9.3
|
|
3551
|
+
|
|
3552
|
+
Yields a value from a generator function, pausing execution.
|
|
3553
|
+
The function resumes from here when next() is called on the generator.
|
|
3554
|
+
|
|
3555
|
+
Syntax:
|
|
3556
|
+
yield value; // Yield a value and pause
|
|
3557
|
+
yield; // Yield None and pause
|
|
3558
|
+
|
|
3559
|
+
Example:
|
|
3560
|
+
generator<int> define Range(int n) {
|
|
3561
|
+
int i = 0;
|
|
3562
|
+
while (i < n) {
|
|
3563
|
+
yield i;
|
|
3564
|
+
i = i + 1;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
"""
|
|
3568
|
+
if node.value is None:
|
|
3569
|
+
raise CSSLYield(None)
|
|
3570
|
+
|
|
3571
|
+
value = self._evaluate(node.value)
|
|
3572
|
+
raise CSSLYield(value)
|
|
3573
|
+
|
|
3574
|
+
def _exec_await(self, node: ASTNode) -> Any:
|
|
3575
|
+
"""Execute await expression - v4.9.3
|
|
3576
|
+
|
|
3577
|
+
Awaits a Future or async function result, blocking until complete.
|
|
3578
|
+
|
|
3579
|
+
Syntax:
|
|
3580
|
+
await future; // Wait for future to complete
|
|
3581
|
+
await asyncFunc(); // Wait for async function
|
|
3582
|
+
result = await future; // Capture result
|
|
3583
|
+
|
|
3584
|
+
Example:
|
|
3585
|
+
async define FetchData() {
|
|
3586
|
+
return http::get("url");
|
|
3587
|
+
}
|
|
3588
|
+
data = await FetchData();
|
|
3589
|
+
"""
|
|
3590
|
+
value = self._evaluate(node.value)
|
|
2503
3591
|
|
|
2504
|
-
#
|
|
2505
|
-
if isinstance(
|
|
2506
|
-
|
|
2507
|
-
|
|
3592
|
+
# If it's a CSSLFuture, wait for it
|
|
3593
|
+
if isinstance(value, CSSLFuture):
|
|
3594
|
+
# Block until the future completes
|
|
3595
|
+
import time
|
|
3596
|
+
while value.state in (CSSLFuture.PENDING, CSSLFuture.RUNNING):
|
|
3597
|
+
time.sleep(0.001) # Small sleep to avoid busy-waiting
|
|
3598
|
+
if value.state == CSSLFuture.FAILED:
|
|
3599
|
+
raise CSSLRuntimeError(f"Awaited future failed: {value._exception}")
|
|
3600
|
+
if value.state == CSSLFuture.CANCELLED:
|
|
3601
|
+
raise CSSLRuntimeError("Awaited future was cancelled")
|
|
3602
|
+
return value.result()
|
|
3603
|
+
|
|
3604
|
+
# If it's an async function wrapper, execute it
|
|
3605
|
+
if isinstance(value, CSSLAsyncFunction):
|
|
3606
|
+
future = value.start()
|
|
3607
|
+
# Wait for completion
|
|
3608
|
+
import time
|
|
3609
|
+
while future.state in (CSSLFuture.PENDING, CSSLFuture.RUNNING):
|
|
3610
|
+
time.sleep(0.001)
|
|
3611
|
+
if future.state == CSSLFuture.FAILED:
|
|
3612
|
+
raise CSSLRuntimeError(f"Async function failed: {future._exception}")
|
|
3613
|
+
return future.result()
|
|
3614
|
+
|
|
3615
|
+
# If it's a generator, exhaust it and return last value
|
|
3616
|
+
if isinstance(value, CSSLGenerator):
|
|
3617
|
+
result = None
|
|
3618
|
+
while value.has_next():
|
|
3619
|
+
result = value.next()
|
|
3620
|
+
return result
|
|
2508
3621
|
|
|
2509
|
-
|
|
2510
|
-
|
|
3622
|
+
# For regular values, just return them
|
|
3623
|
+
return value
|
|
2511
3624
|
|
|
2512
3625
|
def _exec_break(self, node: ASTNode) -> Any:
|
|
2513
3626
|
"""Execute break statement"""
|
|
@@ -2534,6 +3647,76 @@ class CSSLRuntime:
|
|
|
2534
3647
|
message = self._evaluate(node.value)
|
|
2535
3648
|
raise CSSLThrow(message)
|
|
2536
3649
|
|
|
3650
|
+
def _exec_raise(self, node: ASTNode) -> Any:
|
|
3651
|
+
"""Execute raise statement - v4.8 (Python-style exceptions)
|
|
3652
|
+
|
|
3653
|
+
Raises a Python-style exception that propagates to the nearest catch block.
|
|
3654
|
+
|
|
3655
|
+
Syntax:
|
|
3656
|
+
raise; # Re-raise current exception
|
|
3657
|
+
raise "Error message"; # Simple error message
|
|
3658
|
+
raise ValueError("message"); # Python exception type
|
|
3659
|
+
raise CustomError("msg", 123); # Custom exception with args
|
|
3660
|
+
"""
|
|
3661
|
+
value = node.value
|
|
3662
|
+
|
|
3663
|
+
# Handle re-raise (raise;)
|
|
3664
|
+
if value is None or (isinstance(value, dict) and value.get('type') is None):
|
|
3665
|
+
raise CSSLThrow("Re-raised exception")
|
|
3666
|
+
|
|
3667
|
+
# Map of Python exception types
|
|
3668
|
+
EXCEPTION_MAP = {
|
|
3669
|
+
'ValueError': ValueError,
|
|
3670
|
+
'TypeError': TypeError,
|
|
3671
|
+
'KeyError': KeyError,
|
|
3672
|
+
'IndexError': IndexError,
|
|
3673
|
+
'AttributeError': AttributeError,
|
|
3674
|
+
'RuntimeError': RuntimeError,
|
|
3675
|
+
'IOError': IOError,
|
|
3676
|
+
'OSError': OSError,
|
|
3677
|
+
'FileNotFoundError': FileNotFoundError,
|
|
3678
|
+
'NameError': NameError,
|
|
3679
|
+
'ZeroDivisionError': ZeroDivisionError,
|
|
3680
|
+
'OverflowError': OverflowError,
|
|
3681
|
+
'StopIteration': StopIteration,
|
|
3682
|
+
'AssertionError': AssertionError,
|
|
3683
|
+
'NotImplementedError': NotImplementedError,
|
|
3684
|
+
'PermissionError': PermissionError,
|
|
3685
|
+
'TimeoutError': TimeoutError,
|
|
3686
|
+
'ConnectionError': ConnectionError,
|
|
3687
|
+
'ImportError': ImportError,
|
|
3688
|
+
'ModuleNotFoundError': ModuleNotFoundError,
|
|
3689
|
+
'RecursionError': RecursionError,
|
|
3690
|
+
# Generic fallback
|
|
3691
|
+
'Error': Exception,
|
|
3692
|
+
'Exception': Exception,
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
if isinstance(value, dict):
|
|
3696
|
+
exc_type_name = value.get('type', 'Error')
|
|
3697
|
+
args = value.get('args', [])
|
|
3698
|
+
message = value.get('message')
|
|
3699
|
+
|
|
3700
|
+
# Evaluate args if present
|
|
3701
|
+
if args:
|
|
3702
|
+
evaluated_args = [self._evaluate(arg) for arg in args]
|
|
3703
|
+
message_str = evaluated_args[0] if evaluated_args else "Error"
|
|
3704
|
+
elif message:
|
|
3705
|
+
message_str = self._evaluate(message)
|
|
3706
|
+
else:
|
|
3707
|
+
message_str = f"{exc_type_name}: Unknown error"
|
|
3708
|
+
|
|
3709
|
+
# Get the exception class
|
|
3710
|
+
exc_class = EXCEPTION_MAP.get(exc_type_name, Exception)
|
|
3711
|
+
|
|
3712
|
+
# Raise the Python exception
|
|
3713
|
+
# Wrap in CSSLThrow so it can be caught by CSSL try/catch
|
|
3714
|
+
raise CSSLThrow(f"{exc_type_name}: {message_str}", exc_class(message_str))
|
|
3715
|
+
else:
|
|
3716
|
+
# Simple message
|
|
3717
|
+
message = self._evaluate(value)
|
|
3718
|
+
raise CSSLThrow(str(message))
|
|
3719
|
+
|
|
2537
3720
|
def _exec_constructor(self, node: ASTNode) -> Any:
|
|
2538
3721
|
"""Execute constructor node - only called when encountered directly.
|
|
2539
3722
|
|
|
@@ -2952,6 +4135,39 @@ class CSSLRuntime:
|
|
|
2952
4135
|
result = result if result == filter_val else None
|
|
2953
4136
|
elif isinstance(result, list):
|
|
2954
4137
|
result = [item for item in result if isinstance(item, int) and item == filter_val]
|
|
4138
|
+
# v4.8.8: Integer comparison filters
|
|
4139
|
+
elif helper == 'gt': # Greater than
|
|
4140
|
+
if isinstance(result, int):
|
|
4141
|
+
result = result if result > filter_val else None
|
|
4142
|
+
elif isinstance(result, list):
|
|
4143
|
+
result = [item for item in result if isinstance(item, int) and item > filter_val]
|
|
4144
|
+
elif helper == 'lt': # Less than
|
|
4145
|
+
if isinstance(result, int):
|
|
4146
|
+
result = result if result < filter_val else None
|
|
4147
|
+
elif isinstance(result, list):
|
|
4148
|
+
result = [item for item in result if isinstance(item, int) and item < filter_val]
|
|
4149
|
+
elif helper == 'gte' or helper == 'ge': # Greater than or equal
|
|
4150
|
+
if isinstance(result, int):
|
|
4151
|
+
result = result if result >= filter_val else None
|
|
4152
|
+
elif isinstance(result, list):
|
|
4153
|
+
result = [item for item in result if isinstance(item, int) and item >= filter_val]
|
|
4154
|
+
elif helper == 'lte' or helper == 'le': # Less than or equal
|
|
4155
|
+
if isinstance(result, int):
|
|
4156
|
+
result = result if result <= filter_val else None
|
|
4157
|
+
elif isinstance(result, list):
|
|
4158
|
+
result = [item for item in result if isinstance(item, int) and item <= filter_val]
|
|
4159
|
+
elif helper == 'not': # Not equal
|
|
4160
|
+
if isinstance(result, int):
|
|
4161
|
+
result = result if result != filter_val else None
|
|
4162
|
+
elif isinstance(result, list):
|
|
4163
|
+
result = [item for item in result if isinstance(item, int) and item != filter_val]
|
|
4164
|
+
elif helper == 'range': # Range filter [min, max]
|
|
4165
|
+
if isinstance(filter_val, (list, tuple)) and len(filter_val) == 2:
|
|
4166
|
+
min_val, max_val = filter_val
|
|
4167
|
+
if isinstance(result, int):
|
|
4168
|
+
result = result if min_val <= result <= max_val else None
|
|
4169
|
+
elif isinstance(result, list):
|
|
4170
|
+
result = [item for item in result if isinstance(item, int) and min_val <= item <= max_val]
|
|
2955
4171
|
|
|
2956
4172
|
# === JSON HELPERS ===
|
|
2957
4173
|
elif filter_type == 'json':
|
|
@@ -3261,7 +4477,7 @@ class CSSLRuntime:
|
|
|
3261
4477
|
Modes:
|
|
3262
4478
|
- replace: target <== source (replace target with source)
|
|
3263
4479
|
- add: target +<== source (copy & add to target)
|
|
3264
|
-
-
|
|
4480
|
+
- remove: target -<== source (remove items in source from target) [v4.9.2]
|
|
3265
4481
|
"""
|
|
3266
4482
|
target = node.value.get('target')
|
|
3267
4483
|
source_node = node.value.get('source')
|
|
@@ -3364,8 +4580,15 @@ class CSSLRuntime:
|
|
|
3364
4580
|
elif isinstance(current_value, str) and isinstance(source, str):
|
|
3365
4581
|
final_value = current_value + source
|
|
3366
4582
|
# Handle CSSL container types (DataStruct, Vector, Stack, etc.)
|
|
3367
|
-
elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add'):
|
|
3368
|
-
|
|
4583
|
+
elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add') or hasattr(current_value, 'update'):
|
|
4584
|
+
# v4.9.2: Special handling for dict source into containers with update method
|
|
4585
|
+
if isinstance(source, dict) and hasattr(current_value, 'update'):
|
|
4586
|
+
current_value.update(source)
|
|
4587
|
+
elif isinstance(source, dict) and hasattr(current_value, '__setitem__'):
|
|
4588
|
+
# DataStruct, Map, etc. - merge dict key-value pairs
|
|
4589
|
+
for k, v in source.items():
|
|
4590
|
+
current_value[k] = v
|
|
4591
|
+
elif hasattr(current_value, 'append'):
|
|
3369
4592
|
current_value.append(source)
|
|
3370
4593
|
elif hasattr(current_value, 'push'):
|
|
3371
4594
|
current_value.push(source)
|
|
@@ -3373,20 +4596,49 @@ class CSSLRuntime:
|
|
|
3373
4596
|
current_value.add(source)
|
|
3374
4597
|
final_value = current_value
|
|
3375
4598
|
elif current_value is None:
|
|
3376
|
-
|
|
4599
|
+
# v4.9.2: Handle None target - wrap source appropriately
|
|
4600
|
+
if isinstance(source, dict):
|
|
4601
|
+
final_value = source # Keep dict as-is
|
|
4602
|
+
elif isinstance(source, list):
|
|
4603
|
+
final_value = source
|
|
4604
|
+
else:
|
|
4605
|
+
final_value = source # Keep single value as-is
|
|
3377
4606
|
else:
|
|
3378
4607
|
final_value = [current_value, source]
|
|
3379
4608
|
elif mode == 'move':
|
|
3380
|
-
#
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
4609
|
+
# v4.9.2: Changed semantics - remove items from target (not move from source)
|
|
4610
|
+
# target -<== source → remove items in source from target
|
|
4611
|
+
if isinstance(current_value, list):
|
|
4612
|
+
if isinstance(source, list):
|
|
4613
|
+
# Remove all items in source from current_value
|
|
4614
|
+
final_value = [item for item in current_value if item not in source]
|
|
4615
|
+
else:
|
|
4616
|
+
# Remove single item
|
|
4617
|
+
final_value = [item for item in current_value if item != source]
|
|
4618
|
+
elif isinstance(current_value, dict) and isinstance(source, (list, tuple)):
|
|
4619
|
+
# Remove keys from dict
|
|
4620
|
+
final_value = {k: v for k, v in current_value.items() if k not in source}
|
|
4621
|
+
elif isinstance(current_value, dict) and isinstance(source, dict):
|
|
4622
|
+
# Remove keys that exist in source dict
|
|
4623
|
+
final_value = {k: v for k, v in current_value.items() if k not in source}
|
|
4624
|
+
elif hasattr(current_value, 'remove') or hasattr(current_value, '__delitem__'):
|
|
4625
|
+
# CSSL container types
|
|
4626
|
+
if isinstance(source, (list, tuple)):
|
|
4627
|
+
for item in source:
|
|
4628
|
+
if hasattr(current_value, 'remove'):
|
|
4629
|
+
try:
|
|
4630
|
+
current_value.remove(item)
|
|
4631
|
+
except (ValueError, KeyError):
|
|
4632
|
+
pass
|
|
4633
|
+
elif hasattr(current_value, 'remove'):
|
|
4634
|
+
try:
|
|
4635
|
+
current_value.remove(source)
|
|
4636
|
+
except (ValueError, KeyError):
|
|
4637
|
+
pass
|
|
4638
|
+
final_value = current_value
|
|
4639
|
+
else:
|
|
4640
|
+
# Fallback - just return source (old behavior)
|
|
4641
|
+
final_value = source
|
|
3390
4642
|
else:
|
|
3391
4643
|
final_value = source
|
|
3392
4644
|
|
|
@@ -3403,6 +4655,15 @@ class CSSLRuntime:
|
|
|
3403
4655
|
self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
|
|
3404
4656
|
elif target.type == 'member_access':
|
|
3405
4657
|
self._set_member(target, final_value)
|
|
4658
|
+
elif target.type == 'this_access':
|
|
4659
|
+
# v4.9.2: this->member <== value
|
|
4660
|
+
if self._current_instance is None:
|
|
4661
|
+
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
4662
|
+
member = target.value.get('member')
|
|
4663
|
+
if hasattr(self._current_instance, 'set_member'):
|
|
4664
|
+
self._current_instance.set_member(member, final_value)
|
|
4665
|
+
else:
|
|
4666
|
+
setattr(self._current_instance, member, final_value)
|
|
3406
4667
|
elif target.type == 'call':
|
|
3407
4668
|
callee = target.value.get('callee')
|
|
3408
4669
|
if isinstance(callee, ASTNode) and callee.type == 'member_access':
|
|
@@ -3452,8 +4713,28 @@ class CSSLRuntime:
|
|
|
3452
4713
|
final_value = {**current_value, **source}
|
|
3453
4714
|
elif isinstance(current_value, str) and isinstance(source, str):
|
|
3454
4715
|
final_value = current_value + source
|
|
4716
|
+
# v4.9.2: Handle CSSL container types
|
|
4717
|
+
elif hasattr(current_value, 'append') or hasattr(current_value, 'push') or hasattr(current_value, 'add') or hasattr(current_value, 'update'):
|
|
4718
|
+
if isinstance(source, dict) and hasattr(current_value, 'update'):
|
|
4719
|
+
current_value.update(source)
|
|
4720
|
+
elif isinstance(source, dict) and hasattr(current_value, '__setitem__'):
|
|
4721
|
+
for k, v in source.items():
|
|
4722
|
+
current_value[k] = v
|
|
4723
|
+
elif hasattr(current_value, 'append'):
|
|
4724
|
+
current_value.append(source)
|
|
4725
|
+
elif hasattr(current_value, 'push'):
|
|
4726
|
+
current_value.push(source)
|
|
4727
|
+
elif hasattr(current_value, 'add'):
|
|
4728
|
+
current_value.add(source)
|
|
4729
|
+
final_value = current_value
|
|
3455
4730
|
elif current_value is None:
|
|
3456
|
-
|
|
4731
|
+
# v4.9.2: Keep source type when target is None
|
|
4732
|
+
if isinstance(source, dict):
|
|
4733
|
+
final_value = source
|
|
4734
|
+
elif isinstance(source, list):
|
|
4735
|
+
final_value = source
|
|
4736
|
+
else:
|
|
4737
|
+
final_value = source
|
|
3457
4738
|
else:
|
|
3458
4739
|
final_value = [current_value, source]
|
|
3459
4740
|
elif mode == 'move':
|
|
@@ -3510,6 +4791,15 @@ class CSSLRuntime:
|
|
|
3510
4791
|
self.global_scope.set(f'${name}', SharedObjectProxy(name, final_value))
|
|
3511
4792
|
elif target.type == 'member_access':
|
|
3512
4793
|
self._set_member(target, final_value)
|
|
4794
|
+
elif target.type == 'this_access':
|
|
4795
|
+
# v4.9.2: source ==> this->member
|
|
4796
|
+
if self._current_instance is None:
|
|
4797
|
+
raise CSSLRuntimeError("'this' used outside of class method context")
|
|
4798
|
+
member = target.value.get('member')
|
|
4799
|
+
if hasattr(self._current_instance, 'set_member'):
|
|
4800
|
+
self._current_instance.set_member(member, final_value)
|
|
4801
|
+
else:
|
|
4802
|
+
setattr(self._current_instance, member, final_value)
|
|
3513
4803
|
elif target.type == 'typed_declaration':
|
|
3514
4804
|
# Handle typed target: source ==> datastruct<dynamic> Output
|
|
3515
4805
|
var_name = target.value.get('name')
|
|
@@ -3599,6 +4889,26 @@ class CSSLRuntime:
|
|
|
3599
4889
|
if not func_name or code_block is None:
|
|
3600
4890
|
return None
|
|
3601
4891
|
|
|
4892
|
+
# v4.8.6: Check if target is actually a function or if it's just a variable
|
|
4893
|
+
# If it's a variable (not a function), fall back to value assignment with capture
|
|
4894
|
+
existing_func = self.scope.get(func_name)
|
|
4895
|
+
if existing_func is None:
|
|
4896
|
+
existing_func = self.global_scope.get(func_name)
|
|
4897
|
+
is_function = (isinstance(existing_func, ASTNode) and existing_func.type == 'function') or callable(existing_func)
|
|
4898
|
+
|
|
4899
|
+
if not is_function and target.type == 'identifier':
|
|
4900
|
+
# Target is a variable, not a function - do value capture assignment instead
|
|
4901
|
+
# This handles: savedVersion <<== { %version; }
|
|
4902
|
+
captured_values = self._scan_and_capture_refs(code_block)
|
|
4903
|
+
old_captured = self._current_captured_values.copy()
|
|
4904
|
+
self._current_captured_values = captured_values
|
|
4905
|
+
try:
|
|
4906
|
+
value = self._evaluate_action_block(code_block)
|
|
4907
|
+
finally:
|
|
4908
|
+
self._current_captured_values = old_captured
|
|
4909
|
+
self.scope.set(func_name, value)
|
|
4910
|
+
return value
|
|
4911
|
+
|
|
3602
4912
|
if mode == 'add':
|
|
3603
4913
|
# +<<== : Add code to function (both injection + original execute)
|
|
3604
4914
|
self.register_function_injection(func_name, code_block)
|
|
@@ -3760,6 +5070,18 @@ class CSSLRuntime:
|
|
|
3760
5070
|
name = target.value
|
|
3761
5071
|
_live_objects[name] = value
|
|
3762
5072
|
self.global_scope.set(f'${name}', SharedObjectProxy(name, value))
|
|
5073
|
+
elif target.type == 'captured_ref':
|
|
5074
|
+
# v4.8.9: %name = value - assign to snapshot directly
|
|
5075
|
+
# This allows: %xyz = othervar, %xyz = "hello", %xyz = (int number = 200)
|
|
5076
|
+
name = target.value
|
|
5077
|
+
self.builtins._snapshots[name] = value
|
|
5078
|
+
elif target.type == 'pointer_ref':
|
|
5079
|
+
# v4.9.0: ?name = value - create pointer to value
|
|
5080
|
+
# The pointer stores an address to the object
|
|
5081
|
+
name = target.value
|
|
5082
|
+
from .cssl_types import Address
|
|
5083
|
+
addr = Address(obj=value)
|
|
5084
|
+
self.scope.set(f'?{name}', addr)
|
|
3763
5085
|
elif target.type == 'module_ref':
|
|
3764
5086
|
# @Name = value - store in promoted globals (like global keyword)
|
|
3765
5087
|
self._promoted_globals[target.value] = value
|
|
@@ -3823,21 +5145,6 @@ class CSSLRuntime:
|
|
|
3823
5145
|
"""Execute type instantiation as statement (e.g., vector<int>)"""
|
|
3824
5146
|
return self._evaluate(node)
|
|
3825
5147
|
|
|
3826
|
-
def _exec_await(self, node: ASTNode) -> Any:
|
|
3827
|
-
"""Execute await statement - waits for expression to complete"""
|
|
3828
|
-
# Evaluate the awaited expression
|
|
3829
|
-
# The expression is typically a call like wait_for_booted()
|
|
3830
|
-
result = self._evaluate(node.value)
|
|
3831
|
-
|
|
3832
|
-
# If result is a callable (like a coroutine or future), wait for it
|
|
3833
|
-
if hasattr(result, '__await__'):
|
|
3834
|
-
import asyncio
|
|
3835
|
-
loop = asyncio.get_event_loop()
|
|
3836
|
-
return loop.run_until_complete(result)
|
|
3837
|
-
|
|
3838
|
-
# If result is a boolean condition waiting function, it already handled the waiting
|
|
3839
|
-
return result
|
|
3840
|
-
|
|
3841
5148
|
def _exec_then(self, node: ASTNode) -> Any:
|
|
3842
5149
|
"""Execute then block"""
|
|
3843
5150
|
for child in node.children:
|
|
@@ -3892,6 +5199,24 @@ class CSSLRuntime:
|
|
|
3892
5199
|
return {}
|
|
3893
5200
|
return None
|
|
3894
5201
|
|
|
5202
|
+
# v4.9.0: Byte literal (x^y notation)
|
|
5203
|
+
if node.type == 'byte_literal':
|
|
5204
|
+
from .cssl_types import Byte
|
|
5205
|
+
base = node.value.get('base')
|
|
5206
|
+
weight = node.value.get('weight')
|
|
5207
|
+
return Byte(base, weight)
|
|
5208
|
+
|
|
5209
|
+
# v4.8.9: Typed expression (type name = value) - creates variable and returns value
|
|
5210
|
+
# Used for snapshot assignment: %xyz = (int number = 200)
|
|
5211
|
+
if node.type == 'typed_expression':
|
|
5212
|
+
var_type = node.value.get('type')
|
|
5213
|
+
var_name = node.value.get('name')
|
|
5214
|
+
var_value = self._evaluate(node.value.get('value'))
|
|
5215
|
+
# Create the typed variable in current scope
|
|
5216
|
+
self.scope.set(var_name, var_value)
|
|
5217
|
+
# Return the value so it can be used in assignment
|
|
5218
|
+
return var_value
|
|
5219
|
+
|
|
3895
5220
|
if node.type == 'identifier':
|
|
3896
5221
|
name = node.value
|
|
3897
5222
|
|
|
@@ -3901,16 +5226,34 @@ class CSSLRuntime:
|
|
|
3901
5226
|
container_name = parts[0]
|
|
3902
5227
|
member_name = parts[1]
|
|
3903
5228
|
|
|
3904
|
-
# Look up the container (enum, class, or
|
|
5229
|
+
# Look up the container (enum, class, namespace, or module)
|
|
3905
5230
|
container = self.scope.get(container_name)
|
|
3906
5231
|
if container is None:
|
|
3907
5232
|
container = self.global_scope.get(container_name)
|
|
3908
5233
|
if container is None:
|
|
3909
5234
|
container = self._promoted_globals.get(container_name)
|
|
5235
|
+
# v4.8: Also check modules for :: access (fmt::green, etc.)
|
|
5236
|
+
if container is None:
|
|
5237
|
+
container = self.get_module(container_name)
|
|
3910
5238
|
|
|
3911
5239
|
if container is not None:
|
|
5240
|
+
# v4.8: Handle CSSLNamespace - supports arbitrarily deep nested access
|
|
5241
|
+
if isinstance(container, CSSLNamespace):
|
|
5242
|
+
# Handle nested :: access (e.g., ns::inner::deep::func)
|
|
5243
|
+
current = container
|
|
5244
|
+
remaining = member_name
|
|
5245
|
+
while '::' in remaining and isinstance(current, CSSLNamespace):
|
|
5246
|
+
parts = remaining.split('::', 1)
|
|
5247
|
+
current = current.get(parts[0])
|
|
5248
|
+
remaining = parts[1]
|
|
5249
|
+
if current is None:
|
|
5250
|
+
break
|
|
5251
|
+
# Final lookup
|
|
5252
|
+
if isinstance(current, CSSLNamespace):
|
|
5253
|
+
return current.get(remaining)
|
|
5254
|
+
return current if current is not None else None
|
|
3912
5255
|
# If it's a dict-like object (enum or namespace), get the member
|
|
3913
|
-
|
|
5256
|
+
elif isinstance(container, dict):
|
|
3914
5257
|
return container.get(member_name)
|
|
3915
5258
|
# If it's an object with the member as an attribute
|
|
3916
5259
|
elif hasattr(container, member_name):
|
|
@@ -3923,6 +5266,11 @@ class CSSLRuntime:
|
|
|
3923
5266
|
# Fall through to normal lookup if container not found
|
|
3924
5267
|
return None
|
|
3925
5268
|
|
|
5269
|
+
# v4.9.2: When inside a hook execution, return original builtin to prevent recursion
|
|
5270
|
+
# This must be checked BEFORE scope lookup since the hook is stored in scope
|
|
5271
|
+
if name in self._hook_executing and name in self._original_functions:
|
|
5272
|
+
return self._original_functions[name]
|
|
5273
|
+
|
|
3926
5274
|
value = self.scope.get(name)
|
|
3927
5275
|
# Check if it's a class member in current instance context
|
|
3928
5276
|
# This allows accessing members without 'this->' inside methods
|
|
@@ -4011,25 +5359,123 @@ class CSSLRuntime:
|
|
|
4011
5359
|
if value is not None:
|
|
4012
5360
|
return value
|
|
4013
5361
|
|
|
4014
|
-
# 3.
|
|
5362
|
+
# 3. v4.8.8: Check snapshots - %name accesses snapshotted values (BEFORE scope!)
|
|
5363
|
+
# This ensures snapshot(var); var = "new"; %var returns the OLD value
|
|
5364
|
+
if hasattr(self.builtins, '_snapshots') and name in self.builtins._snapshots:
|
|
5365
|
+
return self.builtins._snapshots[name]
|
|
5366
|
+
|
|
5367
|
+
# 4. v4.9.2: Only fall back to scope if we're in an injection context
|
|
5368
|
+
# (i.e., _current_captured_values is populated). For direct %name usage
|
|
5369
|
+
# outside injections, return null if no snapshot exists.
|
|
5370
|
+
if self._current_captured_values:
|
|
5371
|
+
# We're in an injection context - fall back to scope/builtins
|
|
5372
|
+
value = self.scope.get(name)
|
|
5373
|
+
if value is None:
|
|
5374
|
+
value = self.global_scope.get(name)
|
|
5375
|
+
if value is None:
|
|
5376
|
+
# For critical builtins like 'exit', create direct wrapper
|
|
5377
|
+
if name == 'exit':
|
|
5378
|
+
runtime = self
|
|
5379
|
+
value = lambda code=0, rt=runtime: rt.exit(code)
|
|
5380
|
+
else:
|
|
5381
|
+
value = getattr(self.builtins, f'builtin_{name}', None)
|
|
5382
|
+
if value is not None:
|
|
5383
|
+
return value
|
|
5384
|
+
|
|
5385
|
+
# Build helpful error for captured reference in injection context
|
|
5386
|
+
hint = f"Variable '{name}' must exist when the infusion is registered, or be snapshotted with snapshot({name}). Check that '%{name}' is defined before use."
|
|
5387
|
+
raise self._format_error(
|
|
5388
|
+
node.line if hasattr(node, 'line') else 0,
|
|
5389
|
+
f"Captured reference '%{name}' not found (no snapshot or captured value)",
|
|
5390
|
+
hint
|
|
5391
|
+
)
|
|
5392
|
+
else:
|
|
5393
|
+
# v4.9.2: Direct %name usage outside injection - no snapshot exists, return null
|
|
5394
|
+
return None
|
|
5395
|
+
|
|
5396
|
+
# v4.9.2: Local reference - local::<name> accesses hooked function's local variables/params
|
|
5397
|
+
if node.type == 'local_ref':
|
|
5398
|
+
name = node.value
|
|
5399
|
+
# Access the hook's local context (set by hook wrapper)
|
|
5400
|
+
if hasattr(self, '_hook_locals') and self._hook_locals:
|
|
5401
|
+
if name in self._hook_locals:
|
|
5402
|
+
return self._hook_locals[name]
|
|
5403
|
+
# Also check _result which is set automatically for append hooks
|
|
5404
|
+
if name == 'result' or name == '_result':
|
|
5405
|
+
result = self.scope.get('_result')
|
|
5406
|
+
if result is not None:
|
|
5407
|
+
return result
|
|
5408
|
+
# Fall back to current scope
|
|
4015
5409
|
value = self.scope.get(name)
|
|
4016
|
-
if value is None:
|
|
4017
|
-
value = self.global_scope.get(name)
|
|
4018
|
-
if value is None:
|
|
4019
|
-
# For critical builtins like 'exit', create direct wrapper
|
|
4020
|
-
if name == 'exit':
|
|
4021
|
-
runtime = self
|
|
4022
|
-
value = lambda code=0, rt=runtime: rt.exit(code)
|
|
4023
|
-
else:
|
|
4024
|
-
value = getattr(self.builtins, f'builtin_{name}', None)
|
|
4025
5410
|
if value is not None:
|
|
4026
5411
|
return value
|
|
4027
|
-
|
|
4028
|
-
|
|
5412
|
+
return None
|
|
5413
|
+
|
|
5414
|
+
# v4.9.2: Local assignment - local::<name> = value
|
|
5415
|
+
if node.type == 'local_assign':
|
|
5416
|
+
name = node.value.get('name')
|
|
5417
|
+
value_node = node.value.get('value')
|
|
5418
|
+
value = self._evaluate(value_node)
|
|
5419
|
+
# Set in hook locals if available, otherwise scope
|
|
5420
|
+
if hasattr(self, '_hook_locals') and self._hook_locals is not None:
|
|
5421
|
+
self._hook_locals[name] = value
|
|
5422
|
+
else:
|
|
5423
|
+
self.scope.set(name, value)
|
|
5424
|
+
return value
|
|
5425
|
+
|
|
5426
|
+
# v4.9.2: Local injection - local::func -<<== {...} or local::func +<<== {...}
|
|
5427
|
+
if node.type == 'local_injection':
|
|
5428
|
+
local_name = node.value.get('local_name')
|
|
5429
|
+
mode = node.value.get('mode') # 'remove', 'add', 'replace'
|
|
5430
|
+
filters = node.value.get('filters', [])
|
|
5431
|
+
code_block = node.value.get('code')
|
|
5432
|
+
# This modifies the local function at runtime - advanced feature
|
|
5433
|
+
# For now, just store the injection intent
|
|
5434
|
+
if not hasattr(self, '_local_injections'):
|
|
5435
|
+
self._local_injections = {}
|
|
5436
|
+
self._local_injections[local_name] = {
|
|
5437
|
+
'mode': mode,
|
|
5438
|
+
'filters': filters,
|
|
5439
|
+
'code': code_block
|
|
5440
|
+
}
|
|
5441
|
+
return None
|
|
5442
|
+
|
|
5443
|
+
# v4.9.0: Pointer reference - ?name can either:
|
|
5444
|
+
# 1. Dereference an existing pointer named ?name
|
|
5445
|
+
# 2. Create an Address to a variable named 'name' (v4.9.2)
|
|
5446
|
+
if node.type == 'pointer_ref':
|
|
5447
|
+
name = node.value
|
|
5448
|
+
# First check if ?name exists as a pointer in scope
|
|
5449
|
+
addr = self.scope.get(f'?{name}')
|
|
5450
|
+
if addr is None:
|
|
5451
|
+
addr = self.global_scope.get(f'?{name}')
|
|
5452
|
+
|
|
5453
|
+
if addr is not None:
|
|
5454
|
+
# Dereference the existing pointer
|
|
5455
|
+
if isinstance(addr, Address):
|
|
5456
|
+
return addr.reflect()
|
|
5457
|
+
# If it's a direct object (simple pointer), return it
|
|
5458
|
+
return addr
|
|
5459
|
+
|
|
5460
|
+
# v4.9.2: No pointer exists - check if 'name' is a variable
|
|
5461
|
+
# If so, create an Address pointing to it (like &name in C)
|
|
5462
|
+
# v4.9.3: If the variable IS an Address, dereference it instead
|
|
5463
|
+
var_value = self.scope.get(name)
|
|
5464
|
+
if var_value is None:
|
|
5465
|
+
var_value = self.global_scope.get(name)
|
|
5466
|
+
|
|
5467
|
+
if var_value is not None:
|
|
5468
|
+
# v4.9.3: If var is already an Address, dereference it
|
|
5469
|
+
if isinstance(var_value, Address):
|
|
5470
|
+
return var_value.reflect()
|
|
5471
|
+
# Otherwise create an Address to this variable
|
|
5472
|
+
return Address(obj=var_value)
|
|
5473
|
+
|
|
5474
|
+
# Neither pointer nor variable exists
|
|
4029
5475
|
raise self._format_error(
|
|
4030
5476
|
node.line if hasattr(node, 'line') else 0,
|
|
4031
|
-
f"
|
|
4032
|
-
|
|
5477
|
+
f"Cannot resolve '?{name}': no pointer '?{name}' or variable '{name}' found",
|
|
5478
|
+
f"Either create a pointer: ?{name} = someObject, or declare variable: {name} = value"
|
|
4033
5479
|
)
|
|
4034
5480
|
|
|
4035
5481
|
if node.type == 'instance_ref':
|
|
@@ -4187,6 +5633,11 @@ class CSSLRuntime:
|
|
|
4187
5633
|
value = self._evaluate(value_node)
|
|
4188
5634
|
m.insert(key, value)
|
|
4189
5635
|
return m
|
|
5636
|
+
elif type_name == 'queue':
|
|
5637
|
+
# v4.7: Create Queue with element_type and size
|
|
5638
|
+
queue_size = node.value.get('queue_size', 'dynamic')
|
|
5639
|
+
q = Queue(element_type, queue_size)
|
|
5640
|
+
return _populate_container(q, init_values)
|
|
4190
5641
|
else:
|
|
4191
5642
|
return None
|
|
4192
5643
|
|
|
@@ -4196,6 +5647,10 @@ class CSSLRuntime:
|
|
|
4196
5647
|
if node.type == 'unary':
|
|
4197
5648
|
return self._eval_unary(node)
|
|
4198
5649
|
|
|
5650
|
+
# v4.9.3: await expression
|
|
5651
|
+
if node.type == 'await':
|
|
5652
|
+
return self._exec_await(node)
|
|
5653
|
+
|
|
4199
5654
|
# Increment: ++i or i++
|
|
4200
5655
|
if node.type == 'increment':
|
|
4201
5656
|
return self._eval_increment(node)
|
|
@@ -4205,26 +5660,13 @@ class CSSLRuntime:
|
|
|
4205
5660
|
return self._eval_decrement(node)
|
|
4206
5661
|
|
|
4207
5662
|
if node.type == 'non_null_assert':
|
|
4208
|
-
# *$var, *@module, *identifier -
|
|
5663
|
+
# *$var, *@module, *identifier - safe access, returns 0 if null
|
|
5664
|
+
# v4.9.3: Changed from error to safe default (0) for null values
|
|
4209
5665
|
operand = node.value.get('operand')
|
|
4210
5666
|
value = self._evaluate(operand)
|
|
4211
5667
|
if value is None:
|
|
4212
|
-
#
|
|
4213
|
-
|
|
4214
|
-
if isinstance(operand, ASTNode):
|
|
4215
|
-
if operand.type == 'identifier':
|
|
4216
|
-
operand_name = operand.value
|
|
4217
|
-
elif operand.type == 'shared_ref':
|
|
4218
|
-
operand_name = f"${operand.value}"
|
|
4219
|
-
elif operand.type == 'module_ref':
|
|
4220
|
-
operand_name = f"@{operand.value}"
|
|
4221
|
-
elif operand.type == 'global_ref':
|
|
4222
|
-
operand_name = f"r@{operand.value}"
|
|
4223
|
-
raise self._format_error(
|
|
4224
|
-
node.line if hasattr(node, 'line') else 0,
|
|
4225
|
-
f"Non-null assertion failed: '{operand_name}' is null/None",
|
|
4226
|
-
f"The value accessed via '*{operand_name}' must not be null. Check that it is defined and initialized."
|
|
4227
|
-
)
|
|
5668
|
+
# Return 0 as safe default instead of throwing error
|
|
5669
|
+
return 0
|
|
4228
5670
|
return value
|
|
4229
5671
|
|
|
4230
5672
|
if node.type == 'type_exclude_assert':
|
|
@@ -4272,6 +5714,10 @@ class CSSLRuntime:
|
|
|
4272
5714
|
if node.type == 'array':
|
|
4273
5715
|
return [self._evaluate(elem) for elem in node.value]
|
|
4274
5716
|
|
|
5717
|
+
# v4.9.2: Tuple literals (0, 10) or (a, b, c)
|
|
5718
|
+
if node.type == 'tuple':
|
|
5719
|
+
return tuple(self._evaluate(elem) for elem in node.value)
|
|
5720
|
+
|
|
4275
5721
|
if node.type == 'object':
|
|
4276
5722
|
return {k: self._evaluate(v) for k, v in node.value.items()}
|
|
4277
5723
|
|
|
@@ -4396,23 +5842,76 @@ class CSSLRuntime:
|
|
|
4396
5842
|
if op == 'or' or op == '||':
|
|
4397
5843
|
return left or right
|
|
4398
5844
|
|
|
4399
|
-
# === BITWISE OPERATIONS ===
|
|
5845
|
+
# === BITWISE OPERATIONS (or stream/pipe operations) ===
|
|
4400
5846
|
if op == '&':
|
|
4401
5847
|
return int(left or 0) & int(right or 0)
|
|
4402
5848
|
if op == '|':
|
|
5849
|
+
# v4.8.4: Pipe operator support
|
|
5850
|
+
from .cssl_types import Pipe, OutputStream, InputStream, FileStream
|
|
5851
|
+
if isinstance(left, Pipe):
|
|
5852
|
+
if callable(right):
|
|
5853
|
+
return left | right
|
|
5854
|
+
return Pipe(left._data)
|
|
5855
|
+
# If right is a Pipe transform function
|
|
5856
|
+
if callable(right) and hasattr(right, '__name__') and 'pipe' in str(type(right)):
|
|
5857
|
+
return Pipe(left) | right
|
|
5858
|
+
# Bitwise OR for integers
|
|
4403
5859
|
return int(left or 0) | int(right or 0)
|
|
4404
5860
|
if op == '^':
|
|
4405
5861
|
return int(left or 0) ^ int(right or 0)
|
|
4406
5862
|
if op == '<<':
|
|
5863
|
+
# v4.8.4: Stream output operator support
|
|
5864
|
+
from .cssl_types import OutputStream, FileStream
|
|
5865
|
+
if isinstance(left, (OutputStream, FileStream)):
|
|
5866
|
+
# Handle manipulators
|
|
5867
|
+
if isinstance(right, dict) and right.get('type') == 'manipulator':
|
|
5868
|
+
name = right.get('name')
|
|
5869
|
+
value = right.get('value')
|
|
5870
|
+
if name == 'setprecision':
|
|
5871
|
+
left.setprecision(value)
|
|
5872
|
+
elif name == 'setw':
|
|
5873
|
+
left.setw(value)
|
|
5874
|
+
elif name == 'setfill':
|
|
5875
|
+
left.setfill(value)
|
|
5876
|
+
elif name == 'fixed':
|
|
5877
|
+
left.fixed()
|
|
5878
|
+
elif name == 'scientific':
|
|
5879
|
+
left.scientific()
|
|
5880
|
+
return left
|
|
5881
|
+
# Stream output
|
|
5882
|
+
return left.write(right)
|
|
5883
|
+
# Bitwise left shift for integers
|
|
4407
5884
|
return int(left or 0) << int(right or 0)
|
|
4408
5885
|
if op == '>>':
|
|
5886
|
+
# v4.8.4: Stream input operator support
|
|
5887
|
+
from .cssl_types import InputStream, FileStream
|
|
5888
|
+
if isinstance(left, (InputStream, FileStream)):
|
|
5889
|
+
# Read from stream into target type
|
|
5890
|
+
if isinstance(right, type):
|
|
5891
|
+
return left.read(right)
|
|
5892
|
+
return left.read(str)
|
|
5893
|
+
# Bitwise right shift for integers
|
|
4409
5894
|
return int(left or 0) >> int(right or 0)
|
|
4410
5895
|
|
|
4411
|
-
# === IN OPERATOR ===
|
|
5896
|
+
# === IN OPERATOR (v4.8.4: C++ optimized with unative fallback) ===
|
|
4412
5897
|
if op == 'in':
|
|
4413
5898
|
if right is None:
|
|
4414
5899
|
return False
|
|
4415
|
-
|
|
5900
|
+
# Check if unative mode is requested (use Python's native 'in')
|
|
5901
|
+
use_native = node.value.get('unative', False)
|
|
5902
|
+
if use_native:
|
|
5903
|
+
return left in right
|
|
5904
|
+
# C++ optimized containment check
|
|
5905
|
+
return self._contains_fast(right, left)
|
|
5906
|
+
|
|
5907
|
+
# === NOT IN OPERATOR ===
|
|
5908
|
+
if op == 'not in' or op == '!in' or op == 'notin':
|
|
5909
|
+
if right is None:
|
|
5910
|
+
return True
|
|
5911
|
+
use_native = node.value.get('unative', False)
|
|
5912
|
+
if use_native:
|
|
5913
|
+
return left not in right
|
|
5914
|
+
return not self._contains_fast(right, left)
|
|
4416
5915
|
|
|
4417
5916
|
return None
|
|
4418
5917
|
|
|
@@ -4473,7 +5972,7 @@ class CSSLRuntime:
|
|
|
4473
5972
|
elif l > r:
|
|
4474
5973
|
return 1
|
|
4475
5974
|
return 0
|
|
4476
|
-
except:
|
|
5975
|
+
except (ValueError, TypeError):
|
|
4477
5976
|
# Fallback to string comparison
|
|
4478
5977
|
l_str = str(left)
|
|
4479
5978
|
r_str = str(right)
|
|
@@ -4483,6 +5982,61 @@ class CSSLRuntime:
|
|
|
4483
5982
|
return 1
|
|
4484
5983
|
return 0
|
|
4485
5984
|
|
|
5985
|
+
def _contains_fast(self, container: Any, item: Any) -> bool:
|
|
5986
|
+
"""C++ optimized containment check (v4.8.4).
|
|
5987
|
+
|
|
5988
|
+
Uses optimized algorithms based on container type:
|
|
5989
|
+
- Hash-based (dict, set): O(1) lookup
|
|
5990
|
+
- Sorted data: Binary search O(log n)
|
|
5991
|
+
- Linear: O(n) with early termination
|
|
5992
|
+
"""
|
|
5993
|
+
from .cssl_types import Vector, Array, List, Dictionary, Map, DataStruct
|
|
5994
|
+
|
|
5995
|
+
# Hash-based containers - O(1)
|
|
5996
|
+
if isinstance(container, (dict, set, frozenset)):
|
|
5997
|
+
return item in container
|
|
5998
|
+
if isinstance(container, (Dictionary, Map)):
|
|
5999
|
+
if hasattr(container, 'contains'):
|
|
6000
|
+
return container.contains(item)
|
|
6001
|
+
return item in container
|
|
6002
|
+
|
|
6003
|
+
# For list-like containers, check if sorted and use binary search
|
|
6004
|
+
if isinstance(container, (list, tuple, Vector, Array, List, DataStruct)):
|
|
6005
|
+
data = list(container) if not isinstance(container, list) else container
|
|
6006
|
+
|
|
6007
|
+
# For larger collections, check if sorted and use binary search
|
|
6008
|
+
if len(data) > 10:
|
|
6009
|
+
# Quick sorted check on sample
|
|
6010
|
+
sample_size = min(10, len(data))
|
|
6011
|
+
step = max(1, len(data) // sample_size)
|
|
6012
|
+
sample = [data[i] for i in range(0, len(data), step)][:sample_size]
|
|
6013
|
+
|
|
6014
|
+
try:
|
|
6015
|
+
is_sorted = all(sample[i] <= sample[i+1] for i in range(len(sample)-1))
|
|
6016
|
+
if is_sorted:
|
|
6017
|
+
# Binary search for sorted data
|
|
6018
|
+
import bisect
|
|
6019
|
+
idx = bisect.bisect_left(data, item)
|
|
6020
|
+
return idx < len(data) and data[idx] == item
|
|
6021
|
+
except (TypeError, AttributeError):
|
|
6022
|
+
pass # Non-comparable items, fall through to linear
|
|
6023
|
+
|
|
6024
|
+
# Linear search with early termination
|
|
6025
|
+
for x in data:
|
|
6026
|
+
if x == item:
|
|
6027
|
+
return True
|
|
6028
|
+
return False
|
|
6029
|
+
|
|
6030
|
+
# String containment - use native (already optimized in Python)
|
|
6031
|
+
if isinstance(container, str):
|
|
6032
|
+
return str(item) in container
|
|
6033
|
+
|
|
6034
|
+
# Generic fallback
|
|
6035
|
+
try:
|
|
6036
|
+
return item in container
|
|
6037
|
+
except TypeError:
|
|
6038
|
+
return False
|
|
6039
|
+
|
|
4486
6040
|
def _eval_unary(self, node: ASTNode) -> Any:
|
|
4487
6041
|
"""Evaluate unary operation"""
|
|
4488
6042
|
op = node.value.get('op')
|
|
@@ -4572,6 +6126,27 @@ class CSSLRuntime:
|
|
|
4572
6126
|
kwargs_raw = node.value.get('kwargs', {})
|
|
4573
6127
|
kwargs = {k: self._evaluate(v) for k, v in kwargs_raw.items()} if kwargs_raw else {}
|
|
4574
6128
|
|
|
6129
|
+
# v4.9.3: Handle ?FuncName() - call function and handle pointer result
|
|
6130
|
+
if isinstance(callee_node, ASTNode) and callee_node.type == 'pointer_ref':
|
|
6131
|
+
from .cssl_types import Address
|
|
6132
|
+
func_name = callee_node.value
|
|
6133
|
+
# Check if this is a function (not a variable)
|
|
6134
|
+
func = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
6135
|
+
if func is None:
|
|
6136
|
+
func = self.builtins.get(func_name)
|
|
6137
|
+
|
|
6138
|
+
# If it's a function, call it
|
|
6139
|
+
if callable(func) or (isinstance(func, ASTNode) and func.type == 'function'):
|
|
6140
|
+
if callable(func):
|
|
6141
|
+
result = func(*args, **kwargs) if kwargs else func(*args)
|
|
6142
|
+
else:
|
|
6143
|
+
result = self._call_function(func, args, kwargs)
|
|
6144
|
+
# If result is already an Address, dereference it
|
|
6145
|
+
if isinstance(result, Address):
|
|
6146
|
+
return result.reflect()
|
|
6147
|
+
# Otherwise return pointer to the result
|
|
6148
|
+
return Address(obj=result)
|
|
6149
|
+
|
|
4575
6150
|
# Get function name for injection check FIRST (before evaluating callee)
|
|
4576
6151
|
func_name = None
|
|
4577
6152
|
if isinstance(callee_node, ASTNode):
|
|
@@ -4712,6 +6287,50 @@ class CSSLRuntime:
|
|
|
4712
6287
|
|
|
4713
6288
|
return None
|
|
4714
6289
|
|
|
6290
|
+
# v4.9.2: Handle cast<type>(value) - type casting
|
|
6291
|
+
if name == 'cast':
|
|
6292
|
+
if not args:
|
|
6293
|
+
raise CSSLRuntimeError(
|
|
6294
|
+
"cast<type>(value) requires a value argument",
|
|
6295
|
+
node.line,
|
|
6296
|
+
hint="Usage: cast<int>(3.14) or cast<string>(42)"
|
|
6297
|
+
)
|
|
6298
|
+
value = args[0]
|
|
6299
|
+
target_type = type_param.lower()
|
|
6300
|
+
|
|
6301
|
+
try:
|
|
6302
|
+
if target_type in ('int', 'integer'):
|
|
6303
|
+
return int(value)
|
|
6304
|
+
elif target_type in ('float', 'double'):
|
|
6305
|
+
return float(value)
|
|
6306
|
+
elif target_type in ('string', 'str'):
|
|
6307
|
+
return str(value)
|
|
6308
|
+
elif target_type in ('bool', 'boolean'):
|
|
6309
|
+
if isinstance(value, str):
|
|
6310
|
+
return value.lower() not in ('', '0', 'false', 'no', 'null', 'none')
|
|
6311
|
+
return bool(value)
|
|
6312
|
+
elif target_type in ('list', 'array'):
|
|
6313
|
+
if isinstance(value, (list, tuple)):
|
|
6314
|
+
return list(value)
|
|
6315
|
+
return [value]
|
|
6316
|
+
elif target_type in ('dict', 'json'):
|
|
6317
|
+
if isinstance(value, dict):
|
|
6318
|
+
return value
|
|
6319
|
+
return {'value': value}
|
|
6320
|
+
elif target_type == 'dynamic':
|
|
6321
|
+
return value # No conversion
|
|
6322
|
+
else:
|
|
6323
|
+
raise CSSLRuntimeError(
|
|
6324
|
+
f"Unknown cast type: {target_type}",
|
|
6325
|
+
node.line,
|
|
6326
|
+
hint="Supported types: int, float, string, bool, list, dict, dynamic"
|
|
6327
|
+
)
|
|
6328
|
+
except (ValueError, TypeError) as e:
|
|
6329
|
+
raise CSSLRuntimeError(
|
|
6330
|
+
f"Cannot cast {type(value).__name__} to {target_type}: {e}",
|
|
6331
|
+
node.line
|
|
6332
|
+
)
|
|
6333
|
+
|
|
4715
6334
|
# Fallback: call as regular function with type hint
|
|
4716
6335
|
func = self.builtins.get_function(name)
|
|
4717
6336
|
if func and callable(func):
|
|
@@ -4720,8 +6339,8 @@ class CSSLRuntime:
|
|
|
4720
6339
|
raise CSSLRuntimeError(
|
|
4721
6340
|
f"Unknown typed function: {name}<{type_param}>",
|
|
4722
6341
|
node.line,
|
|
4723
|
-
context=f"Available typed functions: OpenFind<type>,
|
|
4724
|
-
hint="Use
|
|
6342
|
+
context=f"Available typed functions: OpenFind<type>, cast<type>",
|
|
6343
|
+
hint="Use cast<int>(value), cast<string>(value), etc."
|
|
4725
6344
|
)
|
|
4726
6345
|
|
|
4727
6346
|
def _eval_new(self, node: ASTNode) -> CSSLInstance:
|
|
@@ -4744,12 +6363,15 @@ class CSSLRuntime:
|
|
|
4744
6363
|
|
|
4745
6364
|
# v4.2.6: Handle Namespace::ClassName lookup
|
|
4746
6365
|
if namespace:
|
|
4747
|
-
# Look up namespace
|
|
4748
|
-
|
|
4749
|
-
if
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
6366
|
+
# Look up namespace first
|
|
6367
|
+
ns_obj = self.scope.get(namespace)
|
|
6368
|
+
if ns_obj is None:
|
|
6369
|
+
ns_obj = self.global_scope.get(namespace)
|
|
6370
|
+
# v4.8: Handle CSSLNamespace objects
|
|
6371
|
+
if isinstance(ns_obj, CSSLNamespace):
|
|
6372
|
+
class_def = ns_obj.classes.get(class_name)
|
|
6373
|
+
elif isinstance(ns_obj, dict) and class_name in ns_obj:
|
|
6374
|
+
class_def = ns_obj[class_name]
|
|
4753
6375
|
|
|
4754
6376
|
# Get class definition from scope (if not found in namespace)
|
|
4755
6377
|
if class_def is None:
|
|
@@ -4851,13 +6473,41 @@ class CSSLRuntime:
|
|
|
4851
6473
|
self._call_parent_constructor(instance, evaluated_extends_args)
|
|
4852
6474
|
instance._parent_constructor_called = True
|
|
4853
6475
|
|
|
4854
|
-
#
|
|
6476
|
+
# v4.8.8: Separate constructors by modifier type
|
|
6477
|
+
regular_constructors = []
|
|
6478
|
+
secure_constructors = []
|
|
6479
|
+
callable_constructors = []
|
|
6480
|
+
|
|
4855
6481
|
for constr in constructors:
|
|
4856
|
-
|
|
6482
|
+
constr_info = constr.value
|
|
6483
|
+
if constr_info.get('is_secure'):
|
|
6484
|
+
secure_constructors.append(constr)
|
|
6485
|
+
elif constr_info.get('is_callable'):
|
|
6486
|
+
callable_constructors.append(constr)
|
|
6487
|
+
else:
|
|
6488
|
+
regular_constructors.append(constr)
|
|
6489
|
+
|
|
6490
|
+
# Store callable constructors on instance for manual invocation
|
|
6491
|
+
instance._callable_constructors = callable_constructors
|
|
6492
|
+
instance._secure_constructors = secure_constructors
|
|
4857
6493
|
|
|
4858
|
-
#
|
|
4859
|
-
|
|
4860
|
-
|
|
6494
|
+
# Execute regular constructors (not callable, not secure)
|
|
6495
|
+
# Wrap in try/except to call secure constructors on exception
|
|
6496
|
+
try:
|
|
6497
|
+
for constr in regular_constructors:
|
|
6498
|
+
self._call_constructor(instance, constr, args, kwargs, param_values)
|
|
6499
|
+
|
|
6500
|
+
# Call primary constructor (old-style) if defined
|
|
6501
|
+
if class_def.constructor:
|
|
6502
|
+
self._call_method(instance, class_def.constructor, args, kwargs)
|
|
6503
|
+
except Exception as e:
|
|
6504
|
+
# v4.8.8: Call all secure constructors on exception
|
|
6505
|
+
for secure_constr in secure_constructors:
|
|
6506
|
+
try:
|
|
6507
|
+
self._call_constructor(instance, secure_constr, args, kwargs, param_values)
|
|
6508
|
+
except:
|
|
6509
|
+
pass # Secure constructor failed, continue with others
|
|
6510
|
+
raise # Re-raise the original exception
|
|
4861
6511
|
|
|
4862
6512
|
return instance
|
|
4863
6513
|
|
|
@@ -4918,6 +6568,11 @@ class CSSLRuntime:
|
|
|
4918
6568
|
"""
|
|
4919
6569
|
from .cssl_builtins import CSSLizedPythonObject
|
|
4920
6570
|
|
|
6571
|
+
# v4.9.2: Handle &builtinName ++ syntax (stored as __builtins__::builtinName)
|
|
6572
|
+
if ref_class == '__builtins__' and ref_member:
|
|
6573
|
+
ref_class = ref_member
|
|
6574
|
+
ref_member = None
|
|
6575
|
+
|
|
4921
6576
|
# Handle direct function reference: &FunctionName ++ (no ::member part)
|
|
4922
6577
|
if ref_member is None and not is_constructor:
|
|
4923
6578
|
# ref_class is actually a function name
|
|
@@ -4925,8 +6580,20 @@ class CSSLRuntime:
|
|
|
4925
6580
|
if func_name.startswith('$'):
|
|
4926
6581
|
func_name = func_name[1:]
|
|
4927
6582
|
|
|
4928
|
-
#
|
|
4929
|
-
|
|
6583
|
+
# v4.9.2: For builtin hooks, check _original_functions FIRST to avoid infinite recursion
|
|
6584
|
+
# When &builtin ++ is used, scope contains the wrapper, but we want the original
|
|
6585
|
+
func = None
|
|
6586
|
+
if hasattr(self, '_original_functions'):
|
|
6587
|
+
func = self._original_functions.get(func_name)
|
|
6588
|
+
|
|
6589
|
+
# If not an overwritten builtin, check scope
|
|
6590
|
+
if func is None:
|
|
6591
|
+
func = self.scope.get(func_name) or self.global_scope.get(func_name)
|
|
6592
|
+
|
|
6593
|
+
# v4.9.2: Check in builtins._functions if still not found (for non-overwritten builtins)
|
|
6594
|
+
if func is None and hasattr(self, 'builtins') and hasattr(self.builtins, '_functions'):
|
|
6595
|
+
func = self.builtins._functions.get(func_name)
|
|
6596
|
+
|
|
4930
6597
|
if func is not None:
|
|
4931
6598
|
if isinstance(func, ASTNode) and func.type in ('function', 'FUNCTION'):
|
|
4932
6599
|
# Execute the referenced function
|
|
@@ -5077,6 +6744,14 @@ class CSSLRuntime:
|
|
|
5077
6744
|
prev_scope = self.scope
|
|
5078
6745
|
self.scope = new_scope
|
|
5079
6746
|
|
|
6747
|
+
# v4.8.8: Set 'this' and 'super' in constructor scope
|
|
6748
|
+
new_scope.set('this', instance)
|
|
6749
|
+
parent_class = getattr(instance, '_parent_class', None)
|
|
6750
|
+
if parent_class is None and hasattr(instance, '_class'):
|
|
6751
|
+
parent_class = getattr(instance._class, 'parent', None)
|
|
6752
|
+
if parent_class:
|
|
6753
|
+
new_scope.set('super', SuperProxy(instance, parent_class, self))
|
|
6754
|
+
|
|
5080
6755
|
try:
|
|
5081
6756
|
for stmt in constr_node.children:
|
|
5082
6757
|
self._execute_node(stmt)
|
|
@@ -5084,6 +6759,41 @@ class CSSLRuntime:
|
|
|
5084
6759
|
self.scope = prev_scope
|
|
5085
6760
|
self._current_instance = prev_instance
|
|
5086
6761
|
|
|
6762
|
+
def _call_destructor(self, instance: CSSLInstance, destr_node: ASTNode):
|
|
6763
|
+
"""v4.8.8: Call a destructor on an instance.
|
|
6764
|
+
|
|
6765
|
+
Destructors are defined with constr ~Name() { } syntax.
|
|
6766
|
+
They are called by delete(instance) or delete(instance, "Name").
|
|
6767
|
+
|
|
6768
|
+
Args:
|
|
6769
|
+
instance: The CSSLInstance to clean up
|
|
6770
|
+
destr_node: The destructor AST node
|
|
6771
|
+
"""
|
|
6772
|
+
# Save previous instance context
|
|
6773
|
+
prev_instance = self._current_instance
|
|
6774
|
+
self._current_instance = instance
|
|
6775
|
+
|
|
6776
|
+
# Create new scope for destructor
|
|
6777
|
+
new_scope = Scope(parent=self.scope)
|
|
6778
|
+
prev_scope = self.scope
|
|
6779
|
+
self.scope = new_scope
|
|
6780
|
+
|
|
6781
|
+
# v4.8.8: Set 'this' and 'super' in destructor scope
|
|
6782
|
+
new_scope.set('this', instance)
|
|
6783
|
+
parent_class = getattr(instance, '_parent_class', None)
|
|
6784
|
+
if parent_class is None and hasattr(instance, '_class'):
|
|
6785
|
+
parent_class = getattr(instance._class, 'parent', None)
|
|
6786
|
+
if parent_class:
|
|
6787
|
+
new_scope.set('super', SuperProxy(instance, parent_class, self))
|
|
6788
|
+
|
|
6789
|
+
try:
|
|
6790
|
+
# Execute destructor body
|
|
6791
|
+
for child in destr_node.children:
|
|
6792
|
+
self._execute_node(child)
|
|
6793
|
+
finally:
|
|
6794
|
+
self.scope = prev_scope
|
|
6795
|
+
self._current_instance = prev_instance
|
|
6796
|
+
|
|
5087
6797
|
def _eval_this_access(self, node: ASTNode) -> Any:
|
|
5088
6798
|
"""Evaluate 'this->member' access.
|
|
5089
6799
|
|
|
@@ -5182,6 +6892,16 @@ class CSSLRuntime:
|
|
|
5182
6892
|
# Check for undefined modifier
|
|
5183
6893
|
is_undefined = 'undefined' in modifiers
|
|
5184
6894
|
|
|
6895
|
+
# v4.7: Handle bytearrayed modifier for methods
|
|
6896
|
+
if 'bytearrayed' in modifiers:
|
|
6897
|
+
# Set up instance context for the bytearrayed execution
|
|
6898
|
+
old_instance = self._current_instance
|
|
6899
|
+
self._current_instance = instance
|
|
6900
|
+
try:
|
|
6901
|
+
return self._execute_bytearrayed_function(method_node)
|
|
6902
|
+
finally:
|
|
6903
|
+
self._current_instance = old_instance
|
|
6904
|
+
|
|
5185
6905
|
# Create new scope for method
|
|
5186
6906
|
new_scope = Scope(parent=self.scope)
|
|
5187
6907
|
|
|
@@ -5203,6 +6923,14 @@ class CSSLRuntime:
|
|
|
5203
6923
|
# Set up method context
|
|
5204
6924
|
self.scope = new_scope
|
|
5205
6925
|
self._current_instance = instance
|
|
6926
|
+
# v4.7: Set 'this' in scope for this.member access
|
|
6927
|
+
new_scope.set('this', instance)
|
|
6928
|
+
# v4.8.8: Set 'super' for parent method access
|
|
6929
|
+
parent_class = getattr(instance, '_parent_class', None)
|
|
6930
|
+
if parent_class is None and hasattr(instance, '_class'):
|
|
6931
|
+
parent_class = getattr(instance._class, 'parent', None)
|
|
6932
|
+
if parent_class:
|
|
6933
|
+
new_scope.set('super', SuperProxy(instance, parent_class, self))
|
|
5206
6934
|
|
|
5207
6935
|
original_return = None
|
|
5208
6936
|
try:
|
|
@@ -5240,14 +6968,103 @@ class CSSLRuntime:
|
|
|
5240
6968
|
# If no return in appended code, use original's return
|
|
5241
6969
|
return original_return
|
|
5242
6970
|
|
|
6971
|
+
def _call_method_with_super(self, instance: CSSLInstance, method_node: ASTNode,
|
|
6972
|
+
args: list, kwargs: dict, super_parent_class) -> Any:
|
|
6973
|
+
"""v4.8.8: Call a method with a specific super context.
|
|
6974
|
+
|
|
6975
|
+
Used when calling parent methods via super->method() to ensure the super
|
|
6976
|
+
inside the parent method points to the grandparent, not the instance's parent.
|
|
6977
|
+
|
|
6978
|
+
Args:
|
|
6979
|
+
instance: The CSSLInstance to use as 'this'
|
|
6980
|
+
method_node: The method AST node to execute
|
|
6981
|
+
args: Method arguments
|
|
6982
|
+
kwargs: Keyword arguments
|
|
6983
|
+
super_parent_class: The class to use for 'super' (typically grandparent)
|
|
6984
|
+
"""
|
|
6985
|
+
kwargs = kwargs or {}
|
|
6986
|
+
func_info = method_node.value
|
|
6987
|
+
params = func_info.get('params', [])
|
|
6988
|
+
modifiers = func_info.get('modifiers', [])
|
|
6989
|
+
|
|
6990
|
+
# Check for undefined modifier
|
|
6991
|
+
is_undefined = 'undefined' in modifiers
|
|
6992
|
+
|
|
6993
|
+
# Create new scope for method
|
|
6994
|
+
new_scope = Scope(parent=self.scope)
|
|
6995
|
+
|
|
6996
|
+
# Bind parameters
|
|
6997
|
+
for i, param in enumerate(params):
|
|
6998
|
+
param_name = param['name'] if isinstance(param, dict) else param
|
|
6999
|
+
if param_name in kwargs:
|
|
7000
|
+
new_scope.set(param_name, kwargs[param_name])
|
|
7001
|
+
elif i < len(args):
|
|
7002
|
+
new_scope.set(param_name, args[i])
|
|
7003
|
+
else:
|
|
7004
|
+
new_scope.set(param_name, None)
|
|
7005
|
+
|
|
7006
|
+
# Save current state
|
|
7007
|
+
old_scope = self.scope
|
|
7008
|
+
old_instance = self._current_instance
|
|
7009
|
+
|
|
7010
|
+
# Set up method context
|
|
7011
|
+
self.scope = new_scope
|
|
7012
|
+
self._current_instance = instance
|
|
7013
|
+
# Set 'this' in scope for this.member access
|
|
7014
|
+
new_scope.set('this', instance)
|
|
7015
|
+
# Set 'super' with the specified parent class (typically grandparent)
|
|
7016
|
+
if super_parent_class:
|
|
7017
|
+
new_scope.set('super', SuperProxy(instance, super_parent_class, self))
|
|
7018
|
+
|
|
7019
|
+
try:
|
|
7020
|
+
for child in method_node.children:
|
|
7021
|
+
if not self._running:
|
|
7022
|
+
break
|
|
7023
|
+
self._execute_node(child)
|
|
7024
|
+
except CSSLReturn as ret:
|
|
7025
|
+
return ret.value
|
|
7026
|
+
except Exception as e:
|
|
7027
|
+
if is_undefined:
|
|
7028
|
+
return None
|
|
7029
|
+
raise
|
|
7030
|
+
finally:
|
|
7031
|
+
# Restore previous state
|
|
7032
|
+
self.scope = old_scope
|
|
7033
|
+
self._current_instance = old_instance
|
|
7034
|
+
|
|
7035
|
+
return None
|
|
7036
|
+
|
|
7037
|
+
# v4.7.1: Attribute blacklist for security - prevents access to dangerous Python attributes
|
|
7038
|
+
ATTR_BLACKLIST = frozenset({
|
|
7039
|
+
'__class__', '__dict__', '__bases__', '__mro__', '__subclasses__',
|
|
7040
|
+
'__import__', '__builtins__', '__globals__', '__locals__',
|
|
7041
|
+
'__code__', '__call__', '__delattr__', '__setattr__', '__getattribute__',
|
|
7042
|
+
'__reduce__', '__reduce_ex__', '__init_subclass__', '__new__',
|
|
7043
|
+
'__module__', '__qualname__', '__annotations__', '__slots__',
|
|
7044
|
+
'__weakref__', '__func__', '__self__', '__wrapped__',
|
|
7045
|
+
})
|
|
7046
|
+
|
|
5243
7047
|
def _eval_member_access(self, node: ASTNode) -> Any:
|
|
5244
7048
|
"""Evaluate member access"""
|
|
5245
7049
|
obj = self._evaluate(node.value.get('object'))
|
|
5246
7050
|
member = node.value.get('member')
|
|
5247
7051
|
|
|
7052
|
+
# v4.9.3: Special handling for null checks - null.is_null() returns true
|
|
5248
7053
|
if obj is None:
|
|
7054
|
+
if member == 'is_null':
|
|
7055
|
+
return lambda: True
|
|
5249
7056
|
return None
|
|
5250
7057
|
|
|
7058
|
+
# v4.7.1: Security - block access to dangerous Python attributes
|
|
7059
|
+
if member in self.ATTR_BLACKLIST:
|
|
7060
|
+
raise CSSLRuntimeError(
|
|
7061
|
+
f"Access to '{member}' is not allowed for security reasons."
|
|
7062
|
+
)
|
|
7063
|
+
if member.startswith('__') and member.endswith('__'):
|
|
7064
|
+
raise CSSLRuntimeError(
|
|
7065
|
+
f"Access to dunder attributes ('{member}') is not allowed."
|
|
7066
|
+
)
|
|
7067
|
+
|
|
5251
7068
|
# Special handling for Parameter.return() -> Parameter.return_()
|
|
5252
7069
|
# since 'return' is a Python keyword
|
|
5253
7070
|
if isinstance(obj, Parameter) and member == 'return':
|
|
@@ -5255,28 +7072,39 @@ class CSSLRuntime:
|
|
|
5255
7072
|
|
|
5256
7073
|
# === ServiceDefinition (from include()) ===
|
|
5257
7074
|
if isinstance(obj, ServiceDefinition):
|
|
5258
|
-
# Check
|
|
7075
|
+
# v4.8.6: Check classes first (for new MyClass() pattern)
|
|
7076
|
+
if member in obj.classes:
|
|
7077
|
+
return obj.classes[member]
|
|
7078
|
+
# Check functions
|
|
5259
7079
|
if member in obj.functions:
|
|
5260
7080
|
func_node = obj.functions[member]
|
|
5261
7081
|
return lambda *args, **kwargs: self._call_function(func_node, list(args), kwargs)
|
|
5262
|
-
# Check structs
|
|
7082
|
+
# Check structs
|
|
5263
7083
|
if member in obj.structs:
|
|
5264
7084
|
return obj.structs[member]
|
|
7085
|
+
# v4.8.6: Check enums
|
|
7086
|
+
if member in obj.enums:
|
|
7087
|
+
return obj.enums[member]
|
|
7088
|
+
# v4.8.6: Check namespaces
|
|
7089
|
+
if member in obj.namespaces:
|
|
7090
|
+
return obj.namespaces[member]
|
|
5265
7091
|
# Check regular attributes
|
|
5266
7092
|
if hasattr(obj, member):
|
|
5267
7093
|
return getattr(obj, member)
|
|
5268
7094
|
# Build helpful error
|
|
5269
|
-
available = list(obj.
|
|
7095
|
+
available = (list(obj.classes.keys()) + list(obj.functions.keys()) +
|
|
7096
|
+
list(obj.structs.keys()) + list(obj.enums.keys()) +
|
|
7097
|
+
list(obj.namespaces.keys()))
|
|
5270
7098
|
similar = _find_similar_names(member, available)
|
|
5271
7099
|
if similar:
|
|
5272
7100
|
hint = f"Did you mean: {', '.join(similar)}?"
|
|
5273
7101
|
elif available:
|
|
5274
7102
|
hint = f"Available: {', '.join(available[:10])}"
|
|
5275
7103
|
else:
|
|
5276
|
-
hint = "No
|
|
7104
|
+
hint = "No exports defined in this module."
|
|
5277
7105
|
raise self._format_error(
|
|
5278
7106
|
node.line if hasattr(node, 'line') else 0,
|
|
5279
|
-
f"Module has no
|
|
7107
|
+
f"Module has no export '{member}'",
|
|
5280
7108
|
hint
|
|
5281
7109
|
)
|
|
5282
7110
|
|
|
@@ -5313,6 +7141,35 @@ class CSSLRuntime:
|
|
|
5313
7141
|
hint
|
|
5314
7142
|
)
|
|
5315
7143
|
|
|
7144
|
+
# === v4.8.8: SUPER PROXY - Parent class method access ===
|
|
7145
|
+
if isinstance(obj, SuperProxy):
|
|
7146
|
+
# Get method from parent class
|
|
7147
|
+
method_node = obj.get_method(member)
|
|
7148
|
+
if method_node:
|
|
7149
|
+
# Return a callable that invokes the parent method with correct super context
|
|
7150
|
+
# Key: super inside the parent method should point to grandparent!
|
|
7151
|
+
parent_of_parent = getattr(obj._parent_class, 'parent', None)
|
|
7152
|
+
|
|
7153
|
+
def call_parent_method(*args, **kwargs):
|
|
7154
|
+
return self._call_method_with_super(
|
|
7155
|
+
obj._instance, method_node, list(args), kwargs,
|
|
7156
|
+
parent_of_parent # Pass grandparent as the super context
|
|
7157
|
+
)
|
|
7158
|
+
return call_parent_method
|
|
7159
|
+
|
|
7160
|
+
# Check for member
|
|
7161
|
+
member_val = obj.get_member(member)
|
|
7162
|
+
if member_val is not None:
|
|
7163
|
+
return member_val
|
|
7164
|
+
|
|
7165
|
+
# Error: no such method in parent
|
|
7166
|
+
parent_name = obj._parent_class.name if obj._parent_class else "parent"
|
|
7167
|
+
raise self._format_error(
|
|
7168
|
+
node.line if hasattr(node, 'line') else 0,
|
|
7169
|
+
f"Parent class '{parent_name}' has no method '{member}'",
|
|
7170
|
+
"Check that the parent class defines this method."
|
|
7171
|
+
)
|
|
7172
|
+
|
|
5316
7173
|
# === UNIVERSAL INSTANCE METHODS ===
|
|
5317
7174
|
from .cssl_types import UniversalInstance
|
|
5318
7175
|
if isinstance(obj, UniversalInstance):
|
|
@@ -5362,11 +7219,28 @@ class CSSLRuntime:
|
|
|
5362
7219
|
return string_methods
|
|
5363
7220
|
|
|
5364
7221
|
# === LIST/ARRAY METHODS for plain lists ===
|
|
5365
|
-
|
|
7222
|
+
# Exclude DataStruct and other custom containers - they have their own methods
|
|
7223
|
+
from .cssl_types import DataStruct, List, Dictionary, Queue, Bit, Byte, Address
|
|
7224
|
+
if isinstance(obj, list) and not isinstance(obj, (Stack, Vector, Array, DataStruct, List)):
|
|
5366
7225
|
list_methods = self._get_list_method(obj, member)
|
|
5367
7226
|
if list_methods is not None:
|
|
5368
7227
|
return list_methods
|
|
5369
7228
|
|
|
7229
|
+
# === CSSL CONTAINER TYPES (Stack, Vector, Array, Map, Queue, etc.) ===
|
|
7230
|
+
# v4.8.7: Explicit handling for CSSL container methods to ensure they're found
|
|
7231
|
+
# v4.9.0: Added Bit and Byte types
|
|
7232
|
+
if isinstance(obj, (Stack, Vector, Array, DataStruct, List, Dictionary, Map, Queue, Bit, Byte, Address)):
|
|
7233
|
+
# Try to get the method directly from the object
|
|
7234
|
+
method = getattr(obj, member, None)
|
|
7235
|
+
if method is not None:
|
|
7236
|
+
return method
|
|
7237
|
+
# Also check the class for methods (handles inheritance)
|
|
7238
|
+
for cls in type(obj).__mro__:
|
|
7239
|
+
if hasattr(cls, member):
|
|
7240
|
+
method = getattr(obj, member)
|
|
7241
|
+
if method is not None:
|
|
7242
|
+
return method
|
|
7243
|
+
|
|
5370
7244
|
if hasattr(obj, member):
|
|
5371
7245
|
return getattr(obj, member)
|
|
5372
7246
|
|
|
@@ -5492,10 +7366,12 @@ class CSSLRuntime:
|
|
|
5492
7366
|
elif method == 'toInt' or method == 'toInteger':
|
|
5493
7367
|
return lambda base=10: int(s, base) if s.lstrip('-').isdigit() else 0
|
|
5494
7368
|
elif method == 'toFloat' or method == 'toDouble':
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
7369
|
+
def _to_float():
|
|
7370
|
+
try:
|
|
7371
|
+
return float(s)
|
|
7372
|
+
except (ValueError, TypeError):
|
|
7373
|
+
return 0.0
|
|
7374
|
+
return _to_float
|
|
5499
7375
|
elif method == 'toBool':
|
|
5500
7376
|
return lambda: s.lower() in ('true', '1', 'yes', 'on')
|
|
5501
7377
|
|
|
@@ -5559,6 +7435,8 @@ class CSSLRuntime:
|
|
|
5559
7435
|
return push_back_item
|
|
5560
7436
|
elif method == 'toArray':
|
|
5561
7437
|
return lambda: list(lst)
|
|
7438
|
+
elif method == 'content':
|
|
7439
|
+
return lambda: list(lst)
|
|
5562
7440
|
# === C++ ITERATOR STYLE ===
|
|
5563
7441
|
elif method == 'begin':
|
|
5564
7442
|
return lambda: 0
|
|
@@ -6051,17 +7929,30 @@ def run_cssl(source: str, service_engine=None, force_python: bool = False) -> An
|
|
|
6051
7929
|
Returns:
|
|
6052
7930
|
Execution result
|
|
6053
7931
|
"""
|
|
7932
|
+
# v4.8.8: Python-only builtins that require subprocess/import magic
|
|
7933
|
+
# These don't work in C++ interpreter and must use Python
|
|
7934
|
+
# v4.9.2: Added address/reflect/memory for pointer system, hooks syntax
|
|
7935
|
+
PYTHON_ONLY_FEATURES = ['includecpp(', 'snapshot(', '%', 'address(', 'reflect(', 'memory(', ' &', '&$', 'destroy(', 'execute(']
|
|
7936
|
+
|
|
7937
|
+
# Check if source uses Python-only features
|
|
7938
|
+
needs_python = any(feat in source for feat in PYTHON_ONLY_FEATURES)
|
|
7939
|
+
|
|
6054
7940
|
# Try C++ interpreter first (10-20x faster)
|
|
6055
|
-
if not force_python:
|
|
7941
|
+
if not force_python and not needs_python:
|
|
6056
7942
|
cpp_interp = _get_cpp_interpreter()
|
|
6057
7943
|
if cpp_interp:
|
|
6058
7944
|
try:
|
|
6059
7945
|
return cpp_interp.run_string(source)
|
|
6060
7946
|
except Exception as e:
|
|
6061
7947
|
# C++ doesn't support this feature, fall back to Python
|
|
7948
|
+
# v4.8.5: Extended fallback triggers for advanced CSSL syntax
|
|
6062
7949
|
error_msg = str(e).lower()
|
|
6063
|
-
|
|
6064
|
-
|
|
7950
|
+
fallback_triggers = [
|
|
7951
|
+
'unsupported', 'not implemented', 'unexpected', 'expected',
|
|
7952
|
+
'syntax error', 'unknown identifier', 'undefined', 'not defined'
|
|
7953
|
+
]
|
|
7954
|
+
should_fallback = any(trigger in error_msg for trigger in fallback_triggers)
|
|
7955
|
+
if should_fallback:
|
|
6065
7956
|
pass # Fall through to Python
|
|
6066
7957
|
else:
|
|
6067
7958
|
# Re-raise actual errors
|
|
@@ -6077,15 +7968,34 @@ def run_cssl_file(filepath: str, service_engine=None, force_python: bool = False
|
|
|
6077
7968
|
|
|
6078
7969
|
Uses C++ interpreter for maximum performance when available.
|
|
6079
7970
|
"""
|
|
7971
|
+
# v4.8.8: Python-only builtins that require subprocess/import magic
|
|
7972
|
+
# v4.9.2: Added address/reflect/memory for pointer system, hooks syntax
|
|
7973
|
+
PYTHON_ONLY_FEATURES = ['includecpp(', 'snapshot(', '%', 'address(', 'reflect(', 'memory(', ' &', '&$', 'destroy(', 'execute(']
|
|
7974
|
+
|
|
7975
|
+
# Check file content for Python-only features
|
|
7976
|
+
needs_python = False
|
|
7977
|
+
try:
|
|
7978
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
7979
|
+
source = f.read()
|
|
7980
|
+
needs_python = any(feat in source for feat in PYTHON_ONLY_FEATURES)
|
|
7981
|
+
except Exception:
|
|
7982
|
+
pass # If we can't read, let the runtime handle it
|
|
7983
|
+
|
|
6080
7984
|
# Try C++ interpreter first
|
|
6081
|
-
if not force_python:
|
|
7985
|
+
if not force_python and not needs_python:
|
|
6082
7986
|
cpp_interp = _get_cpp_interpreter()
|
|
6083
7987
|
if cpp_interp:
|
|
6084
7988
|
try:
|
|
6085
7989
|
return cpp_interp.run(filepath)
|
|
6086
7990
|
except Exception as e:
|
|
7991
|
+
# v4.8.5: Extended fallback triggers
|
|
6087
7992
|
error_msg = str(e).lower()
|
|
6088
|
-
|
|
7993
|
+
fallback_triggers = [
|
|
7994
|
+
'unsupported', 'not implemented', 'unexpected', 'expected',
|
|
7995
|
+
'syntax error', 'unknown identifier', 'undefined', 'not defined'
|
|
7996
|
+
]
|
|
7997
|
+
should_fallback = any(trigger in error_msg for trigger in fallback_triggers)
|
|
7998
|
+
if should_fallback:
|
|
6089
7999
|
pass # Fall through to Python
|
|
6090
8000
|
else:
|
|
6091
8001
|
raise CSSLRuntimeError(str(e))
|