IncludeCPP 3.7.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/__init__.py +59 -0
- includecpp/__init__.pyi +255 -0
- includecpp/__main__.py +4 -0
- includecpp/cli/__init__.py +4 -0
- includecpp/cli/commands.py +8270 -0
- includecpp/cli/config_parser.py +127 -0
- includecpp/core/__init__.py +19 -0
- includecpp/core/ai_integration.py +2132 -0
- includecpp/core/build_manager.py +2416 -0
- includecpp/core/cpp_api.py +376 -0
- includecpp/core/cpp_api.pyi +95 -0
- includecpp/core/cppy_converter.py +3448 -0
- includecpp/core/cssl/CSSL_DOCUMENTATION.md +2075 -0
- includecpp/core/cssl/__init__.py +42 -0
- includecpp/core/cssl/cssl_builtins.py +2271 -0
- includecpp/core/cssl/cssl_builtins.pyi +1393 -0
- includecpp/core/cssl/cssl_events.py +621 -0
- includecpp/core/cssl/cssl_modules.py +2803 -0
- includecpp/core/cssl/cssl_parser.py +2575 -0
- includecpp/core/cssl/cssl_runtime.py +3051 -0
- includecpp/core/cssl/cssl_syntax.py +488 -0
- includecpp/core/cssl/cssl_types.py +1512 -0
- includecpp/core/cssl_bridge.py +882 -0
- includecpp/core/cssl_bridge.pyi +488 -0
- includecpp/core/error_catalog.py +802 -0
- includecpp/core/error_formatter.py +1016 -0
- includecpp/core/exceptions.py +97 -0
- includecpp/core/path_discovery.py +77 -0
- includecpp/core/project_ui.py +3370 -0
- includecpp/core/settings_ui.py +326 -0
- includecpp/generator/__init__.py +1 -0
- includecpp/generator/parser.cpp +1903 -0
- includecpp/generator/parser.h +281 -0
- includecpp/generator/type_resolver.cpp +363 -0
- includecpp/generator/type_resolver.h +68 -0
- includecpp/py.typed +0 -0
- includecpp/templates/cpp.proj.template +18 -0
- includecpp/vscode/__init__.py +1 -0
- includecpp/vscode/cssl/__init__.py +1 -0
- includecpp/vscode/cssl/language-configuration.json +38 -0
- includecpp/vscode/cssl/package.json +50 -0
- includecpp/vscode/cssl/snippets/cssl.snippets.json +1080 -0
- includecpp/vscode/cssl/syntaxes/cssl.tmLanguage.json +341 -0
- includecpp-3.7.3.dist-info/METADATA +1076 -0
- includecpp-3.7.3.dist-info/RECORD +49 -0
- includecpp-3.7.3.dist-info/WHEEL +5 -0
- includecpp-3.7.3.dist-info/entry_points.txt +2 -0
- includecpp-3.7.3.dist-info/licenses/LICENSE +21 -0
- includecpp-3.7.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CSSL Built-in Functions
|
|
3
|
+
Provides standard functions available in all CSSL scripts
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import time
|
|
9
|
+
import random
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import math
|
|
14
|
+
from typing import Any, Dict, List, Optional, Callable, Union
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CSSLBuiltinError(Exception):
|
|
19
|
+
"""Error in builtin function execution"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CSSLBuiltins:
|
|
24
|
+
"""
|
|
25
|
+
Built-in functions for CSSL runtime
|
|
26
|
+
These functions are available in all CSSL scripts without imports
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, runtime=None):
|
|
30
|
+
self.runtime = runtime
|
|
31
|
+
self._functions: Dict[str, Callable] = {}
|
|
32
|
+
self._register_all()
|
|
33
|
+
|
|
34
|
+
def _register_all(self):
|
|
35
|
+
"""Register all built-in functions"""
|
|
36
|
+
# Output functions
|
|
37
|
+
self._functions['print'] = self.builtin_print
|
|
38
|
+
self._functions['println'] = self.builtin_println
|
|
39
|
+
self._functions['debug'] = self.builtin_debug
|
|
40
|
+
self._functions['error'] = self.builtin_error
|
|
41
|
+
self._functions['warn'] = self.builtin_warn
|
|
42
|
+
self._functions['log'] = self.builtin_log
|
|
43
|
+
|
|
44
|
+
# Type conversion
|
|
45
|
+
self._functions['int'] = self.builtin_int
|
|
46
|
+
self._functions['float'] = self.builtin_float
|
|
47
|
+
self._functions['str'] = self.builtin_str
|
|
48
|
+
self._functions['bool'] = self.builtin_bool
|
|
49
|
+
self._functions['list'] = self.builtin_list
|
|
50
|
+
self._functions['dict'] = self.builtin_dict
|
|
51
|
+
|
|
52
|
+
# Type checking
|
|
53
|
+
self._functions['typeof'] = self.builtin_typeof
|
|
54
|
+
self._functions['isinstance'] = self.builtin_isinstance
|
|
55
|
+
self._functions['isint'] = self.builtin_isint
|
|
56
|
+
self._functions['isfloat'] = self.builtin_isfloat
|
|
57
|
+
self._functions['isstr'] = self.builtin_isstr
|
|
58
|
+
self._functions['isbool'] = self.builtin_isbool
|
|
59
|
+
self._functions['islist'] = self.builtin_islist
|
|
60
|
+
self._functions['isdict'] = self.builtin_isdict
|
|
61
|
+
self._functions['isnull'] = self.builtin_isnull
|
|
62
|
+
|
|
63
|
+
# String functions
|
|
64
|
+
self._functions['len'] = self.builtin_len
|
|
65
|
+
self._functions['upper'] = self.builtin_upper
|
|
66
|
+
self._functions['lower'] = self.builtin_lower
|
|
67
|
+
self._functions['trim'] = self.builtin_trim
|
|
68
|
+
self._functions['ltrim'] = self.builtin_ltrim
|
|
69
|
+
self._functions['rtrim'] = self.builtin_rtrim
|
|
70
|
+
self._functions['split'] = self.builtin_split
|
|
71
|
+
self._functions['join'] = self.builtin_join
|
|
72
|
+
self._functions['replace'] = self.builtin_replace
|
|
73
|
+
self._functions['substr'] = self.builtin_substr
|
|
74
|
+
self._functions['contains'] = self.builtin_contains
|
|
75
|
+
self._functions['startswith'] = self.builtin_startswith
|
|
76
|
+
self._functions['endswith'] = self.builtin_endswith
|
|
77
|
+
self._functions['format'] = self.builtin_format
|
|
78
|
+
self._functions['concat'] = self.builtin_concat
|
|
79
|
+
self._functions['repeat'] = self.builtin_repeat
|
|
80
|
+
self._functions['reverse'] = self.builtin_reverse
|
|
81
|
+
self._functions['indexof'] = self.builtin_indexof
|
|
82
|
+
self._functions['lastindexof'] = self.builtin_lastindexof
|
|
83
|
+
self._functions['padleft'] = self.builtin_padleft
|
|
84
|
+
self._functions['padright'] = self.builtin_padright
|
|
85
|
+
|
|
86
|
+
# List functions
|
|
87
|
+
self._functions['push'] = self.builtin_push
|
|
88
|
+
self._functions['pop'] = self.builtin_pop
|
|
89
|
+
self._functions['shift'] = self.builtin_shift
|
|
90
|
+
self._functions['unshift'] = self.builtin_unshift
|
|
91
|
+
self._functions['slice'] = self.builtin_slice
|
|
92
|
+
self._functions['sort'] = self.builtin_sort
|
|
93
|
+
self._functions['rsort'] = self.builtin_rsort
|
|
94
|
+
self._functions['unique'] = self.builtin_unique
|
|
95
|
+
self._functions['flatten'] = self.builtin_flatten
|
|
96
|
+
self._functions['filter'] = self.builtin_filter
|
|
97
|
+
self._functions['map'] = self.builtin_map
|
|
98
|
+
self._functions['reduce'] = self.builtin_reduce
|
|
99
|
+
self._functions['find'] = self.builtin_find
|
|
100
|
+
self._functions['findindex'] = self.builtin_findindex
|
|
101
|
+
self._functions['every'] = self.builtin_every
|
|
102
|
+
self._functions['some'] = self.builtin_some
|
|
103
|
+
self._functions['range'] = self.builtin_range
|
|
104
|
+
|
|
105
|
+
# Dict functions
|
|
106
|
+
self._functions['keys'] = self.builtin_keys
|
|
107
|
+
self._functions['values'] = self.builtin_values
|
|
108
|
+
self._functions['items'] = self.builtin_items
|
|
109
|
+
self._functions['haskey'] = self.builtin_haskey
|
|
110
|
+
self._functions['getkey'] = self.builtin_getkey
|
|
111
|
+
self._functions['setkey'] = self.builtin_setkey
|
|
112
|
+
self._functions['delkey'] = self.builtin_delkey
|
|
113
|
+
self._functions['merge'] = self.builtin_merge
|
|
114
|
+
|
|
115
|
+
# Math functions
|
|
116
|
+
self._functions['abs'] = self.builtin_abs
|
|
117
|
+
self._functions['min'] = self.builtin_min
|
|
118
|
+
self._functions['max'] = self.builtin_max
|
|
119
|
+
self._functions['sum'] = self.builtin_sum
|
|
120
|
+
self._functions['avg'] = self.builtin_avg
|
|
121
|
+
self._functions['round'] = self.builtin_round
|
|
122
|
+
self._functions['floor'] = self.builtin_floor
|
|
123
|
+
self._functions['ceil'] = self.builtin_ceil
|
|
124
|
+
self._functions['pow'] = self.builtin_pow
|
|
125
|
+
self._functions['sqrt'] = self.builtin_sqrt
|
|
126
|
+
self._functions['mod'] = self.builtin_mod
|
|
127
|
+
self._functions['random'] = self.builtin_random
|
|
128
|
+
self._functions['randint'] = self.builtin_randint
|
|
129
|
+
# Extended math functions
|
|
130
|
+
self._functions['sin'] = self.builtin_sin
|
|
131
|
+
self._functions['cos'] = self.builtin_cos
|
|
132
|
+
self._functions['tan'] = self.builtin_tan
|
|
133
|
+
self._functions['asin'] = self.builtin_asin
|
|
134
|
+
self._functions['acos'] = self.builtin_acos
|
|
135
|
+
self._functions['atan'] = self.builtin_atan
|
|
136
|
+
self._functions['atan2'] = self.builtin_atan2
|
|
137
|
+
self._functions['log'] = self.builtin_log
|
|
138
|
+
self._functions['log10'] = self.builtin_log10
|
|
139
|
+
self._functions['exp'] = self.builtin_exp
|
|
140
|
+
self._functions['pi'] = lambda: math.pi
|
|
141
|
+
self._functions['e'] = lambda: math.e
|
|
142
|
+
self._functions['radians'] = self.builtin_radians
|
|
143
|
+
self._functions['degrees'] = self.builtin_degrees
|
|
144
|
+
|
|
145
|
+
# Time functions
|
|
146
|
+
self._functions['now'] = self.builtin_now
|
|
147
|
+
self._functions['timestamp'] = self.builtin_timestamp
|
|
148
|
+
self._functions['sleep'] = self.builtin_sleep
|
|
149
|
+
self._functions['date'] = self.builtin_date
|
|
150
|
+
self._functions['time'] = self.builtin_time
|
|
151
|
+
self._functions['datetime'] = self.builtin_datetime
|
|
152
|
+
self._functions['strftime'] = self.builtin_strftime
|
|
153
|
+
|
|
154
|
+
# File/Path functions
|
|
155
|
+
self._functions['pathexists'] = self.builtin_pathexists
|
|
156
|
+
self._functions['isfile'] = self.builtin_isfile
|
|
157
|
+
self._functions['isdir'] = self.builtin_isdir
|
|
158
|
+
self._functions['basename'] = self.builtin_basename
|
|
159
|
+
self._functions['dirname'] = self.builtin_dirname
|
|
160
|
+
self._functions['joinpath'] = self.builtin_joinpath
|
|
161
|
+
self._functions['splitpath'] = self.builtin_splitpath
|
|
162
|
+
self._functions['abspath'] = self.builtin_abspath
|
|
163
|
+
self._functions['normpath'] = self.builtin_normpath
|
|
164
|
+
# File I/O functions
|
|
165
|
+
self._functions['read'] = self.builtin_read
|
|
166
|
+
self._functions['readline'] = self.builtin_readline
|
|
167
|
+
self._functions['write'] = self.builtin_write
|
|
168
|
+
self._functions['writeline'] = self.builtin_writeline
|
|
169
|
+
self._functions['readfile'] = self.builtin_readfile
|
|
170
|
+
self._functions['writefile'] = self.builtin_writefile
|
|
171
|
+
self._functions['appendfile'] = self.builtin_appendfile
|
|
172
|
+
self._functions['readlines'] = self.builtin_readlines
|
|
173
|
+
self._functions['listdir'] = self.builtin_listdir
|
|
174
|
+
self._functions['makedirs'] = self.builtin_makedirs
|
|
175
|
+
self._functions['removefile'] = self.builtin_removefile
|
|
176
|
+
self._functions['removedir'] = self.builtin_removedir
|
|
177
|
+
self._functions['copyfile'] = self.builtin_copyfile
|
|
178
|
+
self._functions['movefile'] = self.builtin_movefile
|
|
179
|
+
self._functions['filesize'] = self.builtin_filesize
|
|
180
|
+
|
|
181
|
+
# JSON functions
|
|
182
|
+
self._functions['tojson'] = self.builtin_tojson
|
|
183
|
+
self._functions['fromjson'] = self.builtin_fromjson
|
|
184
|
+
# JSON namespace functions (json::read, json::write, etc.)
|
|
185
|
+
self._functions['json::read'] = self.builtin_json_read
|
|
186
|
+
self._functions['json::write'] = self.builtin_json_write
|
|
187
|
+
self._functions['json::parse'] = self.builtin_fromjson
|
|
188
|
+
self._functions['json::stringify'] = self.builtin_tojson
|
|
189
|
+
self._functions['json::pretty'] = self.builtin_json_pretty
|
|
190
|
+
self._functions['json::keys'] = self.builtin_json_keys
|
|
191
|
+
self._functions['json::values'] = self.builtin_json_values
|
|
192
|
+
self._functions['json::get'] = self.builtin_json_get
|
|
193
|
+
self._functions['json::set'] = self.builtin_json_set
|
|
194
|
+
self._functions['json::has'] = self.builtin_json_has
|
|
195
|
+
self._functions['json::merge'] = self.builtin_json_merge
|
|
196
|
+
|
|
197
|
+
# Instance introspection functions (instance::getMethods, etc.)
|
|
198
|
+
self._functions['instance::getMethods'] = self.builtin_instance_getMethods
|
|
199
|
+
self._functions['instance::getClasses'] = self.builtin_instance_getClasses
|
|
200
|
+
self._functions['instance::getVars'] = self.builtin_instance_getVars
|
|
201
|
+
self._functions['instance::getAll'] = self.builtin_instance_getAll
|
|
202
|
+
self._functions['instance::call'] = self.builtin_instance_call
|
|
203
|
+
self._functions['instance::has'] = self.builtin_instance_has
|
|
204
|
+
self._functions['instance::type'] = self.builtin_instance_type
|
|
205
|
+
self._functions['isavailable'] = self.builtin_isavailable
|
|
206
|
+
self._functions['instance::exists'] = self.builtin_isavailable # Alias
|
|
207
|
+
|
|
208
|
+
# Regex functions
|
|
209
|
+
self._functions['match'] = self.builtin_match
|
|
210
|
+
self._functions['search'] = self.builtin_search
|
|
211
|
+
self._functions['findall'] = self.builtin_findall
|
|
212
|
+
self._functions['sub'] = self.builtin_sub
|
|
213
|
+
|
|
214
|
+
# Hash functions
|
|
215
|
+
self._functions['md5'] = self.builtin_md5
|
|
216
|
+
self._functions['sha1'] = self.builtin_sha1
|
|
217
|
+
self._functions['sha256'] = self.builtin_sha256
|
|
218
|
+
|
|
219
|
+
# Utility functions
|
|
220
|
+
self._functions['copy'] = self.builtin_copy
|
|
221
|
+
self._functions['deepcopy'] = self.builtin_deepcopy
|
|
222
|
+
self._functions['assert'] = self.builtin_assert
|
|
223
|
+
self._functions['exit'] = self.builtin_exit
|
|
224
|
+
self._functions['env'] = self.builtin_env
|
|
225
|
+
self._functions['setenv'] = self.builtin_setenv
|
|
226
|
+
self._functions['input'] = self.builtin_input
|
|
227
|
+
self._functions['clear'] = self.builtin_clear
|
|
228
|
+
self._functions['cls'] = self.builtin_clear # Alias
|
|
229
|
+
self._functions['color'] = self.builtin_color
|
|
230
|
+
self._functions['delay'] = self.builtin_delay
|
|
231
|
+
self._functions['pyimport'] = self.builtin_pyimport
|
|
232
|
+
|
|
233
|
+
# Extended string functions
|
|
234
|
+
self._functions['sprintf'] = self.builtin_sprintf
|
|
235
|
+
self._functions['chars'] = self.builtin_chars
|
|
236
|
+
self._functions['ord'] = self.builtin_ord
|
|
237
|
+
self._functions['chr'] = self.builtin_chr
|
|
238
|
+
self._functions['capitalize'] = self.builtin_capitalize
|
|
239
|
+
self._functions['title'] = self.builtin_title
|
|
240
|
+
self._functions['swapcase'] = self.builtin_swapcase
|
|
241
|
+
self._functions['center'] = self.builtin_center
|
|
242
|
+
self._functions['zfill'] = self.builtin_zfill
|
|
243
|
+
self._functions['isalpha'] = self.builtin_isalpha
|
|
244
|
+
self._functions['isdigit'] = self.builtin_isdigit
|
|
245
|
+
self._functions['isalnum'] = self.builtin_isalnum
|
|
246
|
+
self._functions['isspace'] = self.builtin_isspace
|
|
247
|
+
|
|
248
|
+
# Extended list functions
|
|
249
|
+
self._functions['enumerate'] = self.builtin_enumerate
|
|
250
|
+
self._functions['zip'] = self.builtin_zip
|
|
251
|
+
self._functions['reversed'] = self.builtin_reversed
|
|
252
|
+
self._functions['sorted'] = self.builtin_sorted
|
|
253
|
+
self._functions['count'] = self.builtin_count
|
|
254
|
+
self._functions['first'] = self.builtin_first
|
|
255
|
+
self._functions['last'] = self.builtin_last
|
|
256
|
+
self._functions['take'] = self.builtin_take
|
|
257
|
+
self._functions['drop'] = self.builtin_drop
|
|
258
|
+
self._functions['chunk'] = self.builtin_chunk
|
|
259
|
+
self._functions['groupby'] = self.builtin_groupby
|
|
260
|
+
self._functions['shuffle'] = self.builtin_shuffle
|
|
261
|
+
self._functions['sample'] = self.builtin_sample
|
|
262
|
+
|
|
263
|
+
# Extended dict functions
|
|
264
|
+
self._functions['update'] = self.builtin_update
|
|
265
|
+
self._functions['fromkeys'] = self.builtin_fromkeys
|
|
266
|
+
self._functions['invert'] = self.builtin_invert
|
|
267
|
+
self._functions['pick'] = self.builtin_pick
|
|
268
|
+
self._functions['omit'] = self.builtin_omit
|
|
269
|
+
|
|
270
|
+
# CSSL-specific system functions
|
|
271
|
+
self._functions['createcmd'] = self.builtin_createcmd
|
|
272
|
+
self._functions['signal'] = self.builtin_signal
|
|
273
|
+
self._functions['appexec'] = self.builtin_appexec
|
|
274
|
+
self._functions['initpy'] = self.builtin_initpy
|
|
275
|
+
self._functions['initsh'] = self.builtin_initsh
|
|
276
|
+
self._functions['wait_for'] = self.builtin_wait_for
|
|
277
|
+
self._functions['wait_for_event'] = self.builtin_wait_for_event
|
|
278
|
+
self._functions['wait_for_booted'] = self.builtin_wait_for_booted
|
|
279
|
+
self._functions['emit'] = self.builtin_emit
|
|
280
|
+
self._functions['on_event'] = self.builtin_on_event
|
|
281
|
+
|
|
282
|
+
# CSSL Import System Functions
|
|
283
|
+
self._functions['cso_root'] = self.builtin_cso_root
|
|
284
|
+
self._functions['include'] = self.builtin_include
|
|
285
|
+
self._functions['payload'] = self.builtin_payload
|
|
286
|
+
self._functions['get'] = self.builtin_get
|
|
287
|
+
|
|
288
|
+
# NEW: Extended OS Functions
|
|
289
|
+
self._functions['Listdir'] = self.builtin_listdir # Alias with capital L
|
|
290
|
+
self._functions['ReadFile'] = self.builtin_readfile # Alias with capitals
|
|
291
|
+
self._functions['WriteFile'] = self.builtin_writefile # Alias with capitals
|
|
292
|
+
self._functions['isLinux'] = self.builtin_islinux
|
|
293
|
+
self._functions['isWindows'] = self.builtin_iswindows
|
|
294
|
+
self._functions['isMac'] = self.builtin_ismac
|
|
295
|
+
|
|
296
|
+
# NEW: Extended Time Functions
|
|
297
|
+
self._functions['CurrentTime'] = self.builtin_currenttime
|
|
298
|
+
|
|
299
|
+
# NEW: Scope/Global Functions
|
|
300
|
+
self._functions['global'] = self.builtin_global
|
|
301
|
+
|
|
302
|
+
# CSSL Data Type Constructors
|
|
303
|
+
self._functions['datastruct'] = self.builtin_datastruct
|
|
304
|
+
self._functions['shuffled'] = self.builtin_shuffled
|
|
305
|
+
self._functions['iterator'] = self.builtin_iterator
|
|
306
|
+
self._functions['combo'] = self.builtin_combo
|
|
307
|
+
self._functions['dataspace'] = self.builtin_dataspace
|
|
308
|
+
self._functions['openquote'] = self.builtin_openquote
|
|
309
|
+
self._functions['OpenFind'] = self.builtin_openfind
|
|
310
|
+
|
|
311
|
+
# Print aliases for CSSL
|
|
312
|
+
self._functions['printl'] = self.builtin_println # CSSL uses printl for println
|
|
313
|
+
|
|
314
|
+
# Shared object functions
|
|
315
|
+
self._functions['delete'] = self.builtin_delete # Delete shared object ($Name)
|
|
316
|
+
|
|
317
|
+
def get_function(self, name: str) -> Optional[Callable]:
|
|
318
|
+
"""Get a built-in function by name"""
|
|
319
|
+
return self._functions.get(name)
|
|
320
|
+
|
|
321
|
+
def has_function(self, name: str) -> bool:
|
|
322
|
+
"""Check if a built-in function exists"""
|
|
323
|
+
return name in self._functions
|
|
324
|
+
|
|
325
|
+
def call(self, name: str, *args, **kwargs) -> Any:
|
|
326
|
+
"""Call a built-in function"""
|
|
327
|
+
func = self._functions.get(name)
|
|
328
|
+
if not func:
|
|
329
|
+
raise CSSLBuiltinError(f"Unknown builtin function: {name}")
|
|
330
|
+
return func(*args, **kwargs)
|
|
331
|
+
|
|
332
|
+
def list_functions(self) -> List[str]:
|
|
333
|
+
"""List all available built-in functions"""
|
|
334
|
+
return sorted(self._functions.keys())
|
|
335
|
+
|
|
336
|
+
# ============= Output Functions =============
|
|
337
|
+
|
|
338
|
+
def builtin_print(self, *args, **kwargs) -> None:
|
|
339
|
+
"""Print without newline"""
|
|
340
|
+
sep = kwargs.get('sep', ' ')
|
|
341
|
+
end = kwargs.get('end', '')
|
|
342
|
+
output = sep.join(str(a) for a in args) + end
|
|
343
|
+
if self.runtime and hasattr(self.runtime, 'output'):
|
|
344
|
+
self.runtime.output(output)
|
|
345
|
+
else:
|
|
346
|
+
print(output, end='')
|
|
347
|
+
|
|
348
|
+
def builtin_println(self, *args, **kwargs) -> None:
|
|
349
|
+
"""Print with newline"""
|
|
350
|
+
sep = kwargs.get('sep', ' ')
|
|
351
|
+
output = sep.join(str(a) for a in args)
|
|
352
|
+
if self.runtime and hasattr(self.runtime, 'output'):
|
|
353
|
+
self.runtime.output(output + '\n')
|
|
354
|
+
else:
|
|
355
|
+
print(output)
|
|
356
|
+
|
|
357
|
+
def builtin_debug(self, *args) -> None:
|
|
358
|
+
"""Debug output"""
|
|
359
|
+
msg = ' '.join(str(a) for a in args)
|
|
360
|
+
if self.runtime and hasattr(self.runtime, 'debug'):
|
|
361
|
+
self.runtime.debug(msg)
|
|
362
|
+
else:
|
|
363
|
+
print(f"[DEBUG] {msg}")
|
|
364
|
+
|
|
365
|
+
def builtin_error(self, *args) -> None:
|
|
366
|
+
"""Error output"""
|
|
367
|
+
msg = ' '.join(str(a) for a in args)
|
|
368
|
+
if self.runtime and hasattr(self.runtime, 'error'):
|
|
369
|
+
self.runtime.error(msg)
|
|
370
|
+
else:
|
|
371
|
+
print(f"[ERROR] {msg}")
|
|
372
|
+
|
|
373
|
+
def builtin_warn(self, *args) -> None:
|
|
374
|
+
"""Warning output"""
|
|
375
|
+
msg = ' '.join(str(a) for a in args)
|
|
376
|
+
if self.runtime and hasattr(self.runtime, 'warn'):
|
|
377
|
+
self.runtime.warn(msg)
|
|
378
|
+
else:
|
|
379
|
+
print(f"[WARN] {msg}")
|
|
380
|
+
|
|
381
|
+
def builtin_log(self, level: str, *args) -> None:
|
|
382
|
+
"""Log with level"""
|
|
383
|
+
msg = ' '.join(str(a) for a in args)
|
|
384
|
+
if self.runtime and hasattr(self.runtime, 'log'):
|
|
385
|
+
self.runtime.log(level, msg)
|
|
386
|
+
else:
|
|
387
|
+
print(f"[{level.upper()}] {msg}")
|
|
388
|
+
|
|
389
|
+
# ============= Type Conversion =============
|
|
390
|
+
|
|
391
|
+
def builtin_int(self, value: Any, base: int = 10) -> int:
|
|
392
|
+
"""Convert to integer"""
|
|
393
|
+
if isinstance(value, str):
|
|
394
|
+
return int(value, base)
|
|
395
|
+
return int(value)
|
|
396
|
+
|
|
397
|
+
def builtin_float(self, value: Any) -> float:
|
|
398
|
+
"""Convert to float"""
|
|
399
|
+
return float(value)
|
|
400
|
+
|
|
401
|
+
def builtin_str(self, value: Any) -> str:
|
|
402
|
+
"""Convert to string"""
|
|
403
|
+
return str(value)
|
|
404
|
+
|
|
405
|
+
def builtin_bool(self, value: Any) -> bool:
|
|
406
|
+
"""Convert to boolean"""
|
|
407
|
+
if isinstance(value, str):
|
|
408
|
+
return value.lower() not in ('', '0', 'false', 'no', 'null', 'none')
|
|
409
|
+
return bool(value)
|
|
410
|
+
|
|
411
|
+
def builtin_list(self, value: Any = None) -> list:
|
|
412
|
+
"""Convert to list"""
|
|
413
|
+
if value is None:
|
|
414
|
+
return []
|
|
415
|
+
if isinstance(value, (list, tuple)):
|
|
416
|
+
return list(value)
|
|
417
|
+
if isinstance(value, dict):
|
|
418
|
+
return list(value.items())
|
|
419
|
+
if isinstance(value, str):
|
|
420
|
+
return list(value)
|
|
421
|
+
return [value]
|
|
422
|
+
|
|
423
|
+
def builtin_dict(self, value: Any = None) -> dict:
|
|
424
|
+
"""Convert to dict"""
|
|
425
|
+
if value is None:
|
|
426
|
+
return {}
|
|
427
|
+
if isinstance(value, dict):
|
|
428
|
+
return dict(value)
|
|
429
|
+
if isinstance(value, (list, tuple)):
|
|
430
|
+
return dict(value)
|
|
431
|
+
raise CSSLBuiltinError(f"Cannot convert {type(value).__name__} to dict")
|
|
432
|
+
|
|
433
|
+
# ============= Type Checking =============
|
|
434
|
+
|
|
435
|
+
def builtin_typeof(self, value: Any) -> str:
|
|
436
|
+
"""Get type name"""
|
|
437
|
+
if value is None:
|
|
438
|
+
return 'null'
|
|
439
|
+
type_map = {
|
|
440
|
+
int: 'int',
|
|
441
|
+
float: 'float',
|
|
442
|
+
str: 'str',
|
|
443
|
+
bool: 'bool',
|
|
444
|
+
list: 'list',
|
|
445
|
+
dict: 'dict',
|
|
446
|
+
tuple: 'tuple'
|
|
447
|
+
}
|
|
448
|
+
return type_map.get(type(value), type(value).__name__)
|
|
449
|
+
|
|
450
|
+
def builtin_isinstance(self, value: Any, type_name: str) -> bool:
|
|
451
|
+
"""Check if value is of type"""
|
|
452
|
+
type_map = {
|
|
453
|
+
'int': int,
|
|
454
|
+
'float': float,
|
|
455
|
+
'str': str,
|
|
456
|
+
'bool': bool,
|
|
457
|
+
'list': list,
|
|
458
|
+
'dict': dict,
|
|
459
|
+
'tuple': tuple,
|
|
460
|
+
'null': type(None)
|
|
461
|
+
}
|
|
462
|
+
check_type = type_map.get(type_name)
|
|
463
|
+
if check_type:
|
|
464
|
+
return isinstance(value, check_type)
|
|
465
|
+
return False
|
|
466
|
+
|
|
467
|
+
def builtin_isint(self, value: Any) -> bool:
|
|
468
|
+
return isinstance(value, int) and not isinstance(value, bool)
|
|
469
|
+
|
|
470
|
+
def builtin_isfloat(self, value: Any) -> bool:
|
|
471
|
+
return isinstance(value, float)
|
|
472
|
+
|
|
473
|
+
def builtin_isstr(self, value: Any) -> bool:
|
|
474
|
+
return isinstance(value, str)
|
|
475
|
+
|
|
476
|
+
def builtin_isbool(self, value: Any) -> bool:
|
|
477
|
+
return isinstance(value, bool)
|
|
478
|
+
|
|
479
|
+
def builtin_islist(self, value: Any) -> bool:
|
|
480
|
+
return isinstance(value, list)
|
|
481
|
+
|
|
482
|
+
def builtin_isdict(self, value: Any) -> bool:
|
|
483
|
+
return isinstance(value, dict)
|
|
484
|
+
|
|
485
|
+
def builtin_isnull(self, value: Any) -> bool:
|
|
486
|
+
return value is None
|
|
487
|
+
|
|
488
|
+
# ============= String Functions =============
|
|
489
|
+
|
|
490
|
+
def builtin_len(self, value: Union[str, list, dict]) -> int:
|
|
491
|
+
"""Get length"""
|
|
492
|
+
return len(value)
|
|
493
|
+
|
|
494
|
+
def builtin_upper(self, s: str) -> str:
|
|
495
|
+
return str(s).upper()
|
|
496
|
+
|
|
497
|
+
def builtin_lower(self, s: str) -> str:
|
|
498
|
+
return str(s).lower()
|
|
499
|
+
|
|
500
|
+
def builtin_trim(self, s: str, chars: str = None) -> str:
|
|
501
|
+
return str(s).strip(chars)
|
|
502
|
+
|
|
503
|
+
def builtin_ltrim(self, s: str, chars: str = None) -> str:
|
|
504
|
+
return str(s).lstrip(chars)
|
|
505
|
+
|
|
506
|
+
def builtin_rtrim(self, s: str, chars: str = None) -> str:
|
|
507
|
+
return str(s).rstrip(chars)
|
|
508
|
+
|
|
509
|
+
def builtin_split(self, s: str, sep: str = None, maxsplit: int = -1) -> list:
|
|
510
|
+
return str(s).split(sep, maxsplit)
|
|
511
|
+
|
|
512
|
+
def builtin_join(self, sep: str, items: list) -> str:
|
|
513
|
+
return str(sep).join(str(i) for i in items)
|
|
514
|
+
|
|
515
|
+
def builtin_replace(self, s: str, old: str, new: str, count: int = -1) -> str:
|
|
516
|
+
return str(s).replace(old, new, count)
|
|
517
|
+
|
|
518
|
+
def builtin_substr(self, s: str, start: int, length: int = None) -> str:
|
|
519
|
+
s = str(s)
|
|
520
|
+
if length is None:
|
|
521
|
+
return s[start:]
|
|
522
|
+
return s[start:start + length]
|
|
523
|
+
|
|
524
|
+
def builtin_contains(self, s: str, sub: str) -> bool:
|
|
525
|
+
return sub in str(s)
|
|
526
|
+
|
|
527
|
+
def builtin_startswith(self, s: str, prefix: str) -> bool:
|
|
528
|
+
return str(s).startswith(prefix)
|
|
529
|
+
|
|
530
|
+
def builtin_endswith(self, s: str, suffix: str) -> bool:
|
|
531
|
+
return str(s).endswith(suffix)
|
|
532
|
+
|
|
533
|
+
def builtin_format(self, template: str, *args, **kwargs) -> str:
|
|
534
|
+
return template.format(*args, **kwargs)
|
|
535
|
+
|
|
536
|
+
def builtin_concat(self, *args) -> str:
|
|
537
|
+
return ''.join(str(a) for a in args)
|
|
538
|
+
|
|
539
|
+
def builtin_repeat(self, s: str, count: int) -> str:
|
|
540
|
+
return str(s) * count
|
|
541
|
+
|
|
542
|
+
def builtin_reverse(self, value: Union[str, list]) -> Union[str, list]:
|
|
543
|
+
if isinstance(value, str):
|
|
544
|
+
return value[::-1]
|
|
545
|
+
if isinstance(value, list):
|
|
546
|
+
return value[::-1]
|
|
547
|
+
raise CSSLBuiltinError("reverse requires string or list")
|
|
548
|
+
|
|
549
|
+
def builtin_indexof(self, s: str, sub: str, start: int = 0) -> int:
|
|
550
|
+
return str(s).find(sub, start)
|
|
551
|
+
|
|
552
|
+
def builtin_lastindexof(self, s: str, sub: str) -> int:
|
|
553
|
+
return str(s).rfind(sub)
|
|
554
|
+
|
|
555
|
+
def builtin_padleft(self, s: str, width: int, char: str = ' ') -> str:
|
|
556
|
+
return str(s).rjust(width, char)
|
|
557
|
+
|
|
558
|
+
def builtin_padright(self, s: str, width: int, char: str = ' ') -> str:
|
|
559
|
+
return str(s).ljust(width, char)
|
|
560
|
+
|
|
561
|
+
# ============= List Functions =============
|
|
562
|
+
|
|
563
|
+
def builtin_push(self, lst: list, *items) -> list:
|
|
564
|
+
lst = list(lst)
|
|
565
|
+
lst.extend(items)
|
|
566
|
+
return lst
|
|
567
|
+
|
|
568
|
+
def builtin_pop(self, lst: list, index: int = -1) -> Any:
|
|
569
|
+
lst = list(lst)
|
|
570
|
+
return lst.pop(index)
|
|
571
|
+
|
|
572
|
+
def builtin_shift(self, lst: list) -> Any:
|
|
573
|
+
lst = list(lst)
|
|
574
|
+
return lst.pop(0) if lst else None
|
|
575
|
+
|
|
576
|
+
def builtin_unshift(self, lst: list, *items) -> list:
|
|
577
|
+
lst = list(lst)
|
|
578
|
+
for item in reversed(items):
|
|
579
|
+
lst.insert(0, item)
|
|
580
|
+
return lst
|
|
581
|
+
|
|
582
|
+
def builtin_slice(self, value: Union[str, list], start: int, end: int = None) -> Union[str, list]:
|
|
583
|
+
if end is None:
|
|
584
|
+
return value[start:]
|
|
585
|
+
return value[start:end]
|
|
586
|
+
|
|
587
|
+
def builtin_sort(self, lst: list, key: str = None) -> list:
|
|
588
|
+
lst = list(lst)
|
|
589
|
+
if key:
|
|
590
|
+
lst.sort(key=lambda x: x.get(key) if isinstance(x, dict) else x)
|
|
591
|
+
else:
|
|
592
|
+
lst.sort()
|
|
593
|
+
return lst
|
|
594
|
+
|
|
595
|
+
def builtin_rsort(self, lst: list, key: str = None) -> list:
|
|
596
|
+
lst = list(lst)
|
|
597
|
+
if key:
|
|
598
|
+
lst.sort(key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=True)
|
|
599
|
+
else:
|
|
600
|
+
lst.sort(reverse=True)
|
|
601
|
+
return lst
|
|
602
|
+
|
|
603
|
+
def builtin_unique(self, lst: list) -> list:
|
|
604
|
+
seen = []
|
|
605
|
+
result = []
|
|
606
|
+
for item in lst:
|
|
607
|
+
key = json.dumps(item, sort_keys=True) if isinstance(item, (dict, list)) else item
|
|
608
|
+
if key not in seen:
|
|
609
|
+
seen.append(key)
|
|
610
|
+
result.append(item)
|
|
611
|
+
return result
|
|
612
|
+
|
|
613
|
+
def builtin_flatten(self, lst: list, depth: int = 1) -> list:
|
|
614
|
+
result = []
|
|
615
|
+
for item in lst:
|
|
616
|
+
if isinstance(item, list) and depth > 0:
|
|
617
|
+
result.extend(self.builtin_flatten(item, depth - 1))
|
|
618
|
+
else:
|
|
619
|
+
result.append(item)
|
|
620
|
+
return result
|
|
621
|
+
|
|
622
|
+
def builtin_filter(self, lst: list, condition: Callable) -> list:
|
|
623
|
+
return [item for item in lst if condition(item)]
|
|
624
|
+
|
|
625
|
+
def builtin_map(self, lst: list, func: Callable) -> list:
|
|
626
|
+
return [func(item) for item in lst]
|
|
627
|
+
|
|
628
|
+
def builtin_reduce(self, lst: list, func: Callable, initial: Any = None) -> Any:
|
|
629
|
+
from functools import reduce
|
|
630
|
+
if initial is not None:
|
|
631
|
+
return reduce(func, lst, initial)
|
|
632
|
+
return reduce(func, lst)
|
|
633
|
+
|
|
634
|
+
def builtin_find(self, lst: list, condition: Callable) -> Any:
|
|
635
|
+
for item in lst:
|
|
636
|
+
if condition(item):
|
|
637
|
+
return item
|
|
638
|
+
return None
|
|
639
|
+
|
|
640
|
+
def builtin_findindex(self, lst: list, condition: Callable) -> int:
|
|
641
|
+
for i, item in enumerate(lst):
|
|
642
|
+
if condition(item):
|
|
643
|
+
return i
|
|
644
|
+
return -1
|
|
645
|
+
|
|
646
|
+
def builtin_every(self, lst: list, condition: Callable) -> bool:
|
|
647
|
+
return all(condition(item) for item in lst)
|
|
648
|
+
|
|
649
|
+
def builtin_some(self, lst: list, condition: Callable) -> bool:
|
|
650
|
+
return any(condition(item) for item in lst)
|
|
651
|
+
|
|
652
|
+
def builtin_range(self, *args) -> list:
|
|
653
|
+
return list(range(*args))
|
|
654
|
+
|
|
655
|
+
# ============= Dict Functions =============
|
|
656
|
+
|
|
657
|
+
def builtin_keys(self, d: dict) -> list:
|
|
658
|
+
return list(d.keys())
|
|
659
|
+
|
|
660
|
+
def builtin_values(self, d: dict) -> list:
|
|
661
|
+
return list(d.values())
|
|
662
|
+
|
|
663
|
+
def builtin_items(self, d: dict) -> list:
|
|
664
|
+
return list(d.items())
|
|
665
|
+
|
|
666
|
+
def builtin_haskey(self, d: dict, key: str) -> bool:
|
|
667
|
+
return key in d
|
|
668
|
+
|
|
669
|
+
def builtin_getkey(self, d: dict, key: str, default: Any = None) -> Any:
|
|
670
|
+
return d.get(key, default)
|
|
671
|
+
|
|
672
|
+
def builtin_setkey(self, d: dict, key: str, value: Any) -> dict:
|
|
673
|
+
d = dict(d)
|
|
674
|
+
d[key] = value
|
|
675
|
+
return d
|
|
676
|
+
|
|
677
|
+
def builtin_delkey(self, d: dict, key: str) -> dict:
|
|
678
|
+
d = dict(d)
|
|
679
|
+
d.pop(key, None)
|
|
680
|
+
return d
|
|
681
|
+
|
|
682
|
+
def builtin_merge(self, *dicts) -> dict:
|
|
683
|
+
result = {}
|
|
684
|
+
for d in dicts:
|
|
685
|
+
if isinstance(d, dict):
|
|
686
|
+
result.update(d)
|
|
687
|
+
return result
|
|
688
|
+
|
|
689
|
+
# ============= Math Functions =============
|
|
690
|
+
|
|
691
|
+
def builtin_abs(self, x: Union[int, float]) -> Union[int, float]:
|
|
692
|
+
return abs(x)
|
|
693
|
+
|
|
694
|
+
def builtin_min(self, *args) -> Any:
|
|
695
|
+
if len(args) == 1 and isinstance(args[0], (list, tuple)):
|
|
696
|
+
return min(args[0])
|
|
697
|
+
return min(args)
|
|
698
|
+
|
|
699
|
+
def builtin_max(self, *args) -> Any:
|
|
700
|
+
if len(args) == 1 and isinstance(args[0], (list, tuple)):
|
|
701
|
+
return max(args[0])
|
|
702
|
+
return max(args)
|
|
703
|
+
|
|
704
|
+
def builtin_sum(self, items: list, start: Union[int, float] = 0) -> Union[int, float]:
|
|
705
|
+
return sum(items, start)
|
|
706
|
+
|
|
707
|
+
def builtin_avg(self, items: list) -> float:
|
|
708
|
+
if not items:
|
|
709
|
+
return 0.0
|
|
710
|
+
return sum(items) / len(items)
|
|
711
|
+
|
|
712
|
+
def builtin_round(self, x: float, digits: int = 0) -> float:
|
|
713
|
+
return round(x, digits)
|
|
714
|
+
|
|
715
|
+
def builtin_floor(self, x: float) -> int:
|
|
716
|
+
import math
|
|
717
|
+
return math.floor(x)
|
|
718
|
+
|
|
719
|
+
def builtin_ceil(self, x: float) -> int:
|
|
720
|
+
import math
|
|
721
|
+
return math.ceil(x)
|
|
722
|
+
|
|
723
|
+
def builtin_pow(self, base: Union[int, float], exp: Union[int, float]) -> Union[int, float]:
|
|
724
|
+
return pow(base, exp)
|
|
725
|
+
|
|
726
|
+
def builtin_sqrt(self, x: Union[int, float]) -> float:
|
|
727
|
+
import math
|
|
728
|
+
return math.sqrt(x)
|
|
729
|
+
|
|
730
|
+
def builtin_mod(self, a: int, b: int) -> int:
|
|
731
|
+
return a % b
|
|
732
|
+
|
|
733
|
+
def builtin_random(self) -> float:
|
|
734
|
+
return random.random()
|
|
735
|
+
|
|
736
|
+
def builtin_randint(self, a: int, b: int) -> int:
|
|
737
|
+
return random.randint(a, b)
|
|
738
|
+
|
|
739
|
+
def builtin_sin(self, x: float) -> float:
|
|
740
|
+
return math.sin(x)
|
|
741
|
+
|
|
742
|
+
def builtin_cos(self, x: float) -> float:
|
|
743
|
+
return math.cos(x)
|
|
744
|
+
|
|
745
|
+
def builtin_tan(self, x: float) -> float:
|
|
746
|
+
return math.tan(x)
|
|
747
|
+
|
|
748
|
+
def builtin_asin(self, x: float) -> float:
|
|
749
|
+
return math.asin(x)
|
|
750
|
+
|
|
751
|
+
def builtin_acos(self, x: float) -> float:
|
|
752
|
+
return math.acos(x)
|
|
753
|
+
|
|
754
|
+
def builtin_atan(self, x: float) -> float:
|
|
755
|
+
return math.atan(x)
|
|
756
|
+
|
|
757
|
+
def builtin_atan2(self, y: float, x: float) -> float:
|
|
758
|
+
return math.atan2(y, x)
|
|
759
|
+
|
|
760
|
+
def builtin_log(self, x: float, base: float = math.e) -> float:
|
|
761
|
+
return math.log(x, base)
|
|
762
|
+
|
|
763
|
+
def builtin_log10(self, x: float) -> float:
|
|
764
|
+
return math.log10(x)
|
|
765
|
+
|
|
766
|
+
def builtin_exp(self, x: float) -> float:
|
|
767
|
+
return math.exp(x)
|
|
768
|
+
|
|
769
|
+
def builtin_radians(self, degrees: float) -> float:
|
|
770
|
+
return math.radians(degrees)
|
|
771
|
+
|
|
772
|
+
def builtin_degrees(self, radians: float) -> float:
|
|
773
|
+
return math.degrees(radians)
|
|
774
|
+
|
|
775
|
+
# ============= Time Functions =============
|
|
776
|
+
|
|
777
|
+
def builtin_now(self) -> float:
|
|
778
|
+
return time.time()
|
|
779
|
+
|
|
780
|
+
def builtin_timestamp(self) -> int:
|
|
781
|
+
return int(time.time())
|
|
782
|
+
|
|
783
|
+
def builtin_sleep(self, seconds: float) -> None:
|
|
784
|
+
time.sleep(seconds)
|
|
785
|
+
|
|
786
|
+
def builtin_date(self, format_str: str = '%Y-%m-%d') -> str:
|
|
787
|
+
return datetime.now().strftime(format_str)
|
|
788
|
+
|
|
789
|
+
def builtin_time(self, format_str: str = '%H:%M:%S') -> str:
|
|
790
|
+
return datetime.now().strftime(format_str)
|
|
791
|
+
|
|
792
|
+
def builtin_datetime(self, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
|
|
793
|
+
return datetime.now().strftime(format_str)
|
|
794
|
+
|
|
795
|
+
def builtin_strftime(self, format_str: str, timestamp: float = None) -> str:
|
|
796
|
+
if timestamp is None:
|
|
797
|
+
return datetime.now().strftime(format_str)
|
|
798
|
+
return datetime.fromtimestamp(timestamp).strftime(format_str)
|
|
799
|
+
|
|
800
|
+
# ============= File/Path Functions =============
|
|
801
|
+
|
|
802
|
+
def builtin_pathexists(self, path: str) -> bool:
|
|
803
|
+
return os.path.exists(path)
|
|
804
|
+
|
|
805
|
+
def builtin_isfile(self, path: str) -> bool:
|
|
806
|
+
return os.path.isfile(path)
|
|
807
|
+
|
|
808
|
+
def builtin_isdir(self, path: str) -> bool:
|
|
809
|
+
return os.path.isdir(path)
|
|
810
|
+
|
|
811
|
+
def builtin_basename(self, path: str) -> str:
|
|
812
|
+
return os.path.basename(path)
|
|
813
|
+
|
|
814
|
+
def builtin_dirname(self, path: str) -> str:
|
|
815
|
+
return os.path.dirname(path)
|
|
816
|
+
|
|
817
|
+
def builtin_joinpath(self, *parts) -> str:
|
|
818
|
+
return os.path.join(*parts)
|
|
819
|
+
|
|
820
|
+
def builtin_splitpath(self, path: str) -> list:
|
|
821
|
+
return list(os.path.split(path))
|
|
822
|
+
|
|
823
|
+
def builtin_abspath(self, path: str) -> str:
|
|
824
|
+
return os.path.abspath(path)
|
|
825
|
+
|
|
826
|
+
def builtin_normpath(self, path: str) -> str:
|
|
827
|
+
return os.path.normpath(path)
|
|
828
|
+
|
|
829
|
+
# ============= File I/O Functions =============
|
|
830
|
+
|
|
831
|
+
def builtin_read(self, path: str, encoding: str = 'utf-8') -> str:
|
|
832
|
+
"""Read entire file content.
|
|
833
|
+
Usage: read('/path/to/file.txt')
|
|
834
|
+
"""
|
|
835
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
836
|
+
return f.read()
|
|
837
|
+
|
|
838
|
+
def builtin_readline(self, line: int, path: str, encoding: str = 'utf-8') -> str:
|
|
839
|
+
"""Read specific line from file (1-indexed).
|
|
840
|
+
Usage: readline(5, '/path/to/file.txt') -> returns line 5
|
|
841
|
+
"""
|
|
842
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
843
|
+
for i, file_line in enumerate(f, 1):
|
|
844
|
+
if i == line:
|
|
845
|
+
return file_line.rstrip('\n\r')
|
|
846
|
+
return "" # Line not found
|
|
847
|
+
|
|
848
|
+
def builtin_write(self, path: str, content: str, encoding: str = 'utf-8') -> int:
|
|
849
|
+
"""Write content to file, returns chars written.
|
|
850
|
+
Usage: write('/path/to/file.txt', 'Hello World')
|
|
851
|
+
"""
|
|
852
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
853
|
+
return f.write(content)
|
|
854
|
+
|
|
855
|
+
def builtin_writeline(self, line: int, content: str, path: str, encoding: str = 'utf-8') -> bool:
|
|
856
|
+
"""Write/replace specific line in file (1-indexed).
|
|
857
|
+
Usage: writeline(5, 'New content', '/path/to/file.txt')
|
|
858
|
+
"""
|
|
859
|
+
# Read all lines
|
|
860
|
+
lines = []
|
|
861
|
+
if os.path.exists(path):
|
|
862
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
863
|
+
lines = f.readlines()
|
|
864
|
+
|
|
865
|
+
# Ensure we have enough lines
|
|
866
|
+
while len(lines) < line:
|
|
867
|
+
lines.append('\n')
|
|
868
|
+
|
|
869
|
+
# Replace the specific line (1-indexed)
|
|
870
|
+
if not content.endswith('\n'):
|
|
871
|
+
content = content + '\n'
|
|
872
|
+
lines[line - 1] = content
|
|
873
|
+
|
|
874
|
+
# Write back
|
|
875
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
876
|
+
f.writelines(lines)
|
|
877
|
+
return True
|
|
878
|
+
|
|
879
|
+
def builtin_readfile(self, path: str, encoding: str = 'utf-8') -> str:
|
|
880
|
+
"""Read entire file content"""
|
|
881
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
882
|
+
return f.read()
|
|
883
|
+
|
|
884
|
+
def builtin_writefile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
|
|
885
|
+
"""Write content to file, returns bytes written"""
|
|
886
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
887
|
+
return f.write(content)
|
|
888
|
+
|
|
889
|
+
def builtin_appendfile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
|
|
890
|
+
"""Append content to file, returns bytes written"""
|
|
891
|
+
with open(path, 'a', encoding=encoding) as f:
|
|
892
|
+
return f.write(content)
|
|
893
|
+
|
|
894
|
+
def builtin_readlines(self, path: str, encoding: str = 'utf-8') -> list:
|
|
895
|
+
"""Read file lines into list"""
|
|
896
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
897
|
+
return f.readlines()
|
|
898
|
+
|
|
899
|
+
def builtin_listdir(self, path: str = '.') -> list:
|
|
900
|
+
"""List directory contents"""
|
|
901
|
+
return os.listdir(path)
|
|
902
|
+
|
|
903
|
+
def builtin_makedirs(self, path: str, exist_ok: bool = True) -> bool:
|
|
904
|
+
"""Create directories recursively"""
|
|
905
|
+
os.makedirs(path, exist_ok=exist_ok)
|
|
906
|
+
return True
|
|
907
|
+
|
|
908
|
+
def builtin_removefile(self, path: str) -> bool:
|
|
909
|
+
"""Remove a file"""
|
|
910
|
+
os.remove(path)
|
|
911
|
+
return True
|
|
912
|
+
|
|
913
|
+
def builtin_removedir(self, path: str) -> bool:
|
|
914
|
+
"""Remove an empty directory"""
|
|
915
|
+
os.rmdir(path)
|
|
916
|
+
return True
|
|
917
|
+
|
|
918
|
+
def builtin_copyfile(self, src: str, dst: str) -> str:
|
|
919
|
+
"""Copy a file, returns destination path"""
|
|
920
|
+
import shutil
|
|
921
|
+
return shutil.copy2(src, dst)
|
|
922
|
+
|
|
923
|
+
def builtin_movefile(self, src: str, dst: str) -> str:
|
|
924
|
+
"""Move a file, returns destination path"""
|
|
925
|
+
import shutil
|
|
926
|
+
return shutil.move(src, dst)
|
|
927
|
+
|
|
928
|
+
def builtin_filesize(self, path: str) -> int:
|
|
929
|
+
"""Get file size in bytes"""
|
|
930
|
+
return os.path.getsize(path)
|
|
931
|
+
|
|
932
|
+
# ============= JSON Functions =============
|
|
933
|
+
|
|
934
|
+
def builtin_tojson(self, value: Any, indent: int = None) -> str:
|
|
935
|
+
return json.dumps(value, indent=indent, ensure_ascii=False)
|
|
936
|
+
|
|
937
|
+
def builtin_fromjson(self, s: str) -> Any:
|
|
938
|
+
return json.loads(s)
|
|
939
|
+
|
|
940
|
+
# JSON namespace functions (json::read, json::write, etc.)
|
|
941
|
+
def builtin_json_read(self, path: str, encoding: str = 'utf-8') -> Any:
|
|
942
|
+
"""Read and parse JSON file.
|
|
943
|
+
Usage: json::read('/path/to/file.json')
|
|
944
|
+
"""
|
|
945
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
946
|
+
return json.load(f)
|
|
947
|
+
|
|
948
|
+
def builtin_json_write(self, path: str, data: Any, indent: int = 2, encoding: str = 'utf-8') -> bool:
|
|
949
|
+
"""Write data to JSON file.
|
|
950
|
+
Usage: json::write('/path/to/file.json', data)
|
|
951
|
+
"""
|
|
952
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
953
|
+
json.dump(data, f, indent=indent, ensure_ascii=False)
|
|
954
|
+
return True
|
|
955
|
+
|
|
956
|
+
def builtin_json_pretty(self, value: Any, indent: int = 2) -> str:
|
|
957
|
+
"""Pretty print JSON.
|
|
958
|
+
Usage: json::pretty(data)
|
|
959
|
+
"""
|
|
960
|
+
return json.dumps(value, indent=indent, ensure_ascii=False)
|
|
961
|
+
|
|
962
|
+
def builtin_json_keys(self, data: Any) -> list:
|
|
963
|
+
"""Get all keys from JSON object.
|
|
964
|
+
Usage: json::keys(data)
|
|
965
|
+
"""
|
|
966
|
+
if isinstance(data, dict):
|
|
967
|
+
return list(data.keys())
|
|
968
|
+
return []
|
|
969
|
+
|
|
970
|
+
def builtin_json_values(self, data: Any) -> list:
|
|
971
|
+
"""Get all values from JSON object.
|
|
972
|
+
Usage: json::values(data)
|
|
973
|
+
"""
|
|
974
|
+
if isinstance(data, dict):
|
|
975
|
+
return list(data.values())
|
|
976
|
+
return []
|
|
977
|
+
|
|
978
|
+
def builtin_json_get(self, data: Any, path: str, default: Any = None) -> Any:
|
|
979
|
+
"""Get value by dot-path from JSON.
|
|
980
|
+
Usage: json::get(data, 'user.name')
|
|
981
|
+
"""
|
|
982
|
+
if not isinstance(data, dict):
|
|
983
|
+
return default
|
|
984
|
+
keys = path.split('.')
|
|
985
|
+
current = data
|
|
986
|
+
for key in keys:
|
|
987
|
+
if isinstance(current, dict) and key in current:
|
|
988
|
+
current = current[key]
|
|
989
|
+
elif isinstance(current, list):
|
|
990
|
+
try:
|
|
991
|
+
current = current[int(key)]
|
|
992
|
+
except (ValueError, IndexError):
|
|
993
|
+
return default
|
|
994
|
+
else:
|
|
995
|
+
return default
|
|
996
|
+
return current
|
|
997
|
+
|
|
998
|
+
def builtin_json_set(self, data: Any, path: str, value: Any) -> Any:
|
|
999
|
+
"""Set value by dot-path in JSON object.
|
|
1000
|
+
Usage: json::set(data, 'user.name', 'John')
|
|
1001
|
+
"""
|
|
1002
|
+
if not isinstance(data, dict):
|
|
1003
|
+
return data
|
|
1004
|
+
data = dict(data) # Copy
|
|
1005
|
+
keys = path.split('.')
|
|
1006
|
+
current = data
|
|
1007
|
+
for i, key in enumerate(keys[:-1]):
|
|
1008
|
+
if key not in current or not isinstance(current[key], dict):
|
|
1009
|
+
current[key] = {}
|
|
1010
|
+
current = current[key]
|
|
1011
|
+
current[keys[-1]] = value
|
|
1012
|
+
return data
|
|
1013
|
+
|
|
1014
|
+
def builtin_json_has(self, data: Any, path: str) -> bool:
|
|
1015
|
+
"""Check if path exists in JSON.
|
|
1016
|
+
Usage: json::has(data, 'user.name')
|
|
1017
|
+
"""
|
|
1018
|
+
result = self.builtin_json_get(data, path, _MISSING := object())
|
|
1019
|
+
return result is not _MISSING
|
|
1020
|
+
|
|
1021
|
+
def builtin_json_merge(self, *dicts) -> dict:
|
|
1022
|
+
"""Deep merge multiple JSON objects.
|
|
1023
|
+
Usage: json::merge(obj1, obj2, obj3)
|
|
1024
|
+
"""
|
|
1025
|
+
def deep_merge(base, update):
|
|
1026
|
+
result = dict(base)
|
|
1027
|
+
for key, value in update.items():
|
|
1028
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
1029
|
+
result[key] = deep_merge(result[key], value)
|
|
1030
|
+
else:
|
|
1031
|
+
result[key] = value
|
|
1032
|
+
return result
|
|
1033
|
+
|
|
1034
|
+
result = {}
|
|
1035
|
+
for d in dicts:
|
|
1036
|
+
if isinstance(d, dict):
|
|
1037
|
+
result = deep_merge(result, d)
|
|
1038
|
+
return result
|
|
1039
|
+
|
|
1040
|
+
# ============= Instance Introspection Functions =============
|
|
1041
|
+
|
|
1042
|
+
def builtin_instance_getMethods(self, obj: Any) -> list:
|
|
1043
|
+
"""Get all methods from an object/module.
|
|
1044
|
+
Usage: instance::getMethods(@module) or instance::getMethods($obj)
|
|
1045
|
+
Returns list of method names.
|
|
1046
|
+
"""
|
|
1047
|
+
import inspect
|
|
1048
|
+
methods = []
|
|
1049
|
+
for name in dir(obj):
|
|
1050
|
+
if not name.startswith('_'):
|
|
1051
|
+
attr = getattr(obj, name, None)
|
|
1052
|
+
if callable(attr):
|
|
1053
|
+
methods.append(name)
|
|
1054
|
+
return methods
|
|
1055
|
+
|
|
1056
|
+
def builtin_instance_getClasses(self, obj: Any) -> list:
|
|
1057
|
+
"""Get all classes from an object/module.
|
|
1058
|
+
Usage: instance::getClasses(@module)
|
|
1059
|
+
Returns list of class names.
|
|
1060
|
+
"""
|
|
1061
|
+
import inspect
|
|
1062
|
+
classes = []
|
|
1063
|
+
for name in dir(obj):
|
|
1064
|
+
if not name.startswith('_'):
|
|
1065
|
+
attr = getattr(obj, name, None)
|
|
1066
|
+
if inspect.isclass(attr):
|
|
1067
|
+
classes.append(name)
|
|
1068
|
+
return classes
|
|
1069
|
+
|
|
1070
|
+
def builtin_instance_getVars(self, obj: Any) -> list:
|
|
1071
|
+
"""Get all variables/attributes (non-callable) from an object.
|
|
1072
|
+
Usage: instance::getVars(@module)
|
|
1073
|
+
Returns list of variable names.
|
|
1074
|
+
"""
|
|
1075
|
+
vars_list = []
|
|
1076
|
+
for name in dir(obj):
|
|
1077
|
+
if not name.startswith('_'):
|
|
1078
|
+
attr = getattr(obj, name, None)
|
|
1079
|
+
if not callable(attr):
|
|
1080
|
+
vars_list.append(name)
|
|
1081
|
+
return vars_list
|
|
1082
|
+
|
|
1083
|
+
def builtin_instance_getAll(self, obj: Any) -> dict:
|
|
1084
|
+
"""Get all attributes from an object, categorized.
|
|
1085
|
+
Usage: instance::getAll(@module)
|
|
1086
|
+
Returns dict with 'methods', 'classes', 'vars' keys.
|
|
1087
|
+
"""
|
|
1088
|
+
import inspect
|
|
1089
|
+
result = {
|
|
1090
|
+
'methods': [],
|
|
1091
|
+
'classes': [],
|
|
1092
|
+
'vars': []
|
|
1093
|
+
}
|
|
1094
|
+
for name in dir(obj):
|
|
1095
|
+
if not name.startswith('_'):
|
|
1096
|
+
attr = getattr(obj, name, None)
|
|
1097
|
+
if inspect.isclass(attr):
|
|
1098
|
+
result['classes'].append(name)
|
|
1099
|
+
elif callable(attr):
|
|
1100
|
+
result['methods'].append(name)
|
|
1101
|
+
else:
|
|
1102
|
+
result['vars'].append(name)
|
|
1103
|
+
return result
|
|
1104
|
+
|
|
1105
|
+
def builtin_instance_call(self, obj: Any, method_name: str, *args, **kwargs) -> Any:
|
|
1106
|
+
"""Dynamically call a method on an object.
|
|
1107
|
+
Usage: instance::call(@module, 'methodName', arg1, arg2)
|
|
1108
|
+
"""
|
|
1109
|
+
method = getattr(obj, method_name, None)
|
|
1110
|
+
if method and callable(method):
|
|
1111
|
+
return method(*args, **kwargs)
|
|
1112
|
+
raise RuntimeError(f"Method '{method_name}' not found on object")
|
|
1113
|
+
|
|
1114
|
+
def builtin_instance_has(self, obj: Any, name: str) -> bool:
|
|
1115
|
+
"""Check if object has an attribute.
|
|
1116
|
+
Usage: instance::has(@module, 'methodName')
|
|
1117
|
+
"""
|
|
1118
|
+
return hasattr(obj, name)
|
|
1119
|
+
|
|
1120
|
+
def builtin_instance_type(self, obj: Any) -> str:
|
|
1121
|
+
"""Get the type name of an object.
|
|
1122
|
+
Usage: instance::type(@module)
|
|
1123
|
+
"""
|
|
1124
|
+
return type(obj).__name__
|
|
1125
|
+
|
|
1126
|
+
def builtin_isavailable(self, name_or_obj: Any) -> bool:
|
|
1127
|
+
"""Check if a shared instance exists.
|
|
1128
|
+
Usage:
|
|
1129
|
+
isavailable("MyInstance") - check by name string
|
|
1130
|
+
isavailable($MyInstance) - check shared ref (returns True if not None)
|
|
1131
|
+
instance::exists("MyInstance") - alias
|
|
1132
|
+
"""
|
|
1133
|
+
from ..cssl_bridge import _live_objects
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
# If it's a string, check by name
|
|
1137
|
+
if isinstance(name_or_obj, str):
|
|
1138
|
+
return name_or_obj in _live_objects
|
|
1139
|
+
|
|
1140
|
+
# Otherwise, check if the object is not None (for $name or instance<"name">)
|
|
1141
|
+
return name_or_obj is not None
|
|
1142
|
+
|
|
1143
|
+
# ============= Regex Functions =============
|
|
1144
|
+
|
|
1145
|
+
def builtin_match(self, pattern: str, string: str) -> Optional[dict]:
|
|
1146
|
+
m = re.match(pattern, string)
|
|
1147
|
+
if m:
|
|
1148
|
+
return {'match': m.group(), 'groups': m.groups(), 'start': m.start(), 'end': m.end()}
|
|
1149
|
+
return None
|
|
1150
|
+
|
|
1151
|
+
def builtin_search(self, pattern: str, string: str) -> Optional[dict]:
|
|
1152
|
+
m = re.search(pattern, string)
|
|
1153
|
+
if m:
|
|
1154
|
+
return {'match': m.group(), 'groups': m.groups(), 'start': m.start(), 'end': m.end()}
|
|
1155
|
+
return None
|
|
1156
|
+
|
|
1157
|
+
def builtin_findall(self, pattern: str, string: str) -> list:
|
|
1158
|
+
return re.findall(pattern, string)
|
|
1159
|
+
|
|
1160
|
+
def builtin_sub(self, pattern: str, repl: str, string: str, count: int = 0) -> str:
|
|
1161
|
+
return re.sub(pattern, repl, string, count)
|
|
1162
|
+
|
|
1163
|
+
# ============= Hash Functions =============
|
|
1164
|
+
|
|
1165
|
+
def builtin_md5(self, s: str) -> str:
|
|
1166
|
+
return hashlib.md5(s.encode()).hexdigest()
|
|
1167
|
+
|
|
1168
|
+
def builtin_sha1(self, s: str) -> str:
|
|
1169
|
+
return hashlib.sha1(s.encode()).hexdigest()
|
|
1170
|
+
|
|
1171
|
+
def builtin_sha256(self, s: str) -> str:
|
|
1172
|
+
return hashlib.sha256(s.encode()).hexdigest()
|
|
1173
|
+
|
|
1174
|
+
# ============= Utility Functions =============
|
|
1175
|
+
|
|
1176
|
+
def builtin_copy(self, value: Any) -> Any:
|
|
1177
|
+
import copy
|
|
1178
|
+
return copy.copy(value)
|
|
1179
|
+
|
|
1180
|
+
def builtin_deepcopy(self, value: Any) -> Any:
|
|
1181
|
+
import copy
|
|
1182
|
+
return copy.deepcopy(value)
|
|
1183
|
+
|
|
1184
|
+
def builtin_assert(self, condition: bool, message: str = "Assertion failed") -> None:
|
|
1185
|
+
if not condition:
|
|
1186
|
+
raise CSSLBuiltinError(message)
|
|
1187
|
+
|
|
1188
|
+
def builtin_exit(self, code: int = 0) -> None:
|
|
1189
|
+
if self.runtime and hasattr(self.runtime, 'exit'):
|
|
1190
|
+
self.runtime.exit(code)
|
|
1191
|
+
else:
|
|
1192
|
+
raise SystemExit(code)
|
|
1193
|
+
|
|
1194
|
+
def builtin_original(self, func_name: str, *args) -> Any:
|
|
1195
|
+
"""Call the original version of a replaced function.
|
|
1196
|
+
|
|
1197
|
+
Usage:
|
|
1198
|
+
exit <<== { printl("custom exit"); }
|
|
1199
|
+
original("exit"); // Calls the ORIGINAL exit, not the replacement
|
|
1200
|
+
|
|
1201
|
+
// In an injection that was defined BEFORE replacement:
|
|
1202
|
+
old_exit <<== { original("exit"); } // Calls original exit
|
|
1203
|
+
"""
|
|
1204
|
+
if self.runtime and hasattr(self.runtime, '_original_functions'):
|
|
1205
|
+
original_func = self.runtime._original_functions.get(func_name)
|
|
1206
|
+
if original_func is not None:
|
|
1207
|
+
if callable(original_func):
|
|
1208
|
+
return original_func(*args)
|
|
1209
|
+
elif isinstance(original_func, type(lambda: None).__class__.__bases__[0]): # Check if bound method
|
|
1210
|
+
return original_func(*args)
|
|
1211
|
+
# Fallback: try to call builtin directly
|
|
1212
|
+
builtin_method = getattr(self, f'builtin_{func_name}', None)
|
|
1213
|
+
if builtin_method:
|
|
1214
|
+
return builtin_method(*args)
|
|
1215
|
+
raise CSSLBuiltinError(f"No original function '{func_name}' found")
|
|
1216
|
+
|
|
1217
|
+
def builtin_env(self, name: str, default: str = None) -> Optional[str]:
|
|
1218
|
+
return os.environ.get(name, default)
|
|
1219
|
+
|
|
1220
|
+
def builtin_setenv(self, name: str, value: str) -> None:
|
|
1221
|
+
"""Set environment variable"""
|
|
1222
|
+
os.environ[name] = value
|
|
1223
|
+
|
|
1224
|
+
def builtin_input(self, prompt: str = '') -> str:
|
|
1225
|
+
"""Read user input"""
|
|
1226
|
+
return input(prompt)
|
|
1227
|
+
|
|
1228
|
+
def builtin_clear(self) -> None:
|
|
1229
|
+
"""Clear console screen"""
|
|
1230
|
+
if os.name == 'nt':
|
|
1231
|
+
os.system('cls')
|
|
1232
|
+
else:
|
|
1233
|
+
print('\033[2J\033[H', end='')
|
|
1234
|
+
|
|
1235
|
+
def builtin_color(self, text: str, color: str) -> str:
|
|
1236
|
+
"""Apply ANSI color to text"""
|
|
1237
|
+
colors = {
|
|
1238
|
+
'black': '30', 'red': '31', 'green': '32', 'yellow': '33',
|
|
1239
|
+
'blue': '34', 'magenta': '35', 'cyan': '36', 'white': '37',
|
|
1240
|
+
'bright_black': '90', 'bright_red': '91', 'bright_green': '92',
|
|
1241
|
+
'bright_yellow': '93', 'bright_blue': '94', 'bright_magenta': '95',
|
|
1242
|
+
'bright_cyan': '96', 'bright_white': '97',
|
|
1243
|
+
'reset': '0', 'bold': '1', 'dim': '2', 'italic': '3',
|
|
1244
|
+
'underline': '4', 'blink': '5', 'reverse': '7'
|
|
1245
|
+
}
|
|
1246
|
+
code = colors.get(color.lower(), color)
|
|
1247
|
+
return f'\033[{code}m{text}\033[0m'
|
|
1248
|
+
|
|
1249
|
+
def builtin_delay(self, ms: float) -> None:
|
|
1250
|
+
"""Delay execution by milliseconds"""
|
|
1251
|
+
time.sleep(ms / 1000.0)
|
|
1252
|
+
|
|
1253
|
+
def builtin_pyimport(self, module_name: str) -> Any:
|
|
1254
|
+
"""Import a Python module for use in CSSL"""
|
|
1255
|
+
return __import__(module_name)
|
|
1256
|
+
|
|
1257
|
+
# ============= Extended String Functions =============
|
|
1258
|
+
|
|
1259
|
+
def builtin_sprintf(self, fmt: str, *args) -> str:
|
|
1260
|
+
"""C-style format string"""
|
|
1261
|
+
return fmt % args
|
|
1262
|
+
|
|
1263
|
+
def builtin_chars(self, s: str) -> list:
|
|
1264
|
+
"""Convert string to list of characters"""
|
|
1265
|
+
return list(s)
|
|
1266
|
+
|
|
1267
|
+
def builtin_ord(self, c: str) -> int:
|
|
1268
|
+
"""Get ASCII/Unicode code of character"""
|
|
1269
|
+
return ord(c[0] if c else '\0')
|
|
1270
|
+
|
|
1271
|
+
def builtin_chr(self, n: int) -> str:
|
|
1272
|
+
"""Convert ASCII/Unicode code to character"""
|
|
1273
|
+
return chr(n)
|
|
1274
|
+
|
|
1275
|
+
def builtin_capitalize(self, s: str) -> str:
|
|
1276
|
+
return str(s).capitalize()
|
|
1277
|
+
|
|
1278
|
+
def builtin_title(self, s: str) -> str:
|
|
1279
|
+
return str(s).title()
|
|
1280
|
+
|
|
1281
|
+
def builtin_swapcase(self, s: str) -> str:
|
|
1282
|
+
return str(s).swapcase()
|
|
1283
|
+
|
|
1284
|
+
def builtin_center(self, s: str, width: int, fillchar: str = ' ') -> str:
|
|
1285
|
+
return str(s).center(width, fillchar)
|
|
1286
|
+
|
|
1287
|
+
def builtin_zfill(self, s: str, width: int) -> str:
|
|
1288
|
+
return str(s).zfill(width)
|
|
1289
|
+
|
|
1290
|
+
def builtin_isalpha(self, s: str) -> bool:
|
|
1291
|
+
return str(s).isalpha()
|
|
1292
|
+
|
|
1293
|
+
def builtin_isdigit(self, s: str) -> bool:
|
|
1294
|
+
return str(s).isdigit()
|
|
1295
|
+
|
|
1296
|
+
def builtin_isalnum(self, s: str) -> bool:
|
|
1297
|
+
return str(s).isalnum()
|
|
1298
|
+
|
|
1299
|
+
def builtin_isspace(self, s: str) -> bool:
|
|
1300
|
+
return str(s).isspace()
|
|
1301
|
+
|
|
1302
|
+
# ============= Extended List Functions =============
|
|
1303
|
+
|
|
1304
|
+
def builtin_enumerate(self, lst: list, start: int = 0) -> list:
|
|
1305
|
+
"""Return list of (index, value) pairs"""
|
|
1306
|
+
return list(enumerate(lst, start))
|
|
1307
|
+
|
|
1308
|
+
def builtin_zip(self, *lists) -> list:
|
|
1309
|
+
"""Zip multiple lists together"""
|
|
1310
|
+
return list(zip(*lists))
|
|
1311
|
+
|
|
1312
|
+
def builtin_reversed(self, lst: list) -> list:
|
|
1313
|
+
"""Return reversed list"""
|
|
1314
|
+
return list(reversed(lst))
|
|
1315
|
+
|
|
1316
|
+
def builtin_sorted(self, lst: list, key: str = None, reverse: bool = False) -> list:
|
|
1317
|
+
"""Return sorted list"""
|
|
1318
|
+
if key:
|
|
1319
|
+
return sorted(lst, key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=reverse)
|
|
1320
|
+
return sorted(lst, reverse=reverse)
|
|
1321
|
+
|
|
1322
|
+
def builtin_count(self, collection: Union[list, str], item: Any) -> int:
|
|
1323
|
+
"""Count occurrences of item"""
|
|
1324
|
+
return collection.count(item)
|
|
1325
|
+
|
|
1326
|
+
def builtin_first(self, lst: list, default: Any = None) -> Any:
|
|
1327
|
+
"""Get first element or default"""
|
|
1328
|
+
return lst[0] if lst else default
|
|
1329
|
+
|
|
1330
|
+
def builtin_last(self, lst: list, default: Any = None) -> Any:
|
|
1331
|
+
"""Get last element or default"""
|
|
1332
|
+
return lst[-1] if lst else default
|
|
1333
|
+
|
|
1334
|
+
def builtin_take(self, lst: list, n: int) -> list:
|
|
1335
|
+
"""Take first n elements"""
|
|
1336
|
+
return lst[:n]
|
|
1337
|
+
|
|
1338
|
+
def builtin_drop(self, lst: list, n: int) -> list:
|
|
1339
|
+
"""Drop first n elements"""
|
|
1340
|
+
return lst[n:]
|
|
1341
|
+
|
|
1342
|
+
def builtin_chunk(self, lst: list, size: int) -> list:
|
|
1343
|
+
"""Split list into chunks of given size"""
|
|
1344
|
+
return [lst[i:i + size] for i in range(0, len(lst), size)]
|
|
1345
|
+
|
|
1346
|
+
def builtin_groupby(self, lst: list, key: str) -> dict:
|
|
1347
|
+
"""Group list of dicts by key"""
|
|
1348
|
+
result = {}
|
|
1349
|
+
for item in lst:
|
|
1350
|
+
k = item.get(key) if isinstance(item, dict) else getattr(item, key, None)
|
|
1351
|
+
if k not in result:
|
|
1352
|
+
result[k] = []
|
|
1353
|
+
result[k].append(item)
|
|
1354
|
+
return result
|
|
1355
|
+
|
|
1356
|
+
def builtin_shuffle(self, lst: list) -> list:
|
|
1357
|
+
"""Return shuffled copy of list"""
|
|
1358
|
+
result = list(lst)
|
|
1359
|
+
random.shuffle(result)
|
|
1360
|
+
return result
|
|
1361
|
+
|
|
1362
|
+
def builtin_sample(self, lst: list, k: int) -> list:
|
|
1363
|
+
"""Return k random elements from list"""
|
|
1364
|
+
return random.sample(lst, min(k, len(lst)))
|
|
1365
|
+
|
|
1366
|
+
# ============= Extended Dict Functions =============
|
|
1367
|
+
|
|
1368
|
+
def builtin_update(self, d: dict, other: dict) -> dict:
|
|
1369
|
+
"""Update dict with another dict, return new dict"""
|
|
1370
|
+
result = dict(d)
|
|
1371
|
+
result.update(other)
|
|
1372
|
+
return result
|
|
1373
|
+
|
|
1374
|
+
def builtin_fromkeys(self, keys: list, value: Any = None) -> dict:
|
|
1375
|
+
"""Create dict from keys with default value"""
|
|
1376
|
+
return dict.fromkeys(keys, value)
|
|
1377
|
+
|
|
1378
|
+
def builtin_invert(self, d: dict) -> dict:
|
|
1379
|
+
"""Swap keys and values"""
|
|
1380
|
+
return {v: k for k, v in d.items()}
|
|
1381
|
+
|
|
1382
|
+
def builtin_pick(self, d: dict, *keys) -> dict:
|
|
1383
|
+
"""Pick only specified keys from dict"""
|
|
1384
|
+
return {k: d[k] for k in keys if k in d}
|
|
1385
|
+
|
|
1386
|
+
def builtin_omit(self, d: dict, *keys) -> dict:
|
|
1387
|
+
"""Omit specified keys from dict"""
|
|
1388
|
+
return {k: v for k, v in d.items() if k not in keys}
|
|
1389
|
+
|
|
1390
|
+
# ============= CSSL System Functions =============
|
|
1391
|
+
|
|
1392
|
+
def builtin_createcmd(self, cmd_name: str, handler: Callable = None) -> bool:
|
|
1393
|
+
"""
|
|
1394
|
+
Create a custom console command
|
|
1395
|
+
Usage: createcmd('mycommand') <== { ... handler code ... }
|
|
1396
|
+
"""
|
|
1397
|
+
if not self.runtime:
|
|
1398
|
+
print(f"Cannot create command '{cmd_name}': No runtime available")
|
|
1399
|
+
return False
|
|
1400
|
+
|
|
1401
|
+
# Store the command handler in runtime
|
|
1402
|
+
if not hasattr(self.runtime, '_custom_commands'):
|
|
1403
|
+
self.runtime._custom_commands = {}
|
|
1404
|
+
|
|
1405
|
+
self.runtime._custom_commands[cmd_name] = handler
|
|
1406
|
+
|
|
1407
|
+
# Find Console via multiple paths
|
|
1408
|
+
console = None
|
|
1409
|
+
|
|
1410
|
+
# Try 1: Direct _console reference on runtime
|
|
1411
|
+
if hasattr(self.runtime, '_console') and self.runtime._console:
|
|
1412
|
+
console = self.runtime._console
|
|
1413
|
+
|
|
1414
|
+
# Try 2: Via service_engine.Console
|
|
1415
|
+
elif self.runtime.service_engine and hasattr(self.runtime.service_engine, 'Console'):
|
|
1416
|
+
console = self.runtime.service_engine.Console
|
|
1417
|
+
|
|
1418
|
+
# Register with Console if found
|
|
1419
|
+
if console and hasattr(console, 'register_custom_command'):
|
|
1420
|
+
console.register_custom_command(cmd_name, handler)
|
|
1421
|
+
else:
|
|
1422
|
+
print(f"Custom command '{cmd_name}' stored (Console not yet available)")
|
|
1423
|
+
|
|
1424
|
+
return True
|
|
1425
|
+
|
|
1426
|
+
def builtin_signal(self, event_ref: Any, action: str = '+') -> bool:
|
|
1427
|
+
"""
|
|
1428
|
+
Send or register a signal/event
|
|
1429
|
+
Usage: signal(@event.CustomEvent, '+') to emit, '-' to unregister
|
|
1430
|
+
"""
|
|
1431
|
+
try:
|
|
1432
|
+
from .cssl_events import get_event_manager, EventType
|
|
1433
|
+
|
|
1434
|
+
event_manager = get_event_manager()
|
|
1435
|
+
|
|
1436
|
+
# Handle event reference (could be string or module ref)
|
|
1437
|
+
event_name = str(event_ref) if not isinstance(event_ref, str) else event_ref
|
|
1438
|
+
|
|
1439
|
+
if action == '+':
|
|
1440
|
+
# Emit the event
|
|
1441
|
+
event_manager.emit_custom(event_name, source="cssl_signal")
|
|
1442
|
+
print(f"Signal emitted: {event_name}")
|
|
1443
|
+
return True
|
|
1444
|
+
elif action == '-':
|
|
1445
|
+
# Unregister handlers for this event
|
|
1446
|
+
# This would need custom implementation
|
|
1447
|
+
print(f"Signal handlers cleared: {event_name}")
|
|
1448
|
+
return True
|
|
1449
|
+
else:
|
|
1450
|
+
print(f"Unknown signal action: {action}")
|
|
1451
|
+
return False
|
|
1452
|
+
|
|
1453
|
+
except Exception as e:
|
|
1454
|
+
print(f"Signal error: {e}")
|
|
1455
|
+
return False
|
|
1456
|
+
|
|
1457
|
+
def builtin_appexec(self, app_name: str, *args) -> bool:
|
|
1458
|
+
"""
|
|
1459
|
+
Start a desktop application (visually)
|
|
1460
|
+
Usage: appexec('xface')
|
|
1461
|
+
"""
|
|
1462
|
+
if not self.runtime or not self.runtime.service_engine:
|
|
1463
|
+
print(f"Cannot execute app '{app_name}': No service engine available")
|
|
1464
|
+
return False
|
|
1465
|
+
|
|
1466
|
+
try:
|
|
1467
|
+
kernel = self.runtime.service_engine.KernelClient
|
|
1468
|
+
|
|
1469
|
+
# Check if desktop environment is available
|
|
1470
|
+
if hasattr(kernel, 'start_desktop_app'):
|
|
1471
|
+
return kernel.start_desktop_app(app_name, *args)
|
|
1472
|
+
|
|
1473
|
+
# Try to find and launch the app
|
|
1474
|
+
app_paths = [
|
|
1475
|
+
os.path.join(kernel.RootDirectory, 'apps', app_name),
|
|
1476
|
+
os.path.join(kernel.RootDirectory, 'apps', f'{app_name}.py'),
|
|
1477
|
+
os.path.join(kernel.RootDirectory, 'desktop', 'apps', app_name),
|
|
1478
|
+
os.path.join(kernel.RootDirectory, 'desktop', 'apps', f'{app_name}.py'),
|
|
1479
|
+
]
|
|
1480
|
+
|
|
1481
|
+
for app_path in app_paths:
|
|
1482
|
+
if os.path.exists(app_path):
|
|
1483
|
+
print(f"Launching app: {app_name}")
|
|
1484
|
+
if app_path.endswith('.py'):
|
|
1485
|
+
return self.builtin_initpy(app_path)
|
|
1486
|
+
return True
|
|
1487
|
+
|
|
1488
|
+
print(f"App not found: {app_name}")
|
|
1489
|
+
return False
|
|
1490
|
+
|
|
1491
|
+
except Exception as e:
|
|
1492
|
+
print(f"App execution error: {e}")
|
|
1493
|
+
return False
|
|
1494
|
+
|
|
1495
|
+
def builtin_initpy(self, path: str, *args, **kwargs) -> Any:
|
|
1496
|
+
"""
|
|
1497
|
+
Execute a Python file
|
|
1498
|
+
Usage: initpy('/path/to/script.py')
|
|
1499
|
+
"""
|
|
1500
|
+
if not os.path.isabs(path) and self.runtime and self.runtime.service_engine:
|
|
1501
|
+
path = os.path.join(self.runtime.service_engine.KernelClient.RootDirectory, path)
|
|
1502
|
+
|
|
1503
|
+
if not os.path.exists(path):
|
|
1504
|
+
raise CSSLBuiltinError(f"Python file not found: {path}")
|
|
1505
|
+
|
|
1506
|
+
try:
|
|
1507
|
+
# Prepare execution context
|
|
1508
|
+
exec_globals = {
|
|
1509
|
+
'__file__': path,
|
|
1510
|
+
'__name__': '__main__',
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
# Add kernel and service engine if available
|
|
1514
|
+
if self.runtime and self.runtime.service_engine:
|
|
1515
|
+
exec_globals['kernel'] = self.runtime.service_engine.KernelClient
|
|
1516
|
+
exec_globals['service'] = self.runtime.service_engine
|
|
1517
|
+
exec_globals['args'] = args
|
|
1518
|
+
exec_globals['kwargs'] = kwargs
|
|
1519
|
+
|
|
1520
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
1521
|
+
code = f.read()
|
|
1522
|
+
|
|
1523
|
+
exec(compile(code, path, 'exec'), exec_globals)
|
|
1524
|
+
return exec_globals.get('result', True)
|
|
1525
|
+
|
|
1526
|
+
except Exception as e:
|
|
1527
|
+
print(f"Python execution error [{path}]: {e}")
|
|
1528
|
+
raise CSSLBuiltinError(f"initpy failed: {e}")
|
|
1529
|
+
|
|
1530
|
+
def builtin_initsh(self, path: str, *args) -> int:
|
|
1531
|
+
"""
|
|
1532
|
+
Execute a shell script
|
|
1533
|
+
Usage: initsh('/path/to/script.sh')
|
|
1534
|
+
"""
|
|
1535
|
+
import subprocess
|
|
1536
|
+
|
|
1537
|
+
if not os.path.isabs(path) and self.runtime and self.runtime.service_engine:
|
|
1538
|
+
path = os.path.join(self.runtime.service_engine.KernelClient.RootDirectory, path)
|
|
1539
|
+
|
|
1540
|
+
if not os.path.exists(path):
|
|
1541
|
+
raise CSSLBuiltinError(f"Shell script not found: {path}")
|
|
1542
|
+
|
|
1543
|
+
try:
|
|
1544
|
+
# Determine shell based on platform
|
|
1545
|
+
import platform
|
|
1546
|
+
if platform.system() == 'Windows':
|
|
1547
|
+
# Use cmd or powershell on Windows
|
|
1548
|
+
cmd = ['cmd', '/c', path] + list(args)
|
|
1549
|
+
else:
|
|
1550
|
+
cmd = ['bash', path] + list(args)
|
|
1551
|
+
|
|
1552
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
1553
|
+
|
|
1554
|
+
if result.stdout:
|
|
1555
|
+
print(result.stdout)
|
|
1556
|
+
if result.stderr:
|
|
1557
|
+
print(f"STDERR: {result.stderr}")
|
|
1558
|
+
|
|
1559
|
+
return result.returncode
|
|
1560
|
+
|
|
1561
|
+
except Exception as e:
|
|
1562
|
+
print(f"Shell execution error [{path}]: {e}")
|
|
1563
|
+
raise CSSLBuiltinError(f"initsh failed: {e}")
|
|
1564
|
+
|
|
1565
|
+
def builtin_wait_for(self, condition: Callable, timeout: float = 30.0, interval: float = 0.1) -> bool:
|
|
1566
|
+
"""
|
|
1567
|
+
Wait for a condition to become true
|
|
1568
|
+
Usage: wait_for(lambda: some_condition, timeout=30)
|
|
1569
|
+
"""
|
|
1570
|
+
import time
|
|
1571
|
+
start = time.time()
|
|
1572
|
+
|
|
1573
|
+
while time.time() - start < timeout:
|
|
1574
|
+
try:
|
|
1575
|
+
if callable(condition):
|
|
1576
|
+
if condition():
|
|
1577
|
+
return True
|
|
1578
|
+
elif condition:
|
|
1579
|
+
return True
|
|
1580
|
+
except Exception:
|
|
1581
|
+
pass
|
|
1582
|
+
time.sleep(interval)
|
|
1583
|
+
|
|
1584
|
+
return False
|
|
1585
|
+
|
|
1586
|
+
def builtin_wait_for_event(self, event_name: str, timeout: float = 30.0) -> bool:
|
|
1587
|
+
"""
|
|
1588
|
+
Wait for a specific event to occur
|
|
1589
|
+
Usage: wait_for_event('@event.Booted', timeout=60)
|
|
1590
|
+
"""
|
|
1591
|
+
try:
|
|
1592
|
+
from .cssl_events import get_event_manager
|
|
1593
|
+
|
|
1594
|
+
event_manager = get_event_manager()
|
|
1595
|
+
event_occurred = [False]
|
|
1596
|
+
|
|
1597
|
+
def handler(event_data):
|
|
1598
|
+
event_occurred[0] = True
|
|
1599
|
+
|
|
1600
|
+
# Register temporary handler
|
|
1601
|
+
handler_id = event_manager.register_custom(
|
|
1602
|
+
event_name,
|
|
1603
|
+
handler,
|
|
1604
|
+
once=True
|
|
1605
|
+
)
|
|
1606
|
+
|
|
1607
|
+
# Wait for event
|
|
1608
|
+
import time
|
|
1609
|
+
start = time.time()
|
|
1610
|
+
while not event_occurred[0] and (time.time() - start) < timeout:
|
|
1611
|
+
time.sleep(0.1)
|
|
1612
|
+
|
|
1613
|
+
# Cleanup if not occurred
|
|
1614
|
+
if not event_occurred[0]:
|
|
1615
|
+
event_manager.unregister(handler_id)
|
|
1616
|
+
|
|
1617
|
+
return event_occurred[0]
|
|
1618
|
+
|
|
1619
|
+
except Exception as e:
|
|
1620
|
+
print(f"wait_for_event error: {e}")
|
|
1621
|
+
return False
|
|
1622
|
+
|
|
1623
|
+
def builtin_wait_for_booted(self, timeout: float = 60.0) -> bool:
|
|
1624
|
+
"""
|
|
1625
|
+
Wait until the system is fully booted
|
|
1626
|
+
Usage: await wait_for_booted()
|
|
1627
|
+
"""
|
|
1628
|
+
if not self.runtime or not self.runtime.service_engine:
|
|
1629
|
+
return False
|
|
1630
|
+
|
|
1631
|
+
import time
|
|
1632
|
+
start = time.time()
|
|
1633
|
+
|
|
1634
|
+
while time.time() - start < timeout:
|
|
1635
|
+
try:
|
|
1636
|
+
wheel = self.runtime.service_engine.KernelClient.WheelKernel
|
|
1637
|
+
booted = wheel.ReadWheelParam('boot', 'BOOTED')
|
|
1638
|
+
if booted == '1':
|
|
1639
|
+
return True
|
|
1640
|
+
except Exception:
|
|
1641
|
+
pass
|
|
1642
|
+
time.sleep(0.5)
|
|
1643
|
+
|
|
1644
|
+
return False
|
|
1645
|
+
|
|
1646
|
+
def builtin_emit(self, event_name: str, data: Any = None) -> bool:
|
|
1647
|
+
"""
|
|
1648
|
+
Emit a custom event
|
|
1649
|
+
Usage: emit('MyCustomEvent', {data: 'value'})
|
|
1650
|
+
"""
|
|
1651
|
+
try:
|
|
1652
|
+
from .cssl_events import get_event_manager
|
|
1653
|
+
|
|
1654
|
+
event_manager = get_event_manager()
|
|
1655
|
+
event_manager.emit_custom(event_name, source="cssl", data=data or {})
|
|
1656
|
+
return True
|
|
1657
|
+
|
|
1658
|
+
except Exception as e:
|
|
1659
|
+
print(f"emit error: {e}")
|
|
1660
|
+
return False
|
|
1661
|
+
|
|
1662
|
+
def builtin_on_event(self, event_name: str, handler: Callable) -> str:
|
|
1663
|
+
"""
|
|
1664
|
+
Register an event handler
|
|
1665
|
+
Usage: on_event('MyEvent', handler_function)
|
|
1666
|
+
Returns: handler_id for later removal
|
|
1667
|
+
"""
|
|
1668
|
+
try:
|
|
1669
|
+
from .cssl_events import get_event_manager
|
|
1670
|
+
|
|
1671
|
+
event_manager = get_event_manager()
|
|
1672
|
+
handler_id = event_manager.register_custom(event_name, handler)
|
|
1673
|
+
return handler_id
|
|
1674
|
+
|
|
1675
|
+
except Exception as e:
|
|
1676
|
+
print(f"on_event error: {e}")
|
|
1677
|
+
return ""
|
|
1678
|
+
|
|
1679
|
+
# ============= CSSL Import System Functions =============
|
|
1680
|
+
|
|
1681
|
+
def builtin_cso_root(self, path: str = "") -> str:
|
|
1682
|
+
"""
|
|
1683
|
+
Get absolute path relative to CSO root directory
|
|
1684
|
+
Usage: cso_root('/services/myservice.cssl')
|
|
1685
|
+
Returns: Full absolute path to the file
|
|
1686
|
+
"""
|
|
1687
|
+
base = os.getcwd()
|
|
1688
|
+
|
|
1689
|
+
# Try to get base from kernel parameters
|
|
1690
|
+
if self.runtime and self.runtime.service_engine:
|
|
1691
|
+
try:
|
|
1692
|
+
kernel = self.runtime.service_engine.KernelClient
|
|
1693
|
+
if hasattr(kernel, 'WheelKernel'):
|
|
1694
|
+
wheel = kernel.WheelKernel
|
|
1695
|
+
if hasattr(wheel, 'KernelParam'):
|
|
1696
|
+
base = wheel.KernelParam.get('@base', base)
|
|
1697
|
+
if hasattr(kernel, 'RootDirectory'):
|
|
1698
|
+
base = kernel.RootDirectory
|
|
1699
|
+
except Exception:
|
|
1700
|
+
pass
|
|
1701
|
+
|
|
1702
|
+
# Clean path and join
|
|
1703
|
+
if path:
|
|
1704
|
+
clean_path = path.lstrip('/').lstrip('\\')
|
|
1705
|
+
return os.path.normpath(os.path.join(base, clean_path))
|
|
1706
|
+
|
|
1707
|
+
return base
|
|
1708
|
+
|
|
1709
|
+
def builtin_include(self, filepath: str) -> Any:
|
|
1710
|
+
"""
|
|
1711
|
+
Load and execute a CSSL file or .cssl-mod module, returning its ServiceDefinition
|
|
1712
|
+
Usage: include(cso_root('/services/utils.cssl'))
|
|
1713
|
+
include('modules/math_utils.cssl-mod')
|
|
1714
|
+
include('C:/absolute/path/module.cssl-mod')
|
|
1715
|
+
Returns: ServiceDefinition with structs, functions, etc.
|
|
1716
|
+
"""
|
|
1717
|
+
if not self.runtime:
|
|
1718
|
+
raise CSSLBuiltinError("include requires runtime context")
|
|
1719
|
+
|
|
1720
|
+
# Normalize path separators (support both / and \)
|
|
1721
|
+
filepath = filepath.replace('\\', '/')
|
|
1722
|
+
|
|
1723
|
+
# Handle absolute paths (Windows: C:/, D:/, etc. and Unix: /)
|
|
1724
|
+
is_absolute = os.path.isabs(filepath) or (len(filepath) > 2 and filepath[1] == ':')
|
|
1725
|
+
|
|
1726
|
+
if not is_absolute:
|
|
1727
|
+
# Try relative to current working directory first
|
|
1728
|
+
cwd_path = os.path.join(os.getcwd(), filepath)
|
|
1729
|
+
if os.path.exists(cwd_path):
|
|
1730
|
+
filepath = cwd_path
|
|
1731
|
+
else:
|
|
1732
|
+
# Fall back to cso_root for CSO service context
|
|
1733
|
+
filepath = self.builtin_cso_root(filepath)
|
|
1734
|
+
|
|
1735
|
+
# Check file exists
|
|
1736
|
+
if not os.path.exists(filepath):
|
|
1737
|
+
raise CSSLBuiltinError(f"Include file not found: {filepath}")
|
|
1738
|
+
|
|
1739
|
+
# Check include cache to prevent circular imports
|
|
1740
|
+
if not hasattr(self.runtime, '_include_cache'):
|
|
1741
|
+
self.runtime._include_cache = {}
|
|
1742
|
+
|
|
1743
|
+
if filepath in self.runtime._include_cache:
|
|
1744
|
+
return self.runtime._include_cache[filepath]
|
|
1745
|
+
|
|
1746
|
+
try:
|
|
1747
|
+
# Read the file
|
|
1748
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
1749
|
+
source = f.read()
|
|
1750
|
+
|
|
1751
|
+
# Check if this is a .cssl-mod file
|
|
1752
|
+
if filepath.endswith('.cssl-mod') or source.startswith('CSSLMOD1'):
|
|
1753
|
+
result = self._load_cssl_module(filepath, source)
|
|
1754
|
+
self.runtime._include_cache[filepath] = result
|
|
1755
|
+
return result
|
|
1756
|
+
|
|
1757
|
+
# Regular CSSL file
|
|
1758
|
+
from .cssl_parser import parse_cssl
|
|
1759
|
+
|
|
1760
|
+
ast = parse_cssl(source)
|
|
1761
|
+
|
|
1762
|
+
# Execute the service to get definitions
|
|
1763
|
+
service_def = self.runtime._exec_service(ast)
|
|
1764
|
+
|
|
1765
|
+
# Cache the result
|
|
1766
|
+
self.runtime._include_cache[filepath] = service_def
|
|
1767
|
+
|
|
1768
|
+
return service_def
|
|
1769
|
+
|
|
1770
|
+
except Exception as e:
|
|
1771
|
+
raise CSSLBuiltinError(f"Failed to include '{filepath}': {e}")
|
|
1772
|
+
|
|
1773
|
+
def _load_cssl_module(self, filepath: str, source: str) -> Any:
|
|
1774
|
+
"""
|
|
1775
|
+
Load a .cssl-mod module file and return a callable module object.
|
|
1776
|
+
Handles Python and C++ modules.
|
|
1777
|
+
"""
|
|
1778
|
+
import base64
|
|
1779
|
+
import pickle
|
|
1780
|
+
|
|
1781
|
+
# Parse the module format: CSSLMOD1\n<base64-encoded-pickle>
|
|
1782
|
+
lines = source.strip().split('\n', 1)
|
|
1783
|
+
if len(lines) < 2 or lines[0] != 'CSSLMOD1':
|
|
1784
|
+
raise CSSLBuiltinError(f"Invalid .cssl-mod format: {filepath}")
|
|
1785
|
+
|
|
1786
|
+
# Decode the module data
|
|
1787
|
+
try:
|
|
1788
|
+
encoded_data = lines[1].strip()
|
|
1789
|
+
module_data = pickle.loads(base64.b64decode(encoded_data))
|
|
1790
|
+
except Exception as e:
|
|
1791
|
+
raise CSSLBuiltinError(f"Failed to decode .cssl-mod: {e}")
|
|
1792
|
+
|
|
1793
|
+
module_name = module_data.get('name', 'unknown')
|
|
1794
|
+
module_type = module_data.get('type', 'python')
|
|
1795
|
+
module_source = module_data.get('source', '')
|
|
1796
|
+
|
|
1797
|
+
if module_type == 'python':
|
|
1798
|
+
return self._execute_python_module(module_name, module_source, filepath)
|
|
1799
|
+
elif module_type == 'cpp':
|
|
1800
|
+
return self._load_cpp_module(module_name, module_source, filepath)
|
|
1801
|
+
else:
|
|
1802
|
+
raise CSSLBuiltinError(f"Unsupported module type: {module_type}")
|
|
1803
|
+
|
|
1804
|
+
def _execute_python_module(self, name: str, source: str, filepath: str) -> Any:
|
|
1805
|
+
"""
|
|
1806
|
+
Execute Python source and return a module-like object with all functions.
|
|
1807
|
+
"""
|
|
1808
|
+
# Create a namespace for the module
|
|
1809
|
+
module_namespace = {
|
|
1810
|
+
'__name__': name,
|
|
1811
|
+
'__file__': filepath,
|
|
1812
|
+
'__builtins__': __builtins__,
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
# Execute the Python source
|
|
1816
|
+
try:
|
|
1817
|
+
exec(source, module_namespace)
|
|
1818
|
+
except Exception as e:
|
|
1819
|
+
raise CSSLBuiltinError(f"Failed to execute Python module '{name}': {e}")
|
|
1820
|
+
|
|
1821
|
+
# Create a callable module wrapper
|
|
1822
|
+
class CSSLModuleWrapper:
|
|
1823
|
+
"""Wrapper that makes Python functions callable from CSSL"""
|
|
1824
|
+
def __init__(self, namespace, mod_name):
|
|
1825
|
+
self._namespace = namespace
|
|
1826
|
+
self._name = mod_name
|
|
1827
|
+
self._functions = {}
|
|
1828
|
+
|
|
1829
|
+
# Extract all callable functions (not dunder methods)
|
|
1830
|
+
for key, value in namespace.items():
|
|
1831
|
+
if callable(value) and not key.startswith('_'):
|
|
1832
|
+
self._functions[key] = value
|
|
1833
|
+
# Also set as attribute for @Module.func() syntax
|
|
1834
|
+
setattr(self, key, value)
|
|
1835
|
+
|
|
1836
|
+
def __getattr__(self, name):
|
|
1837
|
+
if name.startswith('_'):
|
|
1838
|
+
raise AttributeError(f"'{self._name}' has no attribute '{name}'")
|
|
1839
|
+
if name in self._functions:
|
|
1840
|
+
return self._functions[name]
|
|
1841
|
+
if name in self._namespace:
|
|
1842
|
+
return self._namespace[name]
|
|
1843
|
+
raise AttributeError(f"Module '{self._name}' has no function '{name}'")
|
|
1844
|
+
|
|
1845
|
+
def __repr__(self):
|
|
1846
|
+
funcs = list(self._functions.keys())
|
|
1847
|
+
return f"<CSSLModule '{self._name}' functions={funcs}>"
|
|
1848
|
+
|
|
1849
|
+
def __dir__(self):
|
|
1850
|
+
return list(self._functions.keys())
|
|
1851
|
+
|
|
1852
|
+
return CSSLModuleWrapper(module_namespace, name)
|
|
1853
|
+
|
|
1854
|
+
def _load_cpp_module(self, name: str, source: str, filepath: str) -> Any:
|
|
1855
|
+
"""
|
|
1856
|
+
Load a C++ module (stub for future implementation).
|
|
1857
|
+
For now, returns a placeholder with the source available.
|
|
1858
|
+
"""
|
|
1859
|
+
class CppModuleStub:
|
|
1860
|
+
def __init__(self, mod_name, cpp_source):
|
|
1861
|
+
self._name = mod_name
|
|
1862
|
+
self._source = cpp_source
|
|
1863
|
+
|
|
1864
|
+
def __repr__(self):
|
|
1865
|
+
return f"<CppModule '{self._name}' (not compiled)>"
|
|
1866
|
+
|
|
1867
|
+
def compile(self):
|
|
1868
|
+
"""Future: Compile and load the C++ module"""
|
|
1869
|
+
raise NotImplementedError("C++ module compilation not yet implemented")
|
|
1870
|
+
|
|
1871
|
+
return CppModuleStub(name, source)
|
|
1872
|
+
|
|
1873
|
+
def builtin_payload(self, filepath: str) -> None:
|
|
1874
|
+
"""
|
|
1875
|
+
Load a CSSL payload file (.cssl-pl) and execute it in the current scope.
|
|
1876
|
+
|
|
1877
|
+
Payloads are like header files but for CSSL:
|
|
1878
|
+
- Define global variables (accessible via @name)
|
|
1879
|
+
- Define helper functions (globally callable)
|
|
1880
|
+
- Inject code into builtins (like exit() <<== {...})
|
|
1881
|
+
- Set configuration values
|
|
1882
|
+
|
|
1883
|
+
Usage in .cssl file:
|
|
1884
|
+
payload("myconfig.cssl-pl");
|
|
1885
|
+
// Now all globals, functions, and injections are active
|
|
1886
|
+
|
|
1887
|
+
Usage in Python:
|
|
1888
|
+
cssl = CSSL.CsslLang()
|
|
1889
|
+
cssl.code("myhelper", "... cssl code ...") # Creates inline payload
|
|
1890
|
+
cssl.exec('payload("myhelper");') # Loads the inline payload
|
|
1891
|
+
|
|
1892
|
+
.cssl-pl file format:
|
|
1893
|
+
// Variables
|
|
1894
|
+
global version = "1.0.0";
|
|
1895
|
+
global debug = true;
|
|
1896
|
+
|
|
1897
|
+
// Injections
|
|
1898
|
+
exit() <<== {
|
|
1899
|
+
printl("Cleanup...");
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// Helper functions
|
|
1903
|
+
void log(string msg) {
|
|
1904
|
+
if (@debug) printl("[LOG] " + msg);
|
|
1905
|
+
}
|
|
1906
|
+
"""
|
|
1907
|
+
if not self.runtime:
|
|
1908
|
+
raise CSSLBuiltinError("payload requires runtime context")
|
|
1909
|
+
|
|
1910
|
+
# Normalize path separators
|
|
1911
|
+
filepath = filepath.replace('\\', '/')
|
|
1912
|
+
|
|
1913
|
+
# Check if this is an inline payload (registered via cssl.code())
|
|
1914
|
+
if hasattr(self.runtime, '_inline_payloads') and filepath in self.runtime._inline_payloads:
|
|
1915
|
+
source = self.runtime._inline_payloads[filepath]
|
|
1916
|
+
else:
|
|
1917
|
+
# Handle absolute paths
|
|
1918
|
+
is_absolute = os.path.isabs(filepath) or (len(filepath) > 2 and filepath[1] == ':')
|
|
1919
|
+
|
|
1920
|
+
if not is_absolute:
|
|
1921
|
+
# Try relative to current working directory first
|
|
1922
|
+
cwd_path = os.path.join(os.getcwd(), filepath)
|
|
1923
|
+
if os.path.exists(cwd_path):
|
|
1924
|
+
filepath = cwd_path
|
|
1925
|
+
else:
|
|
1926
|
+
# Fall back to cso_root for CSO service context
|
|
1927
|
+
filepath = self.builtin_cso_root(filepath)
|
|
1928
|
+
|
|
1929
|
+
# Check file exists
|
|
1930
|
+
if not os.path.exists(filepath):
|
|
1931
|
+
raise CSSLBuiltinError(f"Payload file not found: {filepath}")
|
|
1932
|
+
|
|
1933
|
+
# Check payload cache to prevent double loading
|
|
1934
|
+
if not hasattr(self.runtime, '_payload_cache'):
|
|
1935
|
+
self.runtime._payload_cache = set()
|
|
1936
|
+
|
|
1937
|
+
if filepath in self.runtime._payload_cache:
|
|
1938
|
+
return # Already loaded
|
|
1939
|
+
|
|
1940
|
+
# Read the payload file
|
|
1941
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
1942
|
+
source = f.read()
|
|
1943
|
+
|
|
1944
|
+
self.runtime._payload_cache.add(filepath)
|
|
1945
|
+
|
|
1946
|
+
# Parse and execute the payload in current scope
|
|
1947
|
+
# This makes all globals, functions, and injections available
|
|
1948
|
+
try:
|
|
1949
|
+
from .cssl_parser import parse_cssl_program
|
|
1950
|
+
|
|
1951
|
+
ast = parse_cssl_program(source)
|
|
1952
|
+
|
|
1953
|
+
# Execute the payload - this will:
|
|
1954
|
+
# - Register global variables (accessible via @name)
|
|
1955
|
+
# - Define functions in current scope
|
|
1956
|
+
# - Set up any code injections
|
|
1957
|
+
self.runtime._execute_node(ast)
|
|
1958
|
+
|
|
1959
|
+
except Exception as e:
|
|
1960
|
+
raise CSSLBuiltinError(f"Failed to load payload '{filepath}': {e}")
|
|
1961
|
+
|
|
1962
|
+
def builtin_get(self, source: Any, key: str = None) -> Any:
|
|
1963
|
+
"""
|
|
1964
|
+
Get value from module, ServiceDefinition, or dict
|
|
1965
|
+
Usage:
|
|
1966
|
+
get('list') - Get list module (returns empty list creator)
|
|
1967
|
+
get('os') - Get OS module
|
|
1968
|
+
get('time') - Get Time module
|
|
1969
|
+
get(@Time) - Get standard module
|
|
1970
|
+
get(service_def, 'funcName') - Get function from included service
|
|
1971
|
+
get(dict_obj, 'key') - Get key from dict
|
|
1972
|
+
Returns: The requested value or None
|
|
1973
|
+
"""
|
|
1974
|
+
# Single argument - return module reference directly
|
|
1975
|
+
if key is None:
|
|
1976
|
+
if isinstance(source, str):
|
|
1977
|
+
# NEW: Handle standard module names
|
|
1978
|
+
module_map = {
|
|
1979
|
+
'list': self._get_list_module(),
|
|
1980
|
+
'dict': self._get_dict_module(),
|
|
1981
|
+
'os': self._get_os_module(),
|
|
1982
|
+
'time': self._get_time_module(),
|
|
1983
|
+
'vsramsdk': self._get_vsram_module(),
|
|
1984
|
+
'kernel': self._get_kernel_module(),
|
|
1985
|
+
}
|
|
1986
|
+
if source.lower() in module_map:
|
|
1987
|
+
return module_map[source.lower()]
|
|
1988
|
+
|
|
1989
|
+
# Treat as module path
|
|
1990
|
+
if self.runtime:
|
|
1991
|
+
return self.runtime.get_module(source)
|
|
1992
|
+
return source
|
|
1993
|
+
|
|
1994
|
+
# Two arguments - extract from source
|
|
1995
|
+
if hasattr(source, key):
|
|
1996
|
+
return getattr(source, key)
|
|
1997
|
+
|
|
1998
|
+
if isinstance(source, dict):
|
|
1999
|
+
return source.get(key)
|
|
2000
|
+
|
|
2001
|
+
# Check if source is a ServiceDefinition-like object
|
|
2002
|
+
if hasattr(source, 'structs') and key in source.structs:
|
|
2003
|
+
return source.structs[key]
|
|
2004
|
+
|
|
2005
|
+
if hasattr(source, 'functions') and key in source.functions:
|
|
2006
|
+
return source.functions[key]
|
|
2007
|
+
|
|
2008
|
+
return None
|
|
2009
|
+
|
|
2010
|
+
# NEW: Module factories for get()
|
|
2011
|
+
def _get_list_module(self):
|
|
2012
|
+
"""Return a list module proxy"""
|
|
2013
|
+
class ListModule:
|
|
2014
|
+
@staticmethod
|
|
2015
|
+
def create():
|
|
2016
|
+
return []
|
|
2017
|
+
@staticmethod
|
|
2018
|
+
def add(lst, item):
|
|
2019
|
+
if isinstance(lst, list):
|
|
2020
|
+
lst.append(item)
|
|
2021
|
+
return lst
|
|
2022
|
+
return ListModule()
|
|
2023
|
+
|
|
2024
|
+
def _get_dict_module(self):
|
|
2025
|
+
"""Return a dict module proxy"""
|
|
2026
|
+
class DictModule:
|
|
2027
|
+
@staticmethod
|
|
2028
|
+
def create():
|
|
2029
|
+
return {}
|
|
2030
|
+
return DictModule()
|
|
2031
|
+
|
|
2032
|
+
def _get_os_module(self):
|
|
2033
|
+
"""Return OS module proxy"""
|
|
2034
|
+
class OSModule:
|
|
2035
|
+
@staticmethod
|
|
2036
|
+
def Listdir(path='.'):
|
|
2037
|
+
return os.listdir(path)
|
|
2038
|
+
@staticmethod
|
|
2039
|
+
def ReadFile(path, encoding='utf-8'):
|
|
2040
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
2041
|
+
return f.read()
|
|
2042
|
+
@staticmethod
|
|
2043
|
+
def WriteFile(path, content, encoding='utf-8'):
|
|
2044
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
2045
|
+
return f.write(content)
|
|
2046
|
+
@staticmethod
|
|
2047
|
+
def isLinux():
|
|
2048
|
+
import platform
|
|
2049
|
+
return platform.system() == 'Linux'
|
|
2050
|
+
@staticmethod
|
|
2051
|
+
def isWindows():
|
|
2052
|
+
import platform
|
|
2053
|
+
return platform.system() == 'Windows'
|
|
2054
|
+
@staticmethod
|
|
2055
|
+
def isMac():
|
|
2056
|
+
import platform
|
|
2057
|
+
return platform.system() == 'Darwin'
|
|
2058
|
+
return OSModule()
|
|
2059
|
+
|
|
2060
|
+
def _get_time_module(self):
|
|
2061
|
+
"""Return Time module proxy"""
|
|
2062
|
+
class TimeModule:
|
|
2063
|
+
@staticmethod
|
|
2064
|
+
def CurrentTime(format_str='%Y-%m-%d %H:%M:%S'):
|
|
2065
|
+
return datetime.now().strftime(format_str)
|
|
2066
|
+
@staticmethod
|
|
2067
|
+
def Now():
|
|
2068
|
+
return time.time()
|
|
2069
|
+
@staticmethod
|
|
2070
|
+
def Sleep(seconds):
|
|
2071
|
+
time.sleep(seconds)
|
|
2072
|
+
return TimeModule()
|
|
2073
|
+
|
|
2074
|
+
def _get_vsram_module(self):
|
|
2075
|
+
"""Return VSRAM module proxy from runtime"""
|
|
2076
|
+
if self.runtime and hasattr(self.runtime, '_modules'):
|
|
2077
|
+
return self.runtime._modules.get('VSRam') or self.runtime._modules.get('VSRAM')
|
|
2078
|
+
return None
|
|
2079
|
+
|
|
2080
|
+
def _get_kernel_module(self):
|
|
2081
|
+
"""Return Kernel module proxy from runtime"""
|
|
2082
|
+
if self.runtime and hasattr(self.runtime, '_modules'):
|
|
2083
|
+
return self.runtime._modules.get('Kernel') or self.runtime._modules.get('KernelClient')
|
|
2084
|
+
return None
|
|
2085
|
+
|
|
2086
|
+
# NEW: Platform Detection Functions
|
|
2087
|
+
def builtin_islinux(self) -> bool:
|
|
2088
|
+
"""Check if running on Linux"""
|
|
2089
|
+
import platform
|
|
2090
|
+
return platform.system() == 'Linux'
|
|
2091
|
+
|
|
2092
|
+
def builtin_iswindows(self) -> bool:
|
|
2093
|
+
"""Check if running on Windows"""
|
|
2094
|
+
import platform
|
|
2095
|
+
return platform.system() == 'Windows'
|
|
2096
|
+
|
|
2097
|
+
def builtin_ismac(self) -> bool:
|
|
2098
|
+
"""Check if running on macOS"""
|
|
2099
|
+
import platform
|
|
2100
|
+
return platform.system() == 'Darwin'
|
|
2101
|
+
|
|
2102
|
+
# NEW: CurrentTime function
|
|
2103
|
+
def builtin_currenttime(self, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
|
|
2104
|
+
"""Get current time as formatted string"""
|
|
2105
|
+
return datetime.now().strftime(format_str)
|
|
2106
|
+
|
|
2107
|
+
# NEW: Global function for scope promotion
|
|
2108
|
+
def builtin_global(self, s_ref: Any) -> None:
|
|
2109
|
+
"""
|
|
2110
|
+
Promote s@<name> to @<name> (make globally accessible)
|
|
2111
|
+
Usage: global(s@cache) - makes @cache available
|
|
2112
|
+
|
|
2113
|
+
This takes the value referenced by s@<name> and registers it
|
|
2114
|
+
as a module reference accessible via @<name>
|
|
2115
|
+
"""
|
|
2116
|
+
if not self.runtime:
|
|
2117
|
+
return
|
|
2118
|
+
|
|
2119
|
+
# The argument could be:
|
|
2120
|
+
# 1. A direct value from s@<name> reference
|
|
2121
|
+
# 2. A string path like "MyStruct.cache"
|
|
2122
|
+
|
|
2123
|
+
if isinstance(s_ref, str):
|
|
2124
|
+
# It's a path - promote it
|
|
2125
|
+
self.runtime.promote_to_global(s_ref)
|
|
2126
|
+
else:
|
|
2127
|
+
# It's a value - we need the original s@<name> reference
|
|
2128
|
+
# This is handled by the runtime which passes the path
|
|
2129
|
+
pass
|
|
2130
|
+
|
|
2131
|
+
def builtin_delete(self, name: str) -> bool:
|
|
2132
|
+
"""
|
|
2133
|
+
Delete a shared object by name.
|
|
2134
|
+
Usage: delete("MyLib") - removes the $MyLib shared object
|
|
2135
|
+
|
|
2136
|
+
Args:
|
|
2137
|
+
name: Name of the shared object (without the $ prefix)
|
|
2138
|
+
|
|
2139
|
+
Returns:
|
|
2140
|
+
True if deleted, False if not found
|
|
2141
|
+
"""
|
|
2142
|
+
from ..cssl_bridge import _live_objects
|
|
2143
|
+
|
|
2144
|
+
# Remove from live objects registry
|
|
2145
|
+
if name in _live_objects:
|
|
2146
|
+
del _live_objects[name]
|
|
2147
|
+
# Also remove from runtime's global scope if present
|
|
2148
|
+
if self.runtime:
|
|
2149
|
+
try:
|
|
2150
|
+
self.runtime.global_scope.delete(f'${name}')
|
|
2151
|
+
except Exception:
|
|
2152
|
+
pass
|
|
2153
|
+
return True
|
|
2154
|
+
return False
|
|
2155
|
+
|
|
2156
|
+
# ============= CSSL Data Type Constructors =============
|
|
2157
|
+
|
|
2158
|
+
def builtin_datastruct(self, element_type: str = 'dynamic') -> Any:
|
|
2159
|
+
"""Create a datastruct container.
|
|
2160
|
+
|
|
2161
|
+
Usage: datastruct<string> myData; or datastruct('string')
|
|
2162
|
+
"""
|
|
2163
|
+
from .cssl_types import DataStruct
|
|
2164
|
+
return DataStruct(element_type)
|
|
2165
|
+
|
|
2166
|
+
def builtin_shuffled(self, element_type: str = 'dynamic') -> Any:
|
|
2167
|
+
"""Create a shuffled container for multiple returns.
|
|
2168
|
+
|
|
2169
|
+
Usage: shuffled<string> results;
|
|
2170
|
+
"""
|
|
2171
|
+
from .cssl_types import Shuffled
|
|
2172
|
+
return Shuffled(element_type)
|
|
2173
|
+
|
|
2174
|
+
def builtin_iterator(self, element_type: str = 'int', size: int = 16) -> Any:
|
|
2175
|
+
"""Create an advanced iterator.
|
|
2176
|
+
|
|
2177
|
+
Usage: iterator<int, 16> Map;
|
|
2178
|
+
"""
|
|
2179
|
+
from .cssl_types import Iterator
|
|
2180
|
+
return Iterator(element_type, size)
|
|
2181
|
+
|
|
2182
|
+
def builtin_combo(self, element_type: str = 'dynamic') -> Any:
|
|
2183
|
+
"""Create a combo filter/search space.
|
|
2184
|
+
|
|
2185
|
+
Usage: combo<open&string> myCombo;
|
|
2186
|
+
"""
|
|
2187
|
+
from .cssl_types import Combo
|
|
2188
|
+
return Combo(element_type)
|
|
2189
|
+
|
|
2190
|
+
def builtin_dataspace(self, space_type: str = 'dynamic') -> Any:
|
|
2191
|
+
"""Create a dataspace for SQL/structured data.
|
|
2192
|
+
|
|
2193
|
+
Usage: dataspace<sql::table> table;
|
|
2194
|
+
"""
|
|
2195
|
+
from .cssl_types import DataSpace
|
|
2196
|
+
return DataSpace(space_type)
|
|
2197
|
+
|
|
2198
|
+
def builtin_openquote(self, db_ref: Any = None) -> Any:
|
|
2199
|
+
"""Create an openquote container for SQL operations.
|
|
2200
|
+
|
|
2201
|
+
Usage: openquote<datastruct<dynamic>&@sql::db.oqt(@db)> Queue;
|
|
2202
|
+
"""
|
|
2203
|
+
from .cssl_types import OpenQuote
|
|
2204
|
+
return OpenQuote(db_ref)
|
|
2205
|
+
|
|
2206
|
+
def builtin_openfind(self, combo_or_type: Any, index: int = 0, params: list = None) -> Any:
|
|
2207
|
+
"""Find open parameter by type or combo space.
|
|
2208
|
+
|
|
2209
|
+
Usage:
|
|
2210
|
+
OpenFind<string>(0) # Find first string at position 0
|
|
2211
|
+
OpenFind(&@comboSpace) # Find using combo filter
|
|
2212
|
+
|
|
2213
|
+
When using with open parameters:
|
|
2214
|
+
open define myFunc(open Params) {
|
|
2215
|
+
string name = OpenFind<string>(0); // Find nearest string at index 0
|
|
2216
|
+
int age = OpenFind<int>(1); // Find nearest int at index 1
|
|
2217
|
+
}
|
|
2218
|
+
"""
|
|
2219
|
+
from .cssl_types import Combo
|
|
2220
|
+
|
|
2221
|
+
if isinstance(combo_or_type, Combo):
|
|
2222
|
+
# Find by combo space
|
|
2223
|
+
if params:
|
|
2224
|
+
return combo_or_type.find_match(params)
|
|
2225
|
+
return combo_or_type.find_match([])
|
|
2226
|
+
|
|
2227
|
+
# Type-based search
|
|
2228
|
+
target_type = combo_or_type
|
|
2229
|
+
if params is None:
|
|
2230
|
+
params = []
|
|
2231
|
+
|
|
2232
|
+
# Map type names to Python types
|
|
2233
|
+
type_map = {
|
|
2234
|
+
'string': str, 'str': str,
|
|
2235
|
+
'int': int, 'integer': int,
|
|
2236
|
+
'float': float, 'double': float,
|
|
2237
|
+
'bool': bool, 'boolean': bool,
|
|
2238
|
+
'list': list, 'array': list,
|
|
2239
|
+
'dict': dict, 'dictionary': dict
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
python_type = type_map.get(str(target_type).lower(), None)
|
|
2243
|
+
if python_type is None:
|
|
2244
|
+
return None
|
|
2245
|
+
|
|
2246
|
+
# Find the nearest matching type from index position
|
|
2247
|
+
matches_found = 0
|
|
2248
|
+
for i, param in enumerate(params):
|
|
2249
|
+
if isinstance(param, python_type):
|
|
2250
|
+
if matches_found == index:
|
|
2251
|
+
return param
|
|
2252
|
+
matches_found += 1
|
|
2253
|
+
|
|
2254
|
+
return None
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
# Module-level convenience functions
|
|
2258
|
+
_default_builtins: Optional[CSSLBuiltins] = None
|
|
2259
|
+
|
|
2260
|
+
|
|
2261
|
+
def get_builtins(runtime=None) -> CSSLBuiltins:
|
|
2262
|
+
"""Get default builtins instance"""
|
|
2263
|
+
global _default_builtins
|
|
2264
|
+
if _default_builtins is None or runtime is not None:
|
|
2265
|
+
_default_builtins = CSSLBuiltins(runtime)
|
|
2266
|
+
return _default_builtins
|
|
2267
|
+
|
|
2268
|
+
|
|
2269
|
+
def call_builtin(name: str, *args, **kwargs) -> Any:
|
|
2270
|
+
"""Call a builtin function"""
|
|
2271
|
+
return get_builtins().call(name, *args, **kwargs)
|