IncludeCPP 3.4.22__tar.gz → 3.5.2__tar.gz
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-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/PKG-INFO +1 -1
- {includecpp-3.4.22 → includecpp-3.5.2}/PKG-INFO +1 -1
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/__init__.py +1 -1
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/cli/commands.py +32 -6
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_builtins.py +41 -2
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_parser.py +61 -13
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_runtime.py +88 -15
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_types.py +339 -2
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl_bridge.py +100 -4
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl_bridge.pyi +177 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/pyproject.toml +1 -1
- {includecpp-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/SOURCES.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/dependency_links.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/entry_points.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/requires.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/IncludeCPP.egg-info/top_level.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/LICENSE +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/MANIFEST.in +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/README.md +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/__init__.pyi +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/__main__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/cli/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/cli/config_parser.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/ai_integration.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/build_manager.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cpp_api.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cpp_api.pyi +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cppy_converter.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/CSSL_DOCUMENTATION.md +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_events.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_modules.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/cssl/cssl_syntax.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/error_catalog.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/error_formatter.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/exceptions.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/path_discovery.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/project_ui.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/core/settings_ui.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/generator/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/generator/parser.cpp +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/generator/parser.h +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/generator/type_resolver.cpp +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/generator/type_resolver.h +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/py.typed +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/templates/cpp.proj.template +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/vscode/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/vscode/cssl/__init__.py +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/vscode/cssl/language-configuration.json +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/vscode/cssl/package.json +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/requirements.txt +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/setup.cfg +0 -0
- {includecpp-3.4.22 → includecpp-3.5.2}/setup.py +0 -0
|
@@ -7139,20 +7139,21 @@ def cppy_types():
|
|
|
7139
7139
|
# EXEC - Interactive Code Execution
|
|
7140
7140
|
# ============================================================================
|
|
7141
7141
|
|
|
7142
|
-
@cli.command()
|
|
7143
|
-
@click.argument('lang', type=click.Choice(['py', 'cpp', 'python', 'c++']))
|
|
7142
|
+
@cli.command(name='exec')
|
|
7143
|
+
@click.argument('lang', type=click.Choice(['py', 'cpp', 'python', 'c++', 'cssl']))
|
|
7144
7144
|
@click.argument('path', required=False, type=click.Path())
|
|
7145
7145
|
@click.option('--all', 'import_all', is_flag=True, help='Import all available modules')
|
|
7146
|
-
def
|
|
7146
|
+
def exec_repl(lang, path, import_all):
|
|
7147
7147
|
"""Execute code interactively for quick testing.
|
|
7148
7148
|
|
|
7149
|
-
Run Python
|
|
7149
|
+
Run Python, C++ or CSSL code snippets without creating files.
|
|
7150
7150
|
Perfect for testing your IncludeCPP modules quickly.
|
|
7151
7151
|
|
|
7152
7152
|
\b
|
|
7153
7153
|
Usage:
|
|
7154
7154
|
includecpp exec py # Interactive Python
|
|
7155
7155
|
includecpp exec cpp # Interactive C++
|
|
7156
|
+
includecpp exec cssl # Interactive CSSL
|
|
7156
7157
|
includecpp exec py mymodule # Auto-import mymodule
|
|
7157
7158
|
includecpp exec py plugins/x.cp # Auto-import from plugin
|
|
7158
7159
|
includecpp exec py --all # Import all modules
|
|
@@ -7178,7 +7179,8 @@ def exec(lang, path, import_all):
|
|
|
7178
7179
|
|
|
7179
7180
|
# Normalize language
|
|
7180
7181
|
is_python = lang in ('py', 'python')
|
|
7181
|
-
|
|
7182
|
+
is_cssl = lang == 'cssl'
|
|
7183
|
+
lang_name = 'Python' if is_python else ('CSSL' if is_cssl else 'C++')
|
|
7182
7184
|
|
|
7183
7185
|
# Build imports/includes
|
|
7184
7186
|
imports = []
|
|
@@ -7286,6 +7288,7 @@ def exec(lang, path, import_all):
|
|
|
7286
7288
|
|
|
7287
7289
|
if is_python:
|
|
7288
7290
|
# Execute Python code
|
|
7291
|
+
import builtins
|
|
7289
7292
|
full_code = '\n'.join(code_lines)
|
|
7290
7293
|
try:
|
|
7291
7294
|
# Use exec with captured output
|
|
@@ -7299,7 +7302,7 @@ def exec(lang, path, import_all):
|
|
|
7299
7302
|
exec_globals = {'__name__': '__main__'}
|
|
7300
7303
|
|
|
7301
7304
|
with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
|
|
7302
|
-
exec(full_code, exec_globals)
|
|
7305
|
+
builtins.exec(full_code, exec_globals)
|
|
7303
7306
|
|
|
7304
7307
|
stdout_val = stdout_capture.getvalue()
|
|
7305
7308
|
stderr_val = stderr_capture.getvalue()
|
|
@@ -7315,6 +7318,29 @@ def exec(lang, path, import_all):
|
|
|
7315
7318
|
except Exception as e:
|
|
7316
7319
|
click.secho(f"Error: {e}", fg='red')
|
|
7317
7320
|
|
|
7321
|
+
elif is_cssl:
|
|
7322
|
+
# Execute CSSL code
|
|
7323
|
+
full_code = '\n'.join(lines)
|
|
7324
|
+
try:
|
|
7325
|
+
from ..core.cssl_bridge import CsslLang
|
|
7326
|
+
cssl_lang = CsslLang()
|
|
7327
|
+
result = cssl_lang.exec(full_code)
|
|
7328
|
+
|
|
7329
|
+
# Print any output from the execution
|
|
7330
|
+
output = cssl_lang.get_output()
|
|
7331
|
+
if output:
|
|
7332
|
+
for line in output:
|
|
7333
|
+
click.echo(line)
|
|
7334
|
+
|
|
7335
|
+
if result is not None:
|
|
7336
|
+
click.echo(result)
|
|
7337
|
+
|
|
7338
|
+
if not output and result is None:
|
|
7339
|
+
click.secho("(no output)", fg='bright_black')
|
|
7340
|
+
|
|
7341
|
+
except Exception as e:
|
|
7342
|
+
click.secho(f"Error: {e}", fg='red')
|
|
7343
|
+
|
|
7318
7344
|
else:
|
|
7319
7345
|
# Execute C++ code
|
|
7320
7346
|
# Build a complete C++ program
|
|
@@ -1902,15 +1902,54 @@ class CSSLBuiltins:
|
|
|
1902
1902
|
from .cssl_types import OpenQuote
|
|
1903
1903
|
return OpenQuote(db_ref)
|
|
1904
1904
|
|
|
1905
|
-
def builtin_openfind(self, combo_or_type: Any, index: int = 0) -> Any:
|
|
1905
|
+
def builtin_openfind(self, combo_or_type: Any, index: int = 0, params: list = None) -> Any:
|
|
1906
1906
|
"""Find open parameter by type or combo space.
|
|
1907
1907
|
|
|
1908
|
-
Usage:
|
|
1908
|
+
Usage:
|
|
1909
|
+
OpenFind<string>(0) # Find first string at position 0
|
|
1910
|
+
OpenFind(&@comboSpace) # Find using combo filter
|
|
1911
|
+
|
|
1912
|
+
When using with open parameters:
|
|
1913
|
+
open define myFunc(open Params) {
|
|
1914
|
+
string name = OpenFind<string>(0); // Find nearest string at index 0
|
|
1915
|
+
int age = OpenFind<int>(1); // Find nearest int at index 1
|
|
1916
|
+
}
|
|
1909
1917
|
"""
|
|
1910
1918
|
from .cssl_types import Combo
|
|
1911
1919
|
|
|
1912
1920
|
if isinstance(combo_or_type, Combo):
|
|
1921
|
+
# Find by combo space
|
|
1922
|
+
if params:
|
|
1923
|
+
return combo_or_type.find_match(params)
|
|
1913
1924
|
return combo_or_type.find_match([])
|
|
1925
|
+
|
|
1926
|
+
# Type-based search
|
|
1927
|
+
target_type = combo_or_type
|
|
1928
|
+
if params is None:
|
|
1929
|
+
params = []
|
|
1930
|
+
|
|
1931
|
+
# Map type names to Python types
|
|
1932
|
+
type_map = {
|
|
1933
|
+
'string': str, 'str': str,
|
|
1934
|
+
'int': int, 'integer': int,
|
|
1935
|
+
'float': float, 'double': float,
|
|
1936
|
+
'bool': bool, 'boolean': bool,
|
|
1937
|
+
'list': list, 'array': list,
|
|
1938
|
+
'dict': dict, 'dictionary': dict
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
python_type = type_map.get(str(target_type).lower(), None)
|
|
1942
|
+
if python_type is None:
|
|
1943
|
+
return None
|
|
1944
|
+
|
|
1945
|
+
# Find the nearest matching type from index position
|
|
1946
|
+
matches_found = 0
|
|
1947
|
+
for i, param in enumerate(params):
|
|
1948
|
+
if isinstance(param, python_type):
|
|
1949
|
+
if matches_found == index:
|
|
1950
|
+
return param
|
|
1951
|
+
matches_found += 1
|
|
1952
|
+
|
|
1914
1953
|
return None
|
|
1915
1954
|
|
|
1916
1955
|
|
|
@@ -125,6 +125,7 @@ KEYWORDS = {
|
|
|
125
125
|
'package', 'package-includes', 'exec', 'as', 'global',
|
|
126
126
|
# CSSL Type Keywords
|
|
127
127
|
'int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
128
|
+
'list', 'dictionary', 'dict', # Python-like types
|
|
128
129
|
'dynamic', # No type declaration (slow but flexible)
|
|
129
130
|
'undefined', # Function errors ignored
|
|
130
131
|
'open', # Accept any parameter type
|
|
@@ -152,7 +153,7 @@ TYPE_LITERALS = {'list', 'dict'}
|
|
|
152
153
|
# Generic type keywords that use <T> syntax
|
|
153
154
|
TYPE_GENERICS = {
|
|
154
155
|
'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo',
|
|
155
|
-
'vector', 'stack', 'array', 'openquote'
|
|
156
|
+
'vector', 'stack', 'array', 'openquote', 'list', 'dictionary'
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
# Functions that accept type parameters: FuncName<type>(args)
|
|
@@ -660,6 +661,7 @@ class CSSLParser:
|
|
|
660
661
|
def _is_type_keyword(self, value: str) -> bool:
|
|
661
662
|
"""Check if a keyword is a type declaration"""
|
|
662
663
|
return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
664
|
+
'list', 'dictionary', 'dict',
|
|
663
665
|
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
|
|
664
666
|
|
|
665
667
|
def _looks_like_function_declaration(self) -> bool:
|
|
@@ -877,7 +879,8 @@ class CSSLParser:
|
|
|
877
879
|
# Skip known type keywords
|
|
878
880
|
type_keywords = {'int', 'string', 'float', 'bool', 'dynamic', 'void',
|
|
879
881
|
'stack', 'vector', 'datastruct', 'dataspace', 'shuffled',
|
|
880
|
-
'iterator', 'combo', 'array', 'openquote', 'json'
|
|
882
|
+
'iterator', 'combo', 'array', 'openquote', 'json',
|
|
883
|
+
'list', 'dictionary', 'dict'}
|
|
881
884
|
if type_name not in type_keywords:
|
|
882
885
|
return False
|
|
883
886
|
|
|
@@ -1505,7 +1508,13 @@ class CSSLParser:
|
|
|
1505
1508
|
return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
|
|
1506
1509
|
|
|
1507
1510
|
def _parse_python_style_for(self) -> ASTNode:
|
|
1508
|
-
"""Parse Python-style for loop: for (i in range(
|
|
1511
|
+
"""Parse Python-style for loop: for (i in range(...)) { }
|
|
1512
|
+
|
|
1513
|
+
Supports:
|
|
1514
|
+
for (i in range(n)) { } - 0 to n-1
|
|
1515
|
+
for (i in range(start, end)) { } - start to end-1
|
|
1516
|
+
for (i in range(start, end, step)) { }
|
|
1517
|
+
"""
|
|
1509
1518
|
var_name = self._advance().value
|
|
1510
1519
|
self._expect(TokenType.KEYWORD) # 'in'
|
|
1511
1520
|
|
|
@@ -1518,15 +1527,27 @@ class CSSLParser:
|
|
|
1518
1527
|
self.error(f"Expected 'range', got {self._peek().value}")
|
|
1519
1528
|
|
|
1520
1529
|
self._expect(TokenType.PAREN_START)
|
|
1521
|
-
|
|
1522
|
-
self._expect(TokenType.COMMA)
|
|
1523
|
-
end = self._parse_expression()
|
|
1530
|
+
first_arg = self._parse_expression()
|
|
1524
1531
|
|
|
1525
|
-
#
|
|
1532
|
+
# Check if there are more arguments
|
|
1533
|
+
start = None
|
|
1534
|
+
end = None
|
|
1526
1535
|
step = None
|
|
1536
|
+
|
|
1527
1537
|
if self._check(TokenType.COMMA):
|
|
1538
|
+
# range(start, end) or range(start, end, step)
|
|
1528
1539
|
self._advance() # consume comma
|
|
1529
|
-
|
|
1540
|
+
start = first_arg
|
|
1541
|
+
end = self._parse_expression()
|
|
1542
|
+
|
|
1543
|
+
# Optional step parameter
|
|
1544
|
+
if self._check(TokenType.COMMA):
|
|
1545
|
+
self._advance() # consume comma
|
|
1546
|
+
step = self._parse_expression()
|
|
1547
|
+
else:
|
|
1548
|
+
# range(n) - single argument means 0 to n-1
|
|
1549
|
+
start = ASTNode('literal', value={'type': 'int', 'value': 0})
|
|
1550
|
+
end = first_arg
|
|
1530
1551
|
|
|
1531
1552
|
self._expect(TokenType.PAREN_END)
|
|
1532
1553
|
self._expect(TokenType.PAREN_END)
|
|
@@ -1734,12 +1755,26 @@ class CSSLParser:
|
|
|
1734
1755
|
self._match(TokenType.SEMICOLON)
|
|
1735
1756
|
return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'add', 'filter': filter_info})
|
|
1736
1757
|
|
|
1737
|
-
# === MINUS INJECTION: -<== (move & remove from source) ===
|
|
1758
|
+
# === MINUS INJECTION: -<== or -<==[n] (move & remove from source) ===
|
|
1738
1759
|
if self._match(TokenType.INJECT_MINUS_LEFT):
|
|
1760
|
+
# Check for indexed deletion: -<==[n] (only numbers, not filters)
|
|
1761
|
+
remove_index = None
|
|
1762
|
+
if self._check(TokenType.BRACKET_START):
|
|
1763
|
+
# Peek ahead to see if this is an index [n] or a filter [type::helper=...]
|
|
1764
|
+
# Only consume if it's a simple number index
|
|
1765
|
+
saved_pos = self._current
|
|
1766
|
+
self._advance() # consume [
|
|
1767
|
+
if self._check(TokenType.NUMBER):
|
|
1768
|
+
remove_index = int(self._advance().value)
|
|
1769
|
+
self._expect(TokenType.BRACKET_END)
|
|
1770
|
+
else:
|
|
1771
|
+
# Not a number - restore position for filter parsing
|
|
1772
|
+
self._current = saved_pos
|
|
1773
|
+
|
|
1739
1774
|
filter_info = self._parse_injection_filter()
|
|
1740
1775
|
source = self._parse_expression()
|
|
1741
1776
|
self._match(TokenType.SEMICOLON)
|
|
1742
|
-
return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'move', 'filter': filter_info})
|
|
1777
|
+
return ASTNode('inject', value={'target': expr, 'source': source, 'mode': 'move', 'filter': filter_info, 'index': remove_index})
|
|
1743
1778
|
|
|
1744
1779
|
# === CODE INFUSION: <<== (inject code into function) ===
|
|
1745
1780
|
if self._match(TokenType.INFUSE_LEFT):
|
|
@@ -1763,16 +1798,29 @@ class CSSLParser:
|
|
|
1763
1798
|
self._match(TokenType.SEMICOLON)
|
|
1764
1799
|
return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'add'})
|
|
1765
1800
|
|
|
1766
|
-
# === CODE INFUSION MINUS: -<<== (remove code from function) ===
|
|
1801
|
+
# === CODE INFUSION MINUS: -<<== or -<<==[n] (remove code from function) ===
|
|
1767
1802
|
if self._match(TokenType.INFUSE_MINUS_LEFT):
|
|
1803
|
+
# Check for indexed deletion: -<<==[n] (only numbers)
|
|
1804
|
+
remove_index = None
|
|
1805
|
+
if self._check(TokenType.BRACKET_START):
|
|
1806
|
+
# Peek ahead to see if this is an index [n] or something else
|
|
1807
|
+
saved_pos = self._current
|
|
1808
|
+
self._advance() # consume [
|
|
1809
|
+
if self._check(TokenType.NUMBER):
|
|
1810
|
+
remove_index = int(self._advance().value)
|
|
1811
|
+
self._expect(TokenType.BRACKET_END)
|
|
1812
|
+
else:
|
|
1813
|
+
# Not a number - restore position
|
|
1814
|
+
self._current = saved_pos
|
|
1815
|
+
|
|
1768
1816
|
if self._check(TokenType.BLOCK_START):
|
|
1769
1817
|
code_block = self._parse_action_block()
|
|
1770
1818
|
self._match(TokenType.SEMICOLON)
|
|
1771
|
-
return ASTNode('infuse', value={'target': expr, 'code': code_block, 'mode': 'remove'})
|
|
1819
|
+
return ASTNode('infuse', value={'target': expr, 'code': code_block, 'mode': 'remove', 'index': remove_index})
|
|
1772
1820
|
else:
|
|
1773
1821
|
source = self._parse_expression()
|
|
1774
1822
|
self._match(TokenType.SEMICOLON)
|
|
1775
|
-
return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'remove'})
|
|
1823
|
+
return ASTNode('infuse', value={'target': expr, 'source': source, 'mode': 'remove', 'index': remove_index})
|
|
1776
1824
|
|
|
1777
1825
|
# === RIGHT-SIDE OPERATORS ===
|
|
1778
1826
|
|
|
@@ -14,7 +14,7 @@ from .cssl_builtins import CSSLBuiltins
|
|
|
14
14
|
from .cssl_modules import get_module_registry, get_standard_module
|
|
15
15
|
from .cssl_types import (
|
|
16
16
|
Parameter, DataStruct, Shuffled, Iterator, Combo,
|
|
17
|
-
Stack, Vector, Array, DataSpace, OpenQuote
|
|
17
|
+
Stack, Vector, Array, DataSpace, OpenQuote, List, Dictionary
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
|
|
@@ -374,8 +374,13 @@ class CSSLRuntime:
|
|
|
374
374
|
- top-level statements (assignments, function calls, control flow)
|
|
375
375
|
"""
|
|
376
376
|
result = None
|
|
377
|
+
self._running = True # Start running
|
|
377
378
|
|
|
378
379
|
for child in node.children:
|
|
380
|
+
# Check if exit() was called
|
|
381
|
+
if not self._running:
|
|
382
|
+
break
|
|
383
|
+
|
|
379
384
|
if child.type == 'struct':
|
|
380
385
|
self._exec_struct(child)
|
|
381
386
|
elif child.type == 'function':
|
|
@@ -398,13 +403,14 @@ class CSSLRuntime:
|
|
|
398
403
|
except CSSLRuntimeError:
|
|
399
404
|
pass # Ignore unknown nodes in program mode
|
|
400
405
|
|
|
401
|
-
# Look for and execute main() if defined
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
406
|
+
# Look for and execute main() if defined (only if still running)
|
|
407
|
+
if self._running:
|
|
408
|
+
main_func = self.scope.get('main')
|
|
409
|
+
if main_func and isinstance(main_func, ASTNode) and main_func.type == 'function':
|
|
410
|
+
try:
|
|
411
|
+
result = self._call_function(main_func, [])
|
|
412
|
+
except CSSLReturn as ret:
|
|
413
|
+
result = ret.value
|
|
408
414
|
|
|
409
415
|
return result
|
|
410
416
|
|
|
@@ -701,6 +707,10 @@ class CSSLRuntime:
|
|
|
701
707
|
instance = {} if value_node is None else self._evaluate(value_node)
|
|
702
708
|
elif type_name == 'array':
|
|
703
709
|
instance = Array(element_type)
|
|
710
|
+
elif type_name == 'list':
|
|
711
|
+
instance = List(element_type)
|
|
712
|
+
elif type_name in ('dictionary', 'dict'):
|
|
713
|
+
instance = Dictionary(element_type)
|
|
704
714
|
else:
|
|
705
715
|
# Default: evaluate the value or set to None
|
|
706
716
|
instance = self._evaluate(value_node) if value_node else None
|
|
@@ -824,9 +834,11 @@ class CSSLRuntime:
|
|
|
824
834
|
|
|
825
835
|
def _exec_while(self, node: ASTNode) -> Any:
|
|
826
836
|
"""Execute while loop"""
|
|
827
|
-
while self._evaluate(node.value.get('condition')):
|
|
837
|
+
while self._running and self._evaluate(node.value.get('condition')):
|
|
828
838
|
try:
|
|
829
839
|
for child in node.children:
|
|
840
|
+
if not self._running:
|
|
841
|
+
break
|
|
830
842
|
self._execute_node(child)
|
|
831
843
|
except CSSLBreak:
|
|
832
844
|
break
|
|
@@ -846,9 +858,13 @@ class CSSLRuntime:
|
|
|
846
858
|
step = int(self._evaluate(step_node)) if step_node else 1
|
|
847
859
|
|
|
848
860
|
for i in range(start, end, step):
|
|
861
|
+
if not self._running:
|
|
862
|
+
break
|
|
849
863
|
self.scope.set(var_name, i)
|
|
850
864
|
try:
|
|
851
865
|
for child in node.children:
|
|
866
|
+
if not self._running:
|
|
867
|
+
break
|
|
852
868
|
self._execute_node(child)
|
|
853
869
|
except CSSLBreak:
|
|
854
870
|
break
|
|
@@ -879,7 +895,7 @@ class CSSLRuntime:
|
|
|
879
895
|
var_name = None
|
|
880
896
|
|
|
881
897
|
# Main loop
|
|
882
|
-
while
|
|
898
|
+
while self._running:
|
|
883
899
|
# Check condition
|
|
884
900
|
if condition:
|
|
885
901
|
cond_result = self._evaluate(condition)
|
|
@@ -890,6 +906,8 @@ class CSSLRuntime:
|
|
|
890
906
|
# Execute body
|
|
891
907
|
try:
|
|
892
908
|
for child in node.children:
|
|
909
|
+
if not self._running:
|
|
910
|
+
break
|
|
893
911
|
self._execute_node(child)
|
|
894
912
|
except CSSLBreak:
|
|
895
913
|
break
|
|
@@ -933,9 +951,13 @@ class CSSLRuntime:
|
|
|
933
951
|
return None
|
|
934
952
|
|
|
935
953
|
for item in iterable:
|
|
954
|
+
if not self._running:
|
|
955
|
+
break
|
|
936
956
|
self.scope.set(var_name, item)
|
|
937
957
|
try:
|
|
938
958
|
for child in node.children:
|
|
959
|
+
if not self._running:
|
|
960
|
+
break
|
|
939
961
|
self._execute_node(child)
|
|
940
962
|
except CSSLBreak:
|
|
941
963
|
break
|
|
@@ -1109,6 +1131,48 @@ class CSSLRuntime:
|
|
|
1109
1131
|
result = result if len(result) == filter_val else None
|
|
1110
1132
|
elif isinstance(result, list):
|
|
1111
1133
|
result = [item for item in result if isinstance(item, str) and len(item) == filter_val]
|
|
1134
|
+
elif helper == 'cut':
|
|
1135
|
+
# Cut string at given index - returns the part BEFORE the index
|
|
1136
|
+
# x = <==[string::cut=2] "20:200-1" --> x = "20"
|
|
1137
|
+
if isinstance(result, str):
|
|
1138
|
+
idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
|
|
1139
|
+
result = result[:idx] if 0 <= idx <= len(result) else result
|
|
1140
|
+
elif isinstance(result, list):
|
|
1141
|
+
result = [item[:int(filter_val)] if isinstance(item, str) else item for item in result]
|
|
1142
|
+
elif helper == 'cutAfter':
|
|
1143
|
+
# Get the part AFTER the index
|
|
1144
|
+
# x = <==[string::cutAfter=2] "20:200-1" --> x = ":200-1"
|
|
1145
|
+
if isinstance(result, str):
|
|
1146
|
+
idx = int(filter_val) if not isinstance(filter_val, int) else filter_val
|
|
1147
|
+
result = result[idx:] if 0 <= idx <= len(result) else result
|
|
1148
|
+
elif isinstance(result, list):
|
|
1149
|
+
result = [item[int(filter_val):] if isinstance(item, str) else item for item in result]
|
|
1150
|
+
elif helper == 'slice':
|
|
1151
|
+
# Slice string with start:end format (e.g., "2:5")
|
|
1152
|
+
if isinstance(result, str) and isinstance(filter_val, str) and ':' in filter_val:
|
|
1153
|
+
parts = filter_val.split(':')
|
|
1154
|
+
start = int(parts[0]) if parts[0] else 0
|
|
1155
|
+
end = int(parts[1]) if parts[1] else len(result)
|
|
1156
|
+
result = result[start:end]
|
|
1157
|
+
elif helper == 'split':
|
|
1158
|
+
# Split string by delimiter
|
|
1159
|
+
if isinstance(result, str):
|
|
1160
|
+
result = result.split(str(filter_val))
|
|
1161
|
+
elif helper == 'replace':
|
|
1162
|
+
# Replace in string (format: "old:new")
|
|
1163
|
+
if isinstance(result, str) and isinstance(filter_val, str) and ':' in filter_val:
|
|
1164
|
+
parts = filter_val.split(':', 1)
|
|
1165
|
+
if len(parts) == 2:
|
|
1166
|
+
result = result.replace(parts[0], parts[1])
|
|
1167
|
+
elif helper == 'upper':
|
|
1168
|
+
if isinstance(result, str):
|
|
1169
|
+
result = result.upper()
|
|
1170
|
+
elif helper == 'lower':
|
|
1171
|
+
if isinstance(result, str):
|
|
1172
|
+
result = result.lower()
|
|
1173
|
+
elif helper == 'trim':
|
|
1174
|
+
if isinstance(result, str):
|
|
1175
|
+
result = result.strip()
|
|
1112
1176
|
|
|
1113
1177
|
# === INTEGER HELPERS ===
|
|
1114
1178
|
elif filter_type == 'integer':
|
|
@@ -1387,13 +1451,18 @@ class CSSLRuntime:
|
|
|
1387
1451
|
self._function_injections[func_name] = [code_block]
|
|
1388
1452
|
self._function_replaced[func_name] = True # Mark as replaced
|
|
1389
1453
|
elif mode == 'remove':
|
|
1390
|
-
# -<<== : Remove matching code from function body
|
|
1391
|
-
|
|
1454
|
+
# -<<== or -<<==[n] : Remove matching code from function body
|
|
1455
|
+
remove_index = node.value.get('index')
|
|
1456
|
+
|
|
1392
1457
|
if func_name in self._function_injections:
|
|
1393
|
-
|
|
1458
|
+
if remove_index is not None:
|
|
1459
|
+
# Indexed removal: -<<==[n] removes only the nth injection
|
|
1460
|
+
if 0 <= remove_index < len(self._function_injections[func_name]):
|
|
1461
|
+
self._function_injections[func_name].pop(remove_index)
|
|
1462
|
+
else:
|
|
1463
|
+
# No index: -<<== removes all injections
|
|
1464
|
+
self._function_injections[func_name] = []
|
|
1394
1465
|
self._function_replaced[func_name] = False
|
|
1395
|
-
# Note: Removing from actual function body would require AST manipulation
|
|
1396
|
-
# which is complex - for now we just clear injections
|
|
1397
1466
|
|
|
1398
1467
|
return None
|
|
1399
1468
|
|
|
@@ -1608,6 +1677,10 @@ class CSSLRuntime:
|
|
|
1608
1677
|
return OpenQuote()
|
|
1609
1678
|
elif type_name == 'array':
|
|
1610
1679
|
return Array(element_type)
|
|
1680
|
+
elif type_name == 'list':
|
|
1681
|
+
return List(element_type)
|
|
1682
|
+
elif type_name in ('dictionary', 'dict'):
|
|
1683
|
+
return Dictionary(element_type)
|
|
1611
1684
|
else:
|
|
1612
1685
|
return None
|
|
1613
1686
|
|