IncludeCPP 3.3.20__py3-none-any.whl → 3.4.8__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 -58
- includecpp/cli/commands.py +400 -21
- includecpp/core/cppy_converter.py +143 -18
- includecpp/core/cssl/__init__.py +40 -0
- includecpp/core/cssl/cssl_builtins.py +1693 -0
- includecpp/core/cssl/cssl_events.py +621 -0
- includecpp/core/cssl/cssl_modules.py +2803 -0
- includecpp/core/cssl/cssl_parser.py +1791 -0
- includecpp/core/cssl/cssl_runtime.py +1587 -0
- includecpp/core/cssl/cssl_syntax.py +488 -0
- includecpp/core/cssl/cssl_types.py +438 -0
- includecpp/core/cssl_bridge.py +409 -0
- includecpp/core/cssl_bridge.pyi +311 -0
- includecpp/core/project_ui.py +684 -34
- includecpp/generator/parser.cpp +81 -0
- {includecpp-3.3.20.dist-info → includecpp-3.4.8.dist-info}/METADATA +48 -4
- includecpp-3.4.8.dist-info/RECORD +41 -0
- includecpp-3.3.20.dist-info/RECORD +0 -31
- {includecpp-3.3.20.dist-info → includecpp-3.4.8.dist-info}/WHEEL +0 -0
- {includecpp-3.3.20.dist-info → includecpp-3.4.8.dist-info}/entry_points.txt +0 -0
- {includecpp-3.3.20.dist-info → includecpp-3.4.8.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.3.20.dist-info → includecpp-3.4.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1693 @@
|
|
|
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['readfile'] = self.builtin_readfile
|
|
166
|
+
self._functions['writefile'] = self.builtin_writefile
|
|
167
|
+
self._functions['appendfile'] = self.builtin_appendfile
|
|
168
|
+
self._functions['readlines'] = self.builtin_readlines
|
|
169
|
+
self._functions['listdir'] = self.builtin_listdir
|
|
170
|
+
self._functions['makedirs'] = self.builtin_makedirs
|
|
171
|
+
self._functions['removefile'] = self.builtin_removefile
|
|
172
|
+
self._functions['removedir'] = self.builtin_removedir
|
|
173
|
+
self._functions['copyfile'] = self.builtin_copyfile
|
|
174
|
+
self._functions['movefile'] = self.builtin_movefile
|
|
175
|
+
self._functions['filesize'] = self.builtin_filesize
|
|
176
|
+
|
|
177
|
+
# JSON functions
|
|
178
|
+
self._functions['tojson'] = self.builtin_tojson
|
|
179
|
+
self._functions['fromjson'] = self.builtin_fromjson
|
|
180
|
+
|
|
181
|
+
# Regex functions
|
|
182
|
+
self._functions['match'] = self.builtin_match
|
|
183
|
+
self._functions['search'] = self.builtin_search
|
|
184
|
+
self._functions['findall'] = self.builtin_findall
|
|
185
|
+
self._functions['sub'] = self.builtin_sub
|
|
186
|
+
|
|
187
|
+
# Hash functions
|
|
188
|
+
self._functions['md5'] = self.builtin_md5
|
|
189
|
+
self._functions['sha1'] = self.builtin_sha1
|
|
190
|
+
self._functions['sha256'] = self.builtin_sha256
|
|
191
|
+
|
|
192
|
+
# Utility functions
|
|
193
|
+
self._functions['copy'] = self.builtin_copy
|
|
194
|
+
self._functions['deepcopy'] = self.builtin_deepcopy
|
|
195
|
+
self._functions['assert'] = self.builtin_assert
|
|
196
|
+
self._functions['exit'] = self.builtin_exit
|
|
197
|
+
self._functions['env'] = self.builtin_env
|
|
198
|
+
self._functions['setenv'] = self.builtin_setenv
|
|
199
|
+
self._functions['input'] = self.builtin_input
|
|
200
|
+
self._functions['clear'] = self.builtin_clear
|
|
201
|
+
self._functions['cls'] = self.builtin_clear # Alias
|
|
202
|
+
self._functions['color'] = self.builtin_color
|
|
203
|
+
self._functions['delay'] = self.builtin_delay
|
|
204
|
+
self._functions['pyimport'] = self.builtin_pyimport
|
|
205
|
+
|
|
206
|
+
# Extended string functions
|
|
207
|
+
self._functions['sprintf'] = self.builtin_sprintf
|
|
208
|
+
self._functions['chars'] = self.builtin_chars
|
|
209
|
+
self._functions['ord'] = self.builtin_ord
|
|
210
|
+
self._functions['chr'] = self.builtin_chr
|
|
211
|
+
self._functions['capitalize'] = self.builtin_capitalize
|
|
212
|
+
self._functions['title'] = self.builtin_title
|
|
213
|
+
self._functions['swapcase'] = self.builtin_swapcase
|
|
214
|
+
self._functions['center'] = self.builtin_center
|
|
215
|
+
self._functions['zfill'] = self.builtin_zfill
|
|
216
|
+
self._functions['isalpha'] = self.builtin_isalpha
|
|
217
|
+
self._functions['isdigit'] = self.builtin_isdigit
|
|
218
|
+
self._functions['isalnum'] = self.builtin_isalnum
|
|
219
|
+
self._functions['isspace'] = self.builtin_isspace
|
|
220
|
+
|
|
221
|
+
# Extended list functions
|
|
222
|
+
self._functions['enumerate'] = self.builtin_enumerate
|
|
223
|
+
self._functions['zip'] = self.builtin_zip
|
|
224
|
+
self._functions['reversed'] = self.builtin_reversed
|
|
225
|
+
self._functions['sorted'] = self.builtin_sorted
|
|
226
|
+
self._functions['count'] = self.builtin_count
|
|
227
|
+
self._functions['first'] = self.builtin_first
|
|
228
|
+
self._functions['last'] = self.builtin_last
|
|
229
|
+
self._functions['take'] = self.builtin_take
|
|
230
|
+
self._functions['drop'] = self.builtin_drop
|
|
231
|
+
self._functions['chunk'] = self.builtin_chunk
|
|
232
|
+
self._functions['groupby'] = self.builtin_groupby
|
|
233
|
+
self._functions['shuffle'] = self.builtin_shuffle
|
|
234
|
+
self._functions['sample'] = self.builtin_sample
|
|
235
|
+
|
|
236
|
+
# Extended dict functions
|
|
237
|
+
self._functions['update'] = self.builtin_update
|
|
238
|
+
self._functions['fromkeys'] = self.builtin_fromkeys
|
|
239
|
+
self._functions['invert'] = self.builtin_invert
|
|
240
|
+
self._functions['pick'] = self.builtin_pick
|
|
241
|
+
self._functions['omit'] = self.builtin_omit
|
|
242
|
+
|
|
243
|
+
# CSSL-specific system functions
|
|
244
|
+
self._functions['createcmd'] = self.builtin_createcmd
|
|
245
|
+
self._functions['signal'] = self.builtin_signal
|
|
246
|
+
self._functions['appexec'] = self.builtin_appexec
|
|
247
|
+
self._functions['initpy'] = self.builtin_initpy
|
|
248
|
+
self._functions['initsh'] = self.builtin_initsh
|
|
249
|
+
self._functions['wait_for'] = self.builtin_wait_for
|
|
250
|
+
self._functions['wait_for_event'] = self.builtin_wait_for_event
|
|
251
|
+
self._functions['wait_for_booted'] = self.builtin_wait_for_booted
|
|
252
|
+
self._functions['emit'] = self.builtin_emit
|
|
253
|
+
self._functions['on_event'] = self.builtin_on_event
|
|
254
|
+
|
|
255
|
+
# CSSL Import System Functions
|
|
256
|
+
self._functions['cso_root'] = self.builtin_cso_root
|
|
257
|
+
self._functions['include'] = self.builtin_include
|
|
258
|
+
self._functions['get'] = self.builtin_get
|
|
259
|
+
|
|
260
|
+
# NEW: Extended OS Functions
|
|
261
|
+
self._functions['Listdir'] = self.builtin_listdir # Alias with capital L
|
|
262
|
+
self._functions['ReadFile'] = self.builtin_readfile # Alias with capitals
|
|
263
|
+
self._functions['WriteFile'] = self.builtin_writefile # Alias with capitals
|
|
264
|
+
self._functions['isLinux'] = self.builtin_islinux
|
|
265
|
+
self._functions['isWindows'] = self.builtin_iswindows
|
|
266
|
+
self._functions['isMac'] = self.builtin_ismac
|
|
267
|
+
|
|
268
|
+
# NEW: Extended Time Functions
|
|
269
|
+
self._functions['CurrentTime'] = self.builtin_currenttime
|
|
270
|
+
|
|
271
|
+
# NEW: Scope/Global Functions
|
|
272
|
+
self._functions['global'] = self.builtin_global
|
|
273
|
+
|
|
274
|
+
# CSSL Data Type Constructors
|
|
275
|
+
self._functions['datastruct'] = self.builtin_datastruct
|
|
276
|
+
self._functions['shuffled'] = self.builtin_shuffled
|
|
277
|
+
self._functions['iterator'] = self.builtin_iterator
|
|
278
|
+
self._functions['combo'] = self.builtin_combo
|
|
279
|
+
self._functions['dataspace'] = self.builtin_dataspace
|
|
280
|
+
self._functions['openquote'] = self.builtin_openquote
|
|
281
|
+
self._functions['OpenFind'] = self.builtin_openfind
|
|
282
|
+
|
|
283
|
+
# Print aliases for CSSL
|
|
284
|
+
self._functions['printl'] = self.builtin_println # CSSL uses printl for println
|
|
285
|
+
|
|
286
|
+
def get_function(self, name: str) -> Optional[Callable]:
|
|
287
|
+
"""Get a built-in function by name"""
|
|
288
|
+
return self._functions.get(name)
|
|
289
|
+
|
|
290
|
+
def has_function(self, name: str) -> bool:
|
|
291
|
+
"""Check if a built-in function exists"""
|
|
292
|
+
return name in self._functions
|
|
293
|
+
|
|
294
|
+
def call(self, name: str, *args, **kwargs) -> Any:
|
|
295
|
+
"""Call a built-in function"""
|
|
296
|
+
func = self._functions.get(name)
|
|
297
|
+
if not func:
|
|
298
|
+
raise CSSLBuiltinError(f"Unknown builtin function: {name}")
|
|
299
|
+
return func(*args, **kwargs)
|
|
300
|
+
|
|
301
|
+
def list_functions(self) -> List[str]:
|
|
302
|
+
"""List all available built-in functions"""
|
|
303
|
+
return sorted(self._functions.keys())
|
|
304
|
+
|
|
305
|
+
# ============= Output Functions =============
|
|
306
|
+
|
|
307
|
+
def builtin_print(self, *args, **kwargs) -> None:
|
|
308
|
+
"""Print without newline"""
|
|
309
|
+
sep = kwargs.get('sep', ' ')
|
|
310
|
+
end = kwargs.get('end', '')
|
|
311
|
+
output = sep.join(str(a) for a in args) + end
|
|
312
|
+
if self.runtime and hasattr(self.runtime, 'output'):
|
|
313
|
+
self.runtime.output(output)
|
|
314
|
+
else:
|
|
315
|
+
print(output, end='')
|
|
316
|
+
|
|
317
|
+
def builtin_println(self, *args, **kwargs) -> None:
|
|
318
|
+
"""Print with newline"""
|
|
319
|
+
sep = kwargs.get('sep', ' ')
|
|
320
|
+
output = sep.join(str(a) for a in args)
|
|
321
|
+
if self.runtime and hasattr(self.runtime, 'output'):
|
|
322
|
+
self.runtime.output(output + '\n')
|
|
323
|
+
else:
|
|
324
|
+
print(output)
|
|
325
|
+
|
|
326
|
+
def builtin_debug(self, *args) -> None:
|
|
327
|
+
"""Debug output"""
|
|
328
|
+
msg = ' '.join(str(a) for a in args)
|
|
329
|
+
if self.runtime and hasattr(self.runtime, 'debug'):
|
|
330
|
+
self.runtime.debug(msg)
|
|
331
|
+
else:
|
|
332
|
+
print(f"[DEBUG] {msg}")
|
|
333
|
+
|
|
334
|
+
def builtin_error(self, *args) -> None:
|
|
335
|
+
"""Error output"""
|
|
336
|
+
msg = ' '.join(str(a) for a in args)
|
|
337
|
+
if self.runtime and hasattr(self.runtime, 'error'):
|
|
338
|
+
self.runtime.error(msg)
|
|
339
|
+
else:
|
|
340
|
+
print(f"[ERROR] {msg}")
|
|
341
|
+
|
|
342
|
+
def builtin_warn(self, *args) -> None:
|
|
343
|
+
"""Warning output"""
|
|
344
|
+
msg = ' '.join(str(a) for a in args)
|
|
345
|
+
if self.runtime and hasattr(self.runtime, 'warn'):
|
|
346
|
+
self.runtime.warn(msg)
|
|
347
|
+
else:
|
|
348
|
+
print(f"[WARN] {msg}")
|
|
349
|
+
|
|
350
|
+
def builtin_log(self, level: str, *args) -> None:
|
|
351
|
+
"""Log with level"""
|
|
352
|
+
msg = ' '.join(str(a) for a in args)
|
|
353
|
+
if self.runtime and hasattr(self.runtime, 'log'):
|
|
354
|
+
self.runtime.log(level, msg)
|
|
355
|
+
else:
|
|
356
|
+
print(f"[{level.upper()}] {msg}")
|
|
357
|
+
|
|
358
|
+
# ============= Type Conversion =============
|
|
359
|
+
|
|
360
|
+
def builtin_int(self, value: Any, base: int = 10) -> int:
|
|
361
|
+
"""Convert to integer"""
|
|
362
|
+
if isinstance(value, str):
|
|
363
|
+
return int(value, base)
|
|
364
|
+
return int(value)
|
|
365
|
+
|
|
366
|
+
def builtin_float(self, value: Any) -> float:
|
|
367
|
+
"""Convert to float"""
|
|
368
|
+
return float(value)
|
|
369
|
+
|
|
370
|
+
def builtin_str(self, value: Any) -> str:
|
|
371
|
+
"""Convert to string"""
|
|
372
|
+
return str(value)
|
|
373
|
+
|
|
374
|
+
def builtin_bool(self, value: Any) -> bool:
|
|
375
|
+
"""Convert to boolean"""
|
|
376
|
+
if isinstance(value, str):
|
|
377
|
+
return value.lower() not in ('', '0', 'false', 'no', 'null', 'none')
|
|
378
|
+
return bool(value)
|
|
379
|
+
|
|
380
|
+
def builtin_list(self, value: Any = None) -> list:
|
|
381
|
+
"""Convert to list"""
|
|
382
|
+
if value is None:
|
|
383
|
+
return []
|
|
384
|
+
if isinstance(value, (list, tuple)):
|
|
385
|
+
return list(value)
|
|
386
|
+
if isinstance(value, dict):
|
|
387
|
+
return list(value.items())
|
|
388
|
+
if isinstance(value, str):
|
|
389
|
+
return list(value)
|
|
390
|
+
return [value]
|
|
391
|
+
|
|
392
|
+
def builtin_dict(self, value: Any = None) -> dict:
|
|
393
|
+
"""Convert to dict"""
|
|
394
|
+
if value is None:
|
|
395
|
+
return {}
|
|
396
|
+
if isinstance(value, dict):
|
|
397
|
+
return dict(value)
|
|
398
|
+
if isinstance(value, (list, tuple)):
|
|
399
|
+
return dict(value)
|
|
400
|
+
raise CSSLBuiltinError(f"Cannot convert {type(value).__name__} to dict")
|
|
401
|
+
|
|
402
|
+
# ============= Type Checking =============
|
|
403
|
+
|
|
404
|
+
def builtin_typeof(self, value: Any) -> str:
|
|
405
|
+
"""Get type name"""
|
|
406
|
+
if value is None:
|
|
407
|
+
return 'null'
|
|
408
|
+
type_map = {
|
|
409
|
+
int: 'int',
|
|
410
|
+
float: 'float',
|
|
411
|
+
str: 'str',
|
|
412
|
+
bool: 'bool',
|
|
413
|
+
list: 'list',
|
|
414
|
+
dict: 'dict',
|
|
415
|
+
tuple: 'tuple'
|
|
416
|
+
}
|
|
417
|
+
return type_map.get(type(value), type(value).__name__)
|
|
418
|
+
|
|
419
|
+
def builtin_isinstance(self, value: Any, type_name: str) -> bool:
|
|
420
|
+
"""Check if value is of type"""
|
|
421
|
+
type_map = {
|
|
422
|
+
'int': int,
|
|
423
|
+
'float': float,
|
|
424
|
+
'str': str,
|
|
425
|
+
'bool': bool,
|
|
426
|
+
'list': list,
|
|
427
|
+
'dict': dict,
|
|
428
|
+
'tuple': tuple,
|
|
429
|
+
'null': type(None)
|
|
430
|
+
}
|
|
431
|
+
check_type = type_map.get(type_name)
|
|
432
|
+
if check_type:
|
|
433
|
+
return isinstance(value, check_type)
|
|
434
|
+
return False
|
|
435
|
+
|
|
436
|
+
def builtin_isint(self, value: Any) -> bool:
|
|
437
|
+
return isinstance(value, int) and not isinstance(value, bool)
|
|
438
|
+
|
|
439
|
+
def builtin_isfloat(self, value: Any) -> bool:
|
|
440
|
+
return isinstance(value, float)
|
|
441
|
+
|
|
442
|
+
def builtin_isstr(self, value: Any) -> bool:
|
|
443
|
+
return isinstance(value, str)
|
|
444
|
+
|
|
445
|
+
def builtin_isbool(self, value: Any) -> bool:
|
|
446
|
+
return isinstance(value, bool)
|
|
447
|
+
|
|
448
|
+
def builtin_islist(self, value: Any) -> bool:
|
|
449
|
+
return isinstance(value, list)
|
|
450
|
+
|
|
451
|
+
def builtin_isdict(self, value: Any) -> bool:
|
|
452
|
+
return isinstance(value, dict)
|
|
453
|
+
|
|
454
|
+
def builtin_isnull(self, value: Any) -> bool:
|
|
455
|
+
return value is None
|
|
456
|
+
|
|
457
|
+
# ============= String Functions =============
|
|
458
|
+
|
|
459
|
+
def builtin_len(self, value: Union[str, list, dict]) -> int:
|
|
460
|
+
"""Get length"""
|
|
461
|
+
return len(value)
|
|
462
|
+
|
|
463
|
+
def builtin_upper(self, s: str) -> str:
|
|
464
|
+
return str(s).upper()
|
|
465
|
+
|
|
466
|
+
def builtin_lower(self, s: str) -> str:
|
|
467
|
+
return str(s).lower()
|
|
468
|
+
|
|
469
|
+
def builtin_trim(self, s: str, chars: str = None) -> str:
|
|
470
|
+
return str(s).strip(chars)
|
|
471
|
+
|
|
472
|
+
def builtin_ltrim(self, s: str, chars: str = None) -> str:
|
|
473
|
+
return str(s).lstrip(chars)
|
|
474
|
+
|
|
475
|
+
def builtin_rtrim(self, s: str, chars: str = None) -> str:
|
|
476
|
+
return str(s).rstrip(chars)
|
|
477
|
+
|
|
478
|
+
def builtin_split(self, s: str, sep: str = None, maxsplit: int = -1) -> list:
|
|
479
|
+
return str(s).split(sep, maxsplit)
|
|
480
|
+
|
|
481
|
+
def builtin_join(self, sep: str, items: list) -> str:
|
|
482
|
+
return str(sep).join(str(i) for i in items)
|
|
483
|
+
|
|
484
|
+
def builtin_replace(self, s: str, old: str, new: str, count: int = -1) -> str:
|
|
485
|
+
return str(s).replace(old, new, count)
|
|
486
|
+
|
|
487
|
+
def builtin_substr(self, s: str, start: int, length: int = None) -> str:
|
|
488
|
+
s = str(s)
|
|
489
|
+
if length is None:
|
|
490
|
+
return s[start:]
|
|
491
|
+
return s[start:start + length]
|
|
492
|
+
|
|
493
|
+
def builtin_contains(self, s: str, sub: str) -> bool:
|
|
494
|
+
return sub in str(s)
|
|
495
|
+
|
|
496
|
+
def builtin_startswith(self, s: str, prefix: str) -> bool:
|
|
497
|
+
return str(s).startswith(prefix)
|
|
498
|
+
|
|
499
|
+
def builtin_endswith(self, s: str, suffix: str) -> bool:
|
|
500
|
+
return str(s).endswith(suffix)
|
|
501
|
+
|
|
502
|
+
def builtin_format(self, template: str, *args, **kwargs) -> str:
|
|
503
|
+
return template.format(*args, **kwargs)
|
|
504
|
+
|
|
505
|
+
def builtin_concat(self, *args) -> str:
|
|
506
|
+
return ''.join(str(a) for a in args)
|
|
507
|
+
|
|
508
|
+
def builtin_repeat(self, s: str, count: int) -> str:
|
|
509
|
+
return str(s) * count
|
|
510
|
+
|
|
511
|
+
def builtin_reverse(self, value: Union[str, list]) -> Union[str, list]:
|
|
512
|
+
if isinstance(value, str):
|
|
513
|
+
return value[::-1]
|
|
514
|
+
if isinstance(value, list):
|
|
515
|
+
return value[::-1]
|
|
516
|
+
raise CSSLBuiltinError("reverse requires string or list")
|
|
517
|
+
|
|
518
|
+
def builtin_indexof(self, s: str, sub: str, start: int = 0) -> int:
|
|
519
|
+
return str(s).find(sub, start)
|
|
520
|
+
|
|
521
|
+
def builtin_lastindexof(self, s: str, sub: str) -> int:
|
|
522
|
+
return str(s).rfind(sub)
|
|
523
|
+
|
|
524
|
+
def builtin_padleft(self, s: str, width: int, char: str = ' ') -> str:
|
|
525
|
+
return str(s).rjust(width, char)
|
|
526
|
+
|
|
527
|
+
def builtin_padright(self, s: str, width: int, char: str = ' ') -> str:
|
|
528
|
+
return str(s).ljust(width, char)
|
|
529
|
+
|
|
530
|
+
# ============= List Functions =============
|
|
531
|
+
|
|
532
|
+
def builtin_push(self, lst: list, *items) -> list:
|
|
533
|
+
lst = list(lst)
|
|
534
|
+
lst.extend(items)
|
|
535
|
+
return lst
|
|
536
|
+
|
|
537
|
+
def builtin_pop(self, lst: list, index: int = -1) -> Any:
|
|
538
|
+
lst = list(lst)
|
|
539
|
+
return lst.pop(index)
|
|
540
|
+
|
|
541
|
+
def builtin_shift(self, lst: list) -> Any:
|
|
542
|
+
lst = list(lst)
|
|
543
|
+
return lst.pop(0) if lst else None
|
|
544
|
+
|
|
545
|
+
def builtin_unshift(self, lst: list, *items) -> list:
|
|
546
|
+
lst = list(lst)
|
|
547
|
+
for item in reversed(items):
|
|
548
|
+
lst.insert(0, item)
|
|
549
|
+
return lst
|
|
550
|
+
|
|
551
|
+
def builtin_slice(self, value: Union[str, list], start: int, end: int = None) -> Union[str, list]:
|
|
552
|
+
if end is None:
|
|
553
|
+
return value[start:]
|
|
554
|
+
return value[start:end]
|
|
555
|
+
|
|
556
|
+
def builtin_sort(self, lst: list, key: str = None) -> list:
|
|
557
|
+
lst = list(lst)
|
|
558
|
+
if key:
|
|
559
|
+
lst.sort(key=lambda x: x.get(key) if isinstance(x, dict) else x)
|
|
560
|
+
else:
|
|
561
|
+
lst.sort()
|
|
562
|
+
return lst
|
|
563
|
+
|
|
564
|
+
def builtin_rsort(self, lst: list, key: str = None) -> list:
|
|
565
|
+
lst = list(lst)
|
|
566
|
+
if key:
|
|
567
|
+
lst.sort(key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=True)
|
|
568
|
+
else:
|
|
569
|
+
lst.sort(reverse=True)
|
|
570
|
+
return lst
|
|
571
|
+
|
|
572
|
+
def builtin_unique(self, lst: list) -> list:
|
|
573
|
+
seen = []
|
|
574
|
+
result = []
|
|
575
|
+
for item in lst:
|
|
576
|
+
key = json.dumps(item, sort_keys=True) if isinstance(item, (dict, list)) else item
|
|
577
|
+
if key not in seen:
|
|
578
|
+
seen.append(key)
|
|
579
|
+
result.append(item)
|
|
580
|
+
return result
|
|
581
|
+
|
|
582
|
+
def builtin_flatten(self, lst: list, depth: int = 1) -> list:
|
|
583
|
+
result = []
|
|
584
|
+
for item in lst:
|
|
585
|
+
if isinstance(item, list) and depth > 0:
|
|
586
|
+
result.extend(self.builtin_flatten(item, depth - 1))
|
|
587
|
+
else:
|
|
588
|
+
result.append(item)
|
|
589
|
+
return result
|
|
590
|
+
|
|
591
|
+
def builtin_filter(self, lst: list, condition: Callable) -> list:
|
|
592
|
+
return [item for item in lst if condition(item)]
|
|
593
|
+
|
|
594
|
+
def builtin_map(self, lst: list, func: Callable) -> list:
|
|
595
|
+
return [func(item) for item in lst]
|
|
596
|
+
|
|
597
|
+
def builtin_reduce(self, lst: list, func: Callable, initial: Any = None) -> Any:
|
|
598
|
+
from functools import reduce
|
|
599
|
+
if initial is not None:
|
|
600
|
+
return reduce(func, lst, initial)
|
|
601
|
+
return reduce(func, lst)
|
|
602
|
+
|
|
603
|
+
def builtin_find(self, lst: list, condition: Callable) -> Any:
|
|
604
|
+
for item in lst:
|
|
605
|
+
if condition(item):
|
|
606
|
+
return item
|
|
607
|
+
return None
|
|
608
|
+
|
|
609
|
+
def builtin_findindex(self, lst: list, condition: Callable) -> int:
|
|
610
|
+
for i, item in enumerate(lst):
|
|
611
|
+
if condition(item):
|
|
612
|
+
return i
|
|
613
|
+
return -1
|
|
614
|
+
|
|
615
|
+
def builtin_every(self, lst: list, condition: Callable) -> bool:
|
|
616
|
+
return all(condition(item) for item in lst)
|
|
617
|
+
|
|
618
|
+
def builtin_some(self, lst: list, condition: Callable) -> bool:
|
|
619
|
+
return any(condition(item) for item in lst)
|
|
620
|
+
|
|
621
|
+
def builtin_range(self, *args) -> list:
|
|
622
|
+
return list(range(*args))
|
|
623
|
+
|
|
624
|
+
# ============= Dict Functions =============
|
|
625
|
+
|
|
626
|
+
def builtin_keys(self, d: dict) -> list:
|
|
627
|
+
return list(d.keys())
|
|
628
|
+
|
|
629
|
+
def builtin_values(self, d: dict) -> list:
|
|
630
|
+
return list(d.values())
|
|
631
|
+
|
|
632
|
+
def builtin_items(self, d: dict) -> list:
|
|
633
|
+
return list(d.items())
|
|
634
|
+
|
|
635
|
+
def builtin_haskey(self, d: dict, key: str) -> bool:
|
|
636
|
+
return key in d
|
|
637
|
+
|
|
638
|
+
def builtin_getkey(self, d: dict, key: str, default: Any = None) -> Any:
|
|
639
|
+
return d.get(key, default)
|
|
640
|
+
|
|
641
|
+
def builtin_setkey(self, d: dict, key: str, value: Any) -> dict:
|
|
642
|
+
d = dict(d)
|
|
643
|
+
d[key] = value
|
|
644
|
+
return d
|
|
645
|
+
|
|
646
|
+
def builtin_delkey(self, d: dict, key: str) -> dict:
|
|
647
|
+
d = dict(d)
|
|
648
|
+
d.pop(key, None)
|
|
649
|
+
return d
|
|
650
|
+
|
|
651
|
+
def builtin_merge(self, *dicts) -> dict:
|
|
652
|
+
result = {}
|
|
653
|
+
for d in dicts:
|
|
654
|
+
if isinstance(d, dict):
|
|
655
|
+
result.update(d)
|
|
656
|
+
return result
|
|
657
|
+
|
|
658
|
+
# ============= Math Functions =============
|
|
659
|
+
|
|
660
|
+
def builtin_abs(self, x: Union[int, float]) -> Union[int, float]:
|
|
661
|
+
return abs(x)
|
|
662
|
+
|
|
663
|
+
def builtin_min(self, *args) -> Any:
|
|
664
|
+
if len(args) == 1 and isinstance(args[0], (list, tuple)):
|
|
665
|
+
return min(args[0])
|
|
666
|
+
return min(args)
|
|
667
|
+
|
|
668
|
+
def builtin_max(self, *args) -> Any:
|
|
669
|
+
if len(args) == 1 and isinstance(args[0], (list, tuple)):
|
|
670
|
+
return max(args[0])
|
|
671
|
+
return max(args)
|
|
672
|
+
|
|
673
|
+
def builtin_sum(self, items: list, start: Union[int, float] = 0) -> Union[int, float]:
|
|
674
|
+
return sum(items, start)
|
|
675
|
+
|
|
676
|
+
def builtin_avg(self, items: list) -> float:
|
|
677
|
+
if not items:
|
|
678
|
+
return 0.0
|
|
679
|
+
return sum(items) / len(items)
|
|
680
|
+
|
|
681
|
+
def builtin_round(self, x: float, digits: int = 0) -> float:
|
|
682
|
+
return round(x, digits)
|
|
683
|
+
|
|
684
|
+
def builtin_floor(self, x: float) -> int:
|
|
685
|
+
import math
|
|
686
|
+
return math.floor(x)
|
|
687
|
+
|
|
688
|
+
def builtin_ceil(self, x: float) -> int:
|
|
689
|
+
import math
|
|
690
|
+
return math.ceil(x)
|
|
691
|
+
|
|
692
|
+
def builtin_pow(self, base: Union[int, float], exp: Union[int, float]) -> Union[int, float]:
|
|
693
|
+
return pow(base, exp)
|
|
694
|
+
|
|
695
|
+
def builtin_sqrt(self, x: Union[int, float]) -> float:
|
|
696
|
+
import math
|
|
697
|
+
return math.sqrt(x)
|
|
698
|
+
|
|
699
|
+
def builtin_mod(self, a: int, b: int) -> int:
|
|
700
|
+
return a % b
|
|
701
|
+
|
|
702
|
+
def builtin_random(self) -> float:
|
|
703
|
+
return random.random()
|
|
704
|
+
|
|
705
|
+
def builtin_randint(self, a: int, b: int) -> int:
|
|
706
|
+
return random.randint(a, b)
|
|
707
|
+
|
|
708
|
+
def builtin_sin(self, x: float) -> float:
|
|
709
|
+
return math.sin(x)
|
|
710
|
+
|
|
711
|
+
def builtin_cos(self, x: float) -> float:
|
|
712
|
+
return math.cos(x)
|
|
713
|
+
|
|
714
|
+
def builtin_tan(self, x: float) -> float:
|
|
715
|
+
return math.tan(x)
|
|
716
|
+
|
|
717
|
+
def builtin_asin(self, x: float) -> float:
|
|
718
|
+
return math.asin(x)
|
|
719
|
+
|
|
720
|
+
def builtin_acos(self, x: float) -> float:
|
|
721
|
+
return math.acos(x)
|
|
722
|
+
|
|
723
|
+
def builtin_atan(self, x: float) -> float:
|
|
724
|
+
return math.atan(x)
|
|
725
|
+
|
|
726
|
+
def builtin_atan2(self, y: float, x: float) -> float:
|
|
727
|
+
return math.atan2(y, x)
|
|
728
|
+
|
|
729
|
+
def builtin_log(self, x: float, base: float = math.e) -> float:
|
|
730
|
+
return math.log(x, base)
|
|
731
|
+
|
|
732
|
+
def builtin_log10(self, x: float) -> float:
|
|
733
|
+
return math.log10(x)
|
|
734
|
+
|
|
735
|
+
def builtin_exp(self, x: float) -> float:
|
|
736
|
+
return math.exp(x)
|
|
737
|
+
|
|
738
|
+
def builtin_radians(self, degrees: float) -> float:
|
|
739
|
+
return math.radians(degrees)
|
|
740
|
+
|
|
741
|
+
def builtin_degrees(self, radians: float) -> float:
|
|
742
|
+
return math.degrees(radians)
|
|
743
|
+
|
|
744
|
+
# ============= Time Functions =============
|
|
745
|
+
|
|
746
|
+
def builtin_now(self) -> float:
|
|
747
|
+
return time.time()
|
|
748
|
+
|
|
749
|
+
def builtin_timestamp(self) -> int:
|
|
750
|
+
return int(time.time())
|
|
751
|
+
|
|
752
|
+
def builtin_sleep(self, seconds: float) -> None:
|
|
753
|
+
time.sleep(seconds)
|
|
754
|
+
|
|
755
|
+
def builtin_date(self, format_str: str = '%Y-%m-%d') -> str:
|
|
756
|
+
return datetime.now().strftime(format_str)
|
|
757
|
+
|
|
758
|
+
def builtin_time(self, format_str: str = '%H:%M:%S') -> str:
|
|
759
|
+
return datetime.now().strftime(format_str)
|
|
760
|
+
|
|
761
|
+
def builtin_datetime(self, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
|
|
762
|
+
return datetime.now().strftime(format_str)
|
|
763
|
+
|
|
764
|
+
def builtin_strftime(self, format_str: str, timestamp: float = None) -> str:
|
|
765
|
+
if timestamp is None:
|
|
766
|
+
return datetime.now().strftime(format_str)
|
|
767
|
+
return datetime.fromtimestamp(timestamp).strftime(format_str)
|
|
768
|
+
|
|
769
|
+
# ============= File/Path Functions =============
|
|
770
|
+
|
|
771
|
+
def builtin_pathexists(self, path: str) -> bool:
|
|
772
|
+
return os.path.exists(path)
|
|
773
|
+
|
|
774
|
+
def builtin_isfile(self, path: str) -> bool:
|
|
775
|
+
return os.path.isfile(path)
|
|
776
|
+
|
|
777
|
+
def builtin_isdir(self, path: str) -> bool:
|
|
778
|
+
return os.path.isdir(path)
|
|
779
|
+
|
|
780
|
+
def builtin_basename(self, path: str) -> str:
|
|
781
|
+
return os.path.basename(path)
|
|
782
|
+
|
|
783
|
+
def builtin_dirname(self, path: str) -> str:
|
|
784
|
+
return os.path.dirname(path)
|
|
785
|
+
|
|
786
|
+
def builtin_joinpath(self, *parts) -> str:
|
|
787
|
+
return os.path.join(*parts)
|
|
788
|
+
|
|
789
|
+
def builtin_splitpath(self, path: str) -> list:
|
|
790
|
+
return list(os.path.split(path))
|
|
791
|
+
|
|
792
|
+
def builtin_abspath(self, path: str) -> str:
|
|
793
|
+
return os.path.abspath(path)
|
|
794
|
+
|
|
795
|
+
def builtin_normpath(self, path: str) -> str:
|
|
796
|
+
return os.path.normpath(path)
|
|
797
|
+
|
|
798
|
+
# ============= File I/O Functions =============
|
|
799
|
+
|
|
800
|
+
def builtin_readfile(self, path: str, encoding: str = 'utf-8') -> str:
|
|
801
|
+
"""Read entire file content"""
|
|
802
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
803
|
+
return f.read()
|
|
804
|
+
|
|
805
|
+
def builtin_writefile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
|
|
806
|
+
"""Write content to file, returns bytes written"""
|
|
807
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
808
|
+
return f.write(content)
|
|
809
|
+
|
|
810
|
+
def builtin_appendfile(self, path: str, content: str, encoding: str = 'utf-8') -> int:
|
|
811
|
+
"""Append content to file, returns bytes written"""
|
|
812
|
+
with open(path, 'a', encoding=encoding) as f:
|
|
813
|
+
return f.write(content)
|
|
814
|
+
|
|
815
|
+
def builtin_readlines(self, path: str, encoding: str = 'utf-8') -> list:
|
|
816
|
+
"""Read file lines into list"""
|
|
817
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
818
|
+
return f.readlines()
|
|
819
|
+
|
|
820
|
+
def builtin_listdir(self, path: str = '.') -> list:
|
|
821
|
+
"""List directory contents"""
|
|
822
|
+
return os.listdir(path)
|
|
823
|
+
|
|
824
|
+
def builtin_makedirs(self, path: str, exist_ok: bool = True) -> bool:
|
|
825
|
+
"""Create directories recursively"""
|
|
826
|
+
os.makedirs(path, exist_ok=exist_ok)
|
|
827
|
+
return True
|
|
828
|
+
|
|
829
|
+
def builtin_removefile(self, path: str) -> bool:
|
|
830
|
+
"""Remove a file"""
|
|
831
|
+
os.remove(path)
|
|
832
|
+
return True
|
|
833
|
+
|
|
834
|
+
def builtin_removedir(self, path: str) -> bool:
|
|
835
|
+
"""Remove an empty directory"""
|
|
836
|
+
os.rmdir(path)
|
|
837
|
+
return True
|
|
838
|
+
|
|
839
|
+
def builtin_copyfile(self, src: str, dst: str) -> str:
|
|
840
|
+
"""Copy a file, returns destination path"""
|
|
841
|
+
import shutil
|
|
842
|
+
return shutil.copy2(src, dst)
|
|
843
|
+
|
|
844
|
+
def builtin_movefile(self, src: str, dst: str) -> str:
|
|
845
|
+
"""Move a file, returns destination path"""
|
|
846
|
+
import shutil
|
|
847
|
+
return shutil.move(src, dst)
|
|
848
|
+
|
|
849
|
+
def builtin_filesize(self, path: str) -> int:
|
|
850
|
+
"""Get file size in bytes"""
|
|
851
|
+
return os.path.getsize(path)
|
|
852
|
+
|
|
853
|
+
# ============= JSON Functions =============
|
|
854
|
+
|
|
855
|
+
def builtin_tojson(self, value: Any, indent: int = None) -> str:
|
|
856
|
+
return json.dumps(value, indent=indent, ensure_ascii=False)
|
|
857
|
+
|
|
858
|
+
def builtin_fromjson(self, s: str) -> Any:
|
|
859
|
+
return json.loads(s)
|
|
860
|
+
|
|
861
|
+
# ============= Regex Functions =============
|
|
862
|
+
|
|
863
|
+
def builtin_match(self, pattern: str, string: str) -> Optional[dict]:
|
|
864
|
+
m = re.match(pattern, string)
|
|
865
|
+
if m:
|
|
866
|
+
return {'match': m.group(), 'groups': m.groups(), 'start': m.start(), 'end': m.end()}
|
|
867
|
+
return None
|
|
868
|
+
|
|
869
|
+
def builtin_search(self, pattern: str, string: str) -> Optional[dict]:
|
|
870
|
+
m = re.search(pattern, string)
|
|
871
|
+
if m:
|
|
872
|
+
return {'match': m.group(), 'groups': m.groups(), 'start': m.start(), 'end': m.end()}
|
|
873
|
+
return None
|
|
874
|
+
|
|
875
|
+
def builtin_findall(self, pattern: str, string: str) -> list:
|
|
876
|
+
return re.findall(pattern, string)
|
|
877
|
+
|
|
878
|
+
def builtin_sub(self, pattern: str, repl: str, string: str, count: int = 0) -> str:
|
|
879
|
+
return re.sub(pattern, repl, string, count)
|
|
880
|
+
|
|
881
|
+
# ============= Hash Functions =============
|
|
882
|
+
|
|
883
|
+
def builtin_md5(self, s: str) -> str:
|
|
884
|
+
return hashlib.md5(s.encode()).hexdigest()
|
|
885
|
+
|
|
886
|
+
def builtin_sha1(self, s: str) -> str:
|
|
887
|
+
return hashlib.sha1(s.encode()).hexdigest()
|
|
888
|
+
|
|
889
|
+
def builtin_sha256(self, s: str) -> str:
|
|
890
|
+
return hashlib.sha256(s.encode()).hexdigest()
|
|
891
|
+
|
|
892
|
+
# ============= Utility Functions =============
|
|
893
|
+
|
|
894
|
+
def builtin_copy(self, value: Any) -> Any:
|
|
895
|
+
import copy
|
|
896
|
+
return copy.copy(value)
|
|
897
|
+
|
|
898
|
+
def builtin_deepcopy(self, value: Any) -> Any:
|
|
899
|
+
import copy
|
|
900
|
+
return copy.deepcopy(value)
|
|
901
|
+
|
|
902
|
+
def builtin_assert(self, condition: bool, message: str = "Assertion failed") -> None:
|
|
903
|
+
if not condition:
|
|
904
|
+
raise CSSLBuiltinError(message)
|
|
905
|
+
|
|
906
|
+
def builtin_exit(self, code: int = 0) -> None:
|
|
907
|
+
if self.runtime and hasattr(self.runtime, 'exit'):
|
|
908
|
+
self.runtime.exit(code)
|
|
909
|
+
else:
|
|
910
|
+
raise SystemExit(code)
|
|
911
|
+
|
|
912
|
+
def builtin_env(self, name: str, default: str = None) -> Optional[str]:
|
|
913
|
+
return os.environ.get(name, default)
|
|
914
|
+
|
|
915
|
+
def builtin_setenv(self, name: str, value: str) -> None:
|
|
916
|
+
"""Set environment variable"""
|
|
917
|
+
os.environ[name] = value
|
|
918
|
+
|
|
919
|
+
def builtin_input(self, prompt: str = '') -> str:
|
|
920
|
+
"""Read user input"""
|
|
921
|
+
return input(prompt)
|
|
922
|
+
|
|
923
|
+
def builtin_clear(self) -> None:
|
|
924
|
+
"""Clear console screen"""
|
|
925
|
+
if os.name == 'nt':
|
|
926
|
+
os.system('cls')
|
|
927
|
+
else:
|
|
928
|
+
print('\033[2J\033[H', end='')
|
|
929
|
+
|
|
930
|
+
def builtin_color(self, text: str, color: str) -> str:
|
|
931
|
+
"""Apply ANSI color to text"""
|
|
932
|
+
colors = {
|
|
933
|
+
'black': '30', 'red': '31', 'green': '32', 'yellow': '33',
|
|
934
|
+
'blue': '34', 'magenta': '35', 'cyan': '36', 'white': '37',
|
|
935
|
+
'bright_black': '90', 'bright_red': '91', 'bright_green': '92',
|
|
936
|
+
'bright_yellow': '93', 'bright_blue': '94', 'bright_magenta': '95',
|
|
937
|
+
'bright_cyan': '96', 'bright_white': '97',
|
|
938
|
+
'reset': '0', 'bold': '1', 'dim': '2', 'italic': '3',
|
|
939
|
+
'underline': '4', 'blink': '5', 'reverse': '7'
|
|
940
|
+
}
|
|
941
|
+
code = colors.get(color.lower(), color)
|
|
942
|
+
return f'\033[{code}m{text}\033[0m'
|
|
943
|
+
|
|
944
|
+
def builtin_delay(self, ms: float) -> None:
|
|
945
|
+
"""Delay execution by milliseconds"""
|
|
946
|
+
time.sleep(ms / 1000.0)
|
|
947
|
+
|
|
948
|
+
def builtin_pyimport(self, module_name: str) -> Any:
|
|
949
|
+
"""Import a Python module for use in CSSL"""
|
|
950
|
+
return __import__(module_name)
|
|
951
|
+
|
|
952
|
+
# ============= Extended String Functions =============
|
|
953
|
+
|
|
954
|
+
def builtin_sprintf(self, fmt: str, *args) -> str:
|
|
955
|
+
"""C-style format string"""
|
|
956
|
+
return fmt % args
|
|
957
|
+
|
|
958
|
+
def builtin_chars(self, s: str) -> list:
|
|
959
|
+
"""Convert string to list of characters"""
|
|
960
|
+
return list(s)
|
|
961
|
+
|
|
962
|
+
def builtin_ord(self, c: str) -> int:
|
|
963
|
+
"""Get ASCII/Unicode code of character"""
|
|
964
|
+
return ord(c[0] if c else '\0')
|
|
965
|
+
|
|
966
|
+
def builtin_chr(self, n: int) -> str:
|
|
967
|
+
"""Convert ASCII/Unicode code to character"""
|
|
968
|
+
return chr(n)
|
|
969
|
+
|
|
970
|
+
def builtin_capitalize(self, s: str) -> str:
|
|
971
|
+
return str(s).capitalize()
|
|
972
|
+
|
|
973
|
+
def builtin_title(self, s: str) -> str:
|
|
974
|
+
return str(s).title()
|
|
975
|
+
|
|
976
|
+
def builtin_swapcase(self, s: str) -> str:
|
|
977
|
+
return str(s).swapcase()
|
|
978
|
+
|
|
979
|
+
def builtin_center(self, s: str, width: int, fillchar: str = ' ') -> str:
|
|
980
|
+
return str(s).center(width, fillchar)
|
|
981
|
+
|
|
982
|
+
def builtin_zfill(self, s: str, width: int) -> str:
|
|
983
|
+
return str(s).zfill(width)
|
|
984
|
+
|
|
985
|
+
def builtin_isalpha(self, s: str) -> bool:
|
|
986
|
+
return str(s).isalpha()
|
|
987
|
+
|
|
988
|
+
def builtin_isdigit(self, s: str) -> bool:
|
|
989
|
+
return str(s).isdigit()
|
|
990
|
+
|
|
991
|
+
def builtin_isalnum(self, s: str) -> bool:
|
|
992
|
+
return str(s).isalnum()
|
|
993
|
+
|
|
994
|
+
def builtin_isspace(self, s: str) -> bool:
|
|
995
|
+
return str(s).isspace()
|
|
996
|
+
|
|
997
|
+
# ============= Extended List Functions =============
|
|
998
|
+
|
|
999
|
+
def builtin_enumerate(self, lst: list, start: int = 0) -> list:
|
|
1000
|
+
"""Return list of (index, value) pairs"""
|
|
1001
|
+
return list(enumerate(lst, start))
|
|
1002
|
+
|
|
1003
|
+
def builtin_zip(self, *lists) -> list:
|
|
1004
|
+
"""Zip multiple lists together"""
|
|
1005
|
+
return list(zip(*lists))
|
|
1006
|
+
|
|
1007
|
+
def builtin_reversed(self, lst: list) -> list:
|
|
1008
|
+
"""Return reversed list"""
|
|
1009
|
+
return list(reversed(lst))
|
|
1010
|
+
|
|
1011
|
+
def builtin_sorted(self, lst: list, key: str = None, reverse: bool = False) -> list:
|
|
1012
|
+
"""Return sorted list"""
|
|
1013
|
+
if key:
|
|
1014
|
+
return sorted(lst, key=lambda x: x.get(key) if isinstance(x, dict) else x, reverse=reverse)
|
|
1015
|
+
return sorted(lst, reverse=reverse)
|
|
1016
|
+
|
|
1017
|
+
def builtin_count(self, collection: Union[list, str], item: Any) -> int:
|
|
1018
|
+
"""Count occurrences of item"""
|
|
1019
|
+
return collection.count(item)
|
|
1020
|
+
|
|
1021
|
+
def builtin_first(self, lst: list, default: Any = None) -> Any:
|
|
1022
|
+
"""Get first element or default"""
|
|
1023
|
+
return lst[0] if lst else default
|
|
1024
|
+
|
|
1025
|
+
def builtin_last(self, lst: list, default: Any = None) -> Any:
|
|
1026
|
+
"""Get last element or default"""
|
|
1027
|
+
return lst[-1] if lst else default
|
|
1028
|
+
|
|
1029
|
+
def builtin_take(self, lst: list, n: int) -> list:
|
|
1030
|
+
"""Take first n elements"""
|
|
1031
|
+
return lst[:n]
|
|
1032
|
+
|
|
1033
|
+
def builtin_drop(self, lst: list, n: int) -> list:
|
|
1034
|
+
"""Drop first n elements"""
|
|
1035
|
+
return lst[n:]
|
|
1036
|
+
|
|
1037
|
+
def builtin_chunk(self, lst: list, size: int) -> list:
|
|
1038
|
+
"""Split list into chunks of given size"""
|
|
1039
|
+
return [lst[i:i + size] for i in range(0, len(lst), size)]
|
|
1040
|
+
|
|
1041
|
+
def builtin_groupby(self, lst: list, key: str) -> dict:
|
|
1042
|
+
"""Group list of dicts by key"""
|
|
1043
|
+
result = {}
|
|
1044
|
+
for item in lst:
|
|
1045
|
+
k = item.get(key) if isinstance(item, dict) else getattr(item, key, None)
|
|
1046
|
+
if k not in result:
|
|
1047
|
+
result[k] = []
|
|
1048
|
+
result[k].append(item)
|
|
1049
|
+
return result
|
|
1050
|
+
|
|
1051
|
+
def builtin_shuffle(self, lst: list) -> list:
|
|
1052
|
+
"""Return shuffled copy of list"""
|
|
1053
|
+
result = list(lst)
|
|
1054
|
+
random.shuffle(result)
|
|
1055
|
+
return result
|
|
1056
|
+
|
|
1057
|
+
def builtin_sample(self, lst: list, k: int) -> list:
|
|
1058
|
+
"""Return k random elements from list"""
|
|
1059
|
+
return random.sample(lst, min(k, len(lst)))
|
|
1060
|
+
|
|
1061
|
+
# ============= Extended Dict Functions =============
|
|
1062
|
+
|
|
1063
|
+
def builtin_update(self, d: dict, other: dict) -> dict:
|
|
1064
|
+
"""Update dict with another dict, return new dict"""
|
|
1065
|
+
result = dict(d)
|
|
1066
|
+
result.update(other)
|
|
1067
|
+
return result
|
|
1068
|
+
|
|
1069
|
+
def builtin_fromkeys(self, keys: list, value: Any = None) -> dict:
|
|
1070
|
+
"""Create dict from keys with default value"""
|
|
1071
|
+
return dict.fromkeys(keys, value)
|
|
1072
|
+
|
|
1073
|
+
def builtin_invert(self, d: dict) -> dict:
|
|
1074
|
+
"""Swap keys and values"""
|
|
1075
|
+
return {v: k for k, v in d.items()}
|
|
1076
|
+
|
|
1077
|
+
def builtin_pick(self, d: dict, *keys) -> dict:
|
|
1078
|
+
"""Pick only specified keys from dict"""
|
|
1079
|
+
return {k: d[k] for k in keys if k in d}
|
|
1080
|
+
|
|
1081
|
+
def builtin_omit(self, d: dict, *keys) -> dict:
|
|
1082
|
+
"""Omit specified keys from dict"""
|
|
1083
|
+
return {k: v for k, v in d.items() if k not in keys}
|
|
1084
|
+
|
|
1085
|
+
# ============= CSSL System Functions =============
|
|
1086
|
+
|
|
1087
|
+
def builtin_createcmd(self, cmd_name: str, handler: Callable = None) -> bool:
|
|
1088
|
+
"""
|
|
1089
|
+
Create a custom console command
|
|
1090
|
+
Usage: createcmd('mycommand') <== { ... handler code ... }
|
|
1091
|
+
"""
|
|
1092
|
+
if not self.runtime:
|
|
1093
|
+
print(f"Cannot create command '{cmd_name}': No runtime available")
|
|
1094
|
+
return False
|
|
1095
|
+
|
|
1096
|
+
# Store the command handler in runtime
|
|
1097
|
+
if not hasattr(self.runtime, '_custom_commands'):
|
|
1098
|
+
self.runtime._custom_commands = {}
|
|
1099
|
+
|
|
1100
|
+
self.runtime._custom_commands[cmd_name] = handler
|
|
1101
|
+
|
|
1102
|
+
# Find Console via multiple paths
|
|
1103
|
+
console = None
|
|
1104
|
+
|
|
1105
|
+
# Try 1: Direct _console reference on runtime
|
|
1106
|
+
if hasattr(self.runtime, '_console') and self.runtime._console:
|
|
1107
|
+
console = self.runtime._console
|
|
1108
|
+
|
|
1109
|
+
# Try 2: Via service_engine.Console
|
|
1110
|
+
elif self.runtime.service_engine and hasattr(self.runtime.service_engine, 'Console'):
|
|
1111
|
+
console = self.runtime.service_engine.Console
|
|
1112
|
+
|
|
1113
|
+
# Register with Console if found
|
|
1114
|
+
if console and hasattr(console, 'register_custom_command'):
|
|
1115
|
+
console.register_custom_command(cmd_name, handler)
|
|
1116
|
+
else:
|
|
1117
|
+
print(f"Custom command '{cmd_name}' stored (Console not yet available)")
|
|
1118
|
+
|
|
1119
|
+
return True
|
|
1120
|
+
|
|
1121
|
+
def builtin_signal(self, event_ref: Any, action: str = '+') -> bool:
|
|
1122
|
+
"""
|
|
1123
|
+
Send or register a signal/event
|
|
1124
|
+
Usage: signal(@event.CustomEvent, '+') to emit, '-' to unregister
|
|
1125
|
+
"""
|
|
1126
|
+
try:
|
|
1127
|
+
from .cssl_events import get_event_manager, EventType
|
|
1128
|
+
|
|
1129
|
+
event_manager = get_event_manager()
|
|
1130
|
+
|
|
1131
|
+
# Handle event reference (could be string or module ref)
|
|
1132
|
+
event_name = str(event_ref) if not isinstance(event_ref, str) else event_ref
|
|
1133
|
+
|
|
1134
|
+
if action == '+':
|
|
1135
|
+
# Emit the event
|
|
1136
|
+
event_manager.emit_custom(event_name, source="cssl_signal")
|
|
1137
|
+
print(f"Signal emitted: {event_name}")
|
|
1138
|
+
return True
|
|
1139
|
+
elif action == '-':
|
|
1140
|
+
# Unregister handlers for this event
|
|
1141
|
+
# This would need custom implementation
|
|
1142
|
+
print(f"Signal handlers cleared: {event_name}")
|
|
1143
|
+
return True
|
|
1144
|
+
else:
|
|
1145
|
+
print(f"Unknown signal action: {action}")
|
|
1146
|
+
return False
|
|
1147
|
+
|
|
1148
|
+
except Exception as e:
|
|
1149
|
+
print(f"Signal error: {e}")
|
|
1150
|
+
return False
|
|
1151
|
+
|
|
1152
|
+
def builtin_appexec(self, app_name: str, *args) -> bool:
|
|
1153
|
+
"""
|
|
1154
|
+
Start a desktop application (visually)
|
|
1155
|
+
Usage: appexec('xface')
|
|
1156
|
+
"""
|
|
1157
|
+
if not self.runtime or not self.runtime.service_engine:
|
|
1158
|
+
print(f"Cannot execute app '{app_name}': No service engine available")
|
|
1159
|
+
return False
|
|
1160
|
+
|
|
1161
|
+
try:
|
|
1162
|
+
kernel = self.runtime.service_engine.KernelClient
|
|
1163
|
+
|
|
1164
|
+
# Check if desktop environment is available
|
|
1165
|
+
if hasattr(kernel, 'start_desktop_app'):
|
|
1166
|
+
return kernel.start_desktop_app(app_name, *args)
|
|
1167
|
+
|
|
1168
|
+
# Try to find and launch the app
|
|
1169
|
+
app_paths = [
|
|
1170
|
+
os.path.join(kernel.RootDirectory, 'apps', app_name),
|
|
1171
|
+
os.path.join(kernel.RootDirectory, 'apps', f'{app_name}.py'),
|
|
1172
|
+
os.path.join(kernel.RootDirectory, 'desktop', 'apps', app_name),
|
|
1173
|
+
os.path.join(kernel.RootDirectory, 'desktop', 'apps', f'{app_name}.py'),
|
|
1174
|
+
]
|
|
1175
|
+
|
|
1176
|
+
for app_path in app_paths:
|
|
1177
|
+
if os.path.exists(app_path):
|
|
1178
|
+
print(f"Launching app: {app_name}")
|
|
1179
|
+
if app_path.endswith('.py'):
|
|
1180
|
+
return self.builtin_initpy(app_path)
|
|
1181
|
+
return True
|
|
1182
|
+
|
|
1183
|
+
print(f"App not found: {app_name}")
|
|
1184
|
+
return False
|
|
1185
|
+
|
|
1186
|
+
except Exception as e:
|
|
1187
|
+
print(f"App execution error: {e}")
|
|
1188
|
+
return False
|
|
1189
|
+
|
|
1190
|
+
def builtin_initpy(self, path: str, *args, **kwargs) -> Any:
|
|
1191
|
+
"""
|
|
1192
|
+
Execute a Python file
|
|
1193
|
+
Usage: initpy('/path/to/script.py')
|
|
1194
|
+
"""
|
|
1195
|
+
if not os.path.isabs(path) and self.runtime and self.runtime.service_engine:
|
|
1196
|
+
path = os.path.join(self.runtime.service_engine.KernelClient.RootDirectory, path)
|
|
1197
|
+
|
|
1198
|
+
if not os.path.exists(path):
|
|
1199
|
+
raise CSSLBuiltinError(f"Python file not found: {path}")
|
|
1200
|
+
|
|
1201
|
+
try:
|
|
1202
|
+
# Prepare execution context
|
|
1203
|
+
exec_globals = {
|
|
1204
|
+
'__file__': path,
|
|
1205
|
+
'__name__': '__main__',
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
# Add kernel and service engine if available
|
|
1209
|
+
if self.runtime and self.runtime.service_engine:
|
|
1210
|
+
exec_globals['kernel'] = self.runtime.service_engine.KernelClient
|
|
1211
|
+
exec_globals['service'] = self.runtime.service_engine
|
|
1212
|
+
exec_globals['args'] = args
|
|
1213
|
+
exec_globals['kwargs'] = kwargs
|
|
1214
|
+
|
|
1215
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
1216
|
+
code = f.read()
|
|
1217
|
+
|
|
1218
|
+
exec(compile(code, path, 'exec'), exec_globals)
|
|
1219
|
+
return exec_globals.get('result', True)
|
|
1220
|
+
|
|
1221
|
+
except Exception as e:
|
|
1222
|
+
print(f"Python execution error [{path}]: {e}")
|
|
1223
|
+
raise CSSLBuiltinError(f"initpy failed: {e}")
|
|
1224
|
+
|
|
1225
|
+
def builtin_initsh(self, path: str, *args) -> int:
|
|
1226
|
+
"""
|
|
1227
|
+
Execute a shell script
|
|
1228
|
+
Usage: initsh('/path/to/script.sh')
|
|
1229
|
+
"""
|
|
1230
|
+
import subprocess
|
|
1231
|
+
|
|
1232
|
+
if not os.path.isabs(path) and self.runtime and self.runtime.service_engine:
|
|
1233
|
+
path = os.path.join(self.runtime.service_engine.KernelClient.RootDirectory, path)
|
|
1234
|
+
|
|
1235
|
+
if not os.path.exists(path):
|
|
1236
|
+
raise CSSLBuiltinError(f"Shell script not found: {path}")
|
|
1237
|
+
|
|
1238
|
+
try:
|
|
1239
|
+
# Determine shell based on platform
|
|
1240
|
+
import platform
|
|
1241
|
+
if platform.system() == 'Windows':
|
|
1242
|
+
# Use cmd or powershell on Windows
|
|
1243
|
+
cmd = ['cmd', '/c', path] + list(args)
|
|
1244
|
+
else:
|
|
1245
|
+
cmd = ['bash', path] + list(args)
|
|
1246
|
+
|
|
1247
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
1248
|
+
|
|
1249
|
+
if result.stdout:
|
|
1250
|
+
print(result.stdout)
|
|
1251
|
+
if result.stderr:
|
|
1252
|
+
print(f"STDERR: {result.stderr}")
|
|
1253
|
+
|
|
1254
|
+
return result.returncode
|
|
1255
|
+
|
|
1256
|
+
except Exception as e:
|
|
1257
|
+
print(f"Shell execution error [{path}]: {e}")
|
|
1258
|
+
raise CSSLBuiltinError(f"initsh failed: {e}")
|
|
1259
|
+
|
|
1260
|
+
def builtin_wait_for(self, condition: Callable, timeout: float = 30.0, interval: float = 0.1) -> bool:
|
|
1261
|
+
"""
|
|
1262
|
+
Wait for a condition to become true
|
|
1263
|
+
Usage: wait_for(lambda: some_condition, timeout=30)
|
|
1264
|
+
"""
|
|
1265
|
+
import time
|
|
1266
|
+
start = time.time()
|
|
1267
|
+
|
|
1268
|
+
while time.time() - start < timeout:
|
|
1269
|
+
try:
|
|
1270
|
+
if callable(condition):
|
|
1271
|
+
if condition():
|
|
1272
|
+
return True
|
|
1273
|
+
elif condition:
|
|
1274
|
+
return True
|
|
1275
|
+
except Exception:
|
|
1276
|
+
pass
|
|
1277
|
+
time.sleep(interval)
|
|
1278
|
+
|
|
1279
|
+
return False
|
|
1280
|
+
|
|
1281
|
+
def builtin_wait_for_event(self, event_name: str, timeout: float = 30.0) -> bool:
|
|
1282
|
+
"""
|
|
1283
|
+
Wait for a specific event to occur
|
|
1284
|
+
Usage: wait_for_event('@event.Booted', timeout=60)
|
|
1285
|
+
"""
|
|
1286
|
+
try:
|
|
1287
|
+
from .cssl_events import get_event_manager
|
|
1288
|
+
|
|
1289
|
+
event_manager = get_event_manager()
|
|
1290
|
+
event_occurred = [False]
|
|
1291
|
+
|
|
1292
|
+
def handler(event_data):
|
|
1293
|
+
event_occurred[0] = True
|
|
1294
|
+
|
|
1295
|
+
# Register temporary handler
|
|
1296
|
+
handler_id = event_manager.register_custom(
|
|
1297
|
+
event_name,
|
|
1298
|
+
handler,
|
|
1299
|
+
once=True
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
# Wait for event
|
|
1303
|
+
import time
|
|
1304
|
+
start = time.time()
|
|
1305
|
+
while not event_occurred[0] and (time.time() - start) < timeout:
|
|
1306
|
+
time.sleep(0.1)
|
|
1307
|
+
|
|
1308
|
+
# Cleanup if not occurred
|
|
1309
|
+
if not event_occurred[0]:
|
|
1310
|
+
event_manager.unregister(handler_id)
|
|
1311
|
+
|
|
1312
|
+
return event_occurred[0]
|
|
1313
|
+
|
|
1314
|
+
except Exception as e:
|
|
1315
|
+
print(f"wait_for_event error: {e}")
|
|
1316
|
+
return False
|
|
1317
|
+
|
|
1318
|
+
def builtin_wait_for_booted(self, timeout: float = 60.0) -> bool:
|
|
1319
|
+
"""
|
|
1320
|
+
Wait until the system is fully booted
|
|
1321
|
+
Usage: await wait_for_booted()
|
|
1322
|
+
"""
|
|
1323
|
+
if not self.runtime or not self.runtime.service_engine:
|
|
1324
|
+
return False
|
|
1325
|
+
|
|
1326
|
+
import time
|
|
1327
|
+
start = time.time()
|
|
1328
|
+
|
|
1329
|
+
while time.time() - start < timeout:
|
|
1330
|
+
try:
|
|
1331
|
+
wheel = self.runtime.service_engine.KernelClient.WheelKernel
|
|
1332
|
+
booted = wheel.ReadWheelParam('boot', 'BOOTED')
|
|
1333
|
+
if booted == '1':
|
|
1334
|
+
return True
|
|
1335
|
+
except Exception:
|
|
1336
|
+
pass
|
|
1337
|
+
time.sleep(0.5)
|
|
1338
|
+
|
|
1339
|
+
return False
|
|
1340
|
+
|
|
1341
|
+
def builtin_emit(self, event_name: str, data: Any = None) -> bool:
|
|
1342
|
+
"""
|
|
1343
|
+
Emit a custom event
|
|
1344
|
+
Usage: emit('MyCustomEvent', {data: 'value'})
|
|
1345
|
+
"""
|
|
1346
|
+
try:
|
|
1347
|
+
from .cssl_events import get_event_manager
|
|
1348
|
+
|
|
1349
|
+
event_manager = get_event_manager()
|
|
1350
|
+
event_manager.emit_custom(event_name, source="cssl", data=data or {})
|
|
1351
|
+
return True
|
|
1352
|
+
|
|
1353
|
+
except Exception as e:
|
|
1354
|
+
print(f"emit error: {e}")
|
|
1355
|
+
return False
|
|
1356
|
+
|
|
1357
|
+
def builtin_on_event(self, event_name: str, handler: Callable) -> str:
|
|
1358
|
+
"""
|
|
1359
|
+
Register an event handler
|
|
1360
|
+
Usage: on_event('MyEvent', handler_function)
|
|
1361
|
+
Returns: handler_id for later removal
|
|
1362
|
+
"""
|
|
1363
|
+
try:
|
|
1364
|
+
from .cssl_events import get_event_manager
|
|
1365
|
+
|
|
1366
|
+
event_manager = get_event_manager()
|
|
1367
|
+
handler_id = event_manager.register_custom(event_name, handler)
|
|
1368
|
+
return handler_id
|
|
1369
|
+
|
|
1370
|
+
except Exception as e:
|
|
1371
|
+
print(f"on_event error: {e}")
|
|
1372
|
+
return ""
|
|
1373
|
+
|
|
1374
|
+
# ============= CSSL Import System Functions =============
|
|
1375
|
+
|
|
1376
|
+
def builtin_cso_root(self, path: str = "") -> str:
|
|
1377
|
+
"""
|
|
1378
|
+
Get absolute path relative to CSO root directory
|
|
1379
|
+
Usage: cso_root('/services/myservice.cssl')
|
|
1380
|
+
Returns: Full absolute path to the file
|
|
1381
|
+
"""
|
|
1382
|
+
base = os.getcwd()
|
|
1383
|
+
|
|
1384
|
+
# Try to get base from kernel parameters
|
|
1385
|
+
if self.runtime and self.runtime.service_engine:
|
|
1386
|
+
try:
|
|
1387
|
+
kernel = self.runtime.service_engine.KernelClient
|
|
1388
|
+
if hasattr(kernel, 'WheelKernel'):
|
|
1389
|
+
wheel = kernel.WheelKernel
|
|
1390
|
+
if hasattr(wheel, 'KernelParam'):
|
|
1391
|
+
base = wheel.KernelParam.get('@base', base)
|
|
1392
|
+
if hasattr(kernel, 'RootDirectory'):
|
|
1393
|
+
base = kernel.RootDirectory
|
|
1394
|
+
except Exception:
|
|
1395
|
+
pass
|
|
1396
|
+
|
|
1397
|
+
# Clean path and join
|
|
1398
|
+
if path:
|
|
1399
|
+
clean_path = path.lstrip('/').lstrip('\\')
|
|
1400
|
+
return os.path.normpath(os.path.join(base, clean_path))
|
|
1401
|
+
|
|
1402
|
+
return base
|
|
1403
|
+
|
|
1404
|
+
def builtin_include(self, filepath: str) -> Any:
|
|
1405
|
+
"""
|
|
1406
|
+
Load and execute a CSSL file, returning its ServiceDefinition
|
|
1407
|
+
Usage: include(cso_root('/services/utils.cssl'))
|
|
1408
|
+
Returns: ServiceDefinition with structs, functions, etc.
|
|
1409
|
+
"""
|
|
1410
|
+
if not self.runtime:
|
|
1411
|
+
raise CSSLBuiltinError("include requires runtime context")
|
|
1412
|
+
|
|
1413
|
+
# Resolve relative paths
|
|
1414
|
+
if not os.path.isabs(filepath):
|
|
1415
|
+
filepath = self.builtin_cso_root(filepath)
|
|
1416
|
+
|
|
1417
|
+
# Check file exists
|
|
1418
|
+
if not os.path.exists(filepath):
|
|
1419
|
+
raise CSSLBuiltinError(f"Include file not found: {filepath}")
|
|
1420
|
+
|
|
1421
|
+
# Check include cache to prevent circular imports
|
|
1422
|
+
if not hasattr(self.runtime, '_include_cache'):
|
|
1423
|
+
self.runtime._include_cache = {}
|
|
1424
|
+
|
|
1425
|
+
if filepath in self.runtime._include_cache:
|
|
1426
|
+
return self.runtime._include_cache[filepath]
|
|
1427
|
+
|
|
1428
|
+
try:
|
|
1429
|
+
# Read and parse the file
|
|
1430
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
1431
|
+
source = f.read()
|
|
1432
|
+
|
|
1433
|
+
from .cssl_parser import parse_cssl
|
|
1434
|
+
|
|
1435
|
+
ast = parse_cssl(source)
|
|
1436
|
+
|
|
1437
|
+
# Execute the service to get definitions
|
|
1438
|
+
service_def = self.runtime._exec_service(ast)
|
|
1439
|
+
|
|
1440
|
+
# Cache the result
|
|
1441
|
+
self.runtime._include_cache[filepath] = service_def
|
|
1442
|
+
|
|
1443
|
+
return service_def
|
|
1444
|
+
|
|
1445
|
+
except Exception as e:
|
|
1446
|
+
raise CSSLBuiltinError(f"Failed to include '{filepath}': {e}")
|
|
1447
|
+
|
|
1448
|
+
def builtin_get(self, source: Any, key: str = None) -> Any:
|
|
1449
|
+
"""
|
|
1450
|
+
Get value from module, ServiceDefinition, or dict
|
|
1451
|
+
Usage:
|
|
1452
|
+
get('list') - Get list module (returns empty list creator)
|
|
1453
|
+
get('os') - Get OS module
|
|
1454
|
+
get('time') - Get Time module
|
|
1455
|
+
get(@Time) - Get standard module
|
|
1456
|
+
get(service_def, 'funcName') - Get function from included service
|
|
1457
|
+
get(dict_obj, 'key') - Get key from dict
|
|
1458
|
+
Returns: The requested value or None
|
|
1459
|
+
"""
|
|
1460
|
+
# Single argument - return module reference directly
|
|
1461
|
+
if key is None:
|
|
1462
|
+
if isinstance(source, str):
|
|
1463
|
+
# NEW: Handle standard module names
|
|
1464
|
+
module_map = {
|
|
1465
|
+
'list': self._get_list_module(),
|
|
1466
|
+
'dict': self._get_dict_module(),
|
|
1467
|
+
'os': self._get_os_module(),
|
|
1468
|
+
'time': self._get_time_module(),
|
|
1469
|
+
'vsramsdk': self._get_vsram_module(),
|
|
1470
|
+
'kernel': self._get_kernel_module(),
|
|
1471
|
+
}
|
|
1472
|
+
if source.lower() in module_map:
|
|
1473
|
+
return module_map[source.lower()]
|
|
1474
|
+
|
|
1475
|
+
# Treat as module path
|
|
1476
|
+
if self.runtime:
|
|
1477
|
+
return self.runtime.get_module(source)
|
|
1478
|
+
return source
|
|
1479
|
+
|
|
1480
|
+
# Two arguments - extract from source
|
|
1481
|
+
if hasattr(source, key):
|
|
1482
|
+
return getattr(source, key)
|
|
1483
|
+
|
|
1484
|
+
if isinstance(source, dict):
|
|
1485
|
+
return source.get(key)
|
|
1486
|
+
|
|
1487
|
+
# Check if source is a ServiceDefinition-like object
|
|
1488
|
+
if hasattr(source, 'structs') and key in source.structs:
|
|
1489
|
+
return source.structs[key]
|
|
1490
|
+
|
|
1491
|
+
if hasattr(source, 'functions') and key in source.functions:
|
|
1492
|
+
return source.functions[key]
|
|
1493
|
+
|
|
1494
|
+
return None
|
|
1495
|
+
|
|
1496
|
+
# NEW: Module factories for get()
|
|
1497
|
+
def _get_list_module(self):
|
|
1498
|
+
"""Return a list module proxy"""
|
|
1499
|
+
class ListModule:
|
|
1500
|
+
@staticmethod
|
|
1501
|
+
def create():
|
|
1502
|
+
return []
|
|
1503
|
+
@staticmethod
|
|
1504
|
+
def add(lst, item):
|
|
1505
|
+
if isinstance(lst, list):
|
|
1506
|
+
lst.append(item)
|
|
1507
|
+
return lst
|
|
1508
|
+
return ListModule()
|
|
1509
|
+
|
|
1510
|
+
def _get_dict_module(self):
|
|
1511
|
+
"""Return a dict module proxy"""
|
|
1512
|
+
class DictModule:
|
|
1513
|
+
@staticmethod
|
|
1514
|
+
def create():
|
|
1515
|
+
return {}
|
|
1516
|
+
return DictModule()
|
|
1517
|
+
|
|
1518
|
+
def _get_os_module(self):
|
|
1519
|
+
"""Return OS module proxy"""
|
|
1520
|
+
class OSModule:
|
|
1521
|
+
@staticmethod
|
|
1522
|
+
def Listdir(path='.'):
|
|
1523
|
+
return os.listdir(path)
|
|
1524
|
+
@staticmethod
|
|
1525
|
+
def ReadFile(path, encoding='utf-8'):
|
|
1526
|
+
with open(path, 'r', encoding=encoding) as f:
|
|
1527
|
+
return f.read()
|
|
1528
|
+
@staticmethod
|
|
1529
|
+
def WriteFile(path, content, encoding='utf-8'):
|
|
1530
|
+
with open(path, 'w', encoding=encoding) as f:
|
|
1531
|
+
return f.write(content)
|
|
1532
|
+
@staticmethod
|
|
1533
|
+
def isLinux():
|
|
1534
|
+
import platform
|
|
1535
|
+
return platform.system() == 'Linux'
|
|
1536
|
+
@staticmethod
|
|
1537
|
+
def isWindows():
|
|
1538
|
+
import platform
|
|
1539
|
+
return platform.system() == 'Windows'
|
|
1540
|
+
@staticmethod
|
|
1541
|
+
def isMac():
|
|
1542
|
+
import platform
|
|
1543
|
+
return platform.system() == 'Darwin'
|
|
1544
|
+
return OSModule()
|
|
1545
|
+
|
|
1546
|
+
def _get_time_module(self):
|
|
1547
|
+
"""Return Time module proxy"""
|
|
1548
|
+
class TimeModule:
|
|
1549
|
+
@staticmethod
|
|
1550
|
+
def CurrentTime(format_str='%Y-%m-%d %H:%M:%S'):
|
|
1551
|
+
return datetime.now().strftime(format_str)
|
|
1552
|
+
@staticmethod
|
|
1553
|
+
def Now():
|
|
1554
|
+
return time.time()
|
|
1555
|
+
@staticmethod
|
|
1556
|
+
def Sleep(seconds):
|
|
1557
|
+
time.sleep(seconds)
|
|
1558
|
+
return TimeModule()
|
|
1559
|
+
|
|
1560
|
+
def _get_vsram_module(self):
|
|
1561
|
+
"""Return VSRAM module proxy from runtime"""
|
|
1562
|
+
if self.runtime and hasattr(self.runtime, '_modules'):
|
|
1563
|
+
return self.runtime._modules.get('VSRam') or self.runtime._modules.get('VSRAM')
|
|
1564
|
+
return None
|
|
1565
|
+
|
|
1566
|
+
def _get_kernel_module(self):
|
|
1567
|
+
"""Return Kernel module proxy from runtime"""
|
|
1568
|
+
if self.runtime and hasattr(self.runtime, '_modules'):
|
|
1569
|
+
return self.runtime._modules.get('Kernel') or self.runtime._modules.get('KernelClient')
|
|
1570
|
+
return None
|
|
1571
|
+
|
|
1572
|
+
# NEW: Platform Detection Functions
|
|
1573
|
+
def builtin_islinux(self) -> bool:
|
|
1574
|
+
"""Check if running on Linux"""
|
|
1575
|
+
import platform
|
|
1576
|
+
return platform.system() == 'Linux'
|
|
1577
|
+
|
|
1578
|
+
def builtin_iswindows(self) -> bool:
|
|
1579
|
+
"""Check if running on Windows"""
|
|
1580
|
+
import platform
|
|
1581
|
+
return platform.system() == 'Windows'
|
|
1582
|
+
|
|
1583
|
+
def builtin_ismac(self) -> bool:
|
|
1584
|
+
"""Check if running on macOS"""
|
|
1585
|
+
import platform
|
|
1586
|
+
return platform.system() == 'Darwin'
|
|
1587
|
+
|
|
1588
|
+
# NEW: CurrentTime function
|
|
1589
|
+
def builtin_currenttime(self, format_str: str = '%Y-%m-%d %H:%M:%S') -> str:
|
|
1590
|
+
"""Get current time as formatted string"""
|
|
1591
|
+
return datetime.now().strftime(format_str)
|
|
1592
|
+
|
|
1593
|
+
# NEW: Global function for scope promotion
|
|
1594
|
+
def builtin_global(self, s_ref: Any) -> None:
|
|
1595
|
+
"""
|
|
1596
|
+
Promote s@<name> to @<name> (make globally accessible)
|
|
1597
|
+
Usage: global(s@cache) - makes @cache available
|
|
1598
|
+
|
|
1599
|
+
This takes the value referenced by s@<name> and registers it
|
|
1600
|
+
as a module reference accessible via @<name>
|
|
1601
|
+
"""
|
|
1602
|
+
if not self.runtime:
|
|
1603
|
+
return
|
|
1604
|
+
|
|
1605
|
+
# The argument could be:
|
|
1606
|
+
# 1. A direct value from s@<name> reference
|
|
1607
|
+
# 2. A string path like "MyStruct.cache"
|
|
1608
|
+
|
|
1609
|
+
if isinstance(s_ref, str):
|
|
1610
|
+
# It's a path - promote it
|
|
1611
|
+
self.runtime.promote_to_global(s_ref)
|
|
1612
|
+
else:
|
|
1613
|
+
# It's a value - we need the original s@<name> reference
|
|
1614
|
+
# This is handled by the runtime which passes the path
|
|
1615
|
+
pass
|
|
1616
|
+
|
|
1617
|
+
# ============= CSSL Data Type Constructors =============
|
|
1618
|
+
|
|
1619
|
+
def builtin_datastruct(self, element_type: str = 'dynamic') -> Any:
|
|
1620
|
+
"""Create a datastruct container.
|
|
1621
|
+
|
|
1622
|
+
Usage: datastruct<string> myData; or datastruct('string')
|
|
1623
|
+
"""
|
|
1624
|
+
from .cssl_types import DataStruct
|
|
1625
|
+
return DataStruct(element_type)
|
|
1626
|
+
|
|
1627
|
+
def builtin_shuffled(self, element_type: str = 'dynamic') -> Any:
|
|
1628
|
+
"""Create a shuffled container for multiple returns.
|
|
1629
|
+
|
|
1630
|
+
Usage: shuffled<string> results;
|
|
1631
|
+
"""
|
|
1632
|
+
from .cssl_types import Shuffled
|
|
1633
|
+
return Shuffled(element_type)
|
|
1634
|
+
|
|
1635
|
+
def builtin_iterator(self, element_type: str = 'int', size: int = 16) -> Any:
|
|
1636
|
+
"""Create an advanced iterator.
|
|
1637
|
+
|
|
1638
|
+
Usage: iterator<int, 16> Map;
|
|
1639
|
+
"""
|
|
1640
|
+
from .cssl_types import Iterator
|
|
1641
|
+
return Iterator(element_type, size)
|
|
1642
|
+
|
|
1643
|
+
def builtin_combo(self, element_type: str = 'dynamic') -> Any:
|
|
1644
|
+
"""Create a combo filter/search space.
|
|
1645
|
+
|
|
1646
|
+
Usage: combo<open&string> myCombo;
|
|
1647
|
+
"""
|
|
1648
|
+
from .cssl_types import Combo
|
|
1649
|
+
return Combo(element_type)
|
|
1650
|
+
|
|
1651
|
+
def builtin_dataspace(self, space_type: str = 'dynamic') -> Any:
|
|
1652
|
+
"""Create a dataspace for SQL/structured data.
|
|
1653
|
+
|
|
1654
|
+
Usage: dataspace<sql::table> table;
|
|
1655
|
+
"""
|
|
1656
|
+
from .cssl_types import DataSpace
|
|
1657
|
+
return DataSpace(space_type)
|
|
1658
|
+
|
|
1659
|
+
def builtin_openquote(self, db_ref: Any = None) -> Any:
|
|
1660
|
+
"""Create an openquote container for SQL operations.
|
|
1661
|
+
|
|
1662
|
+
Usage: openquote<datastruct<dynamic>&@sql::db.oqt(@db)> Queue;
|
|
1663
|
+
"""
|
|
1664
|
+
from .cssl_types import OpenQuote
|
|
1665
|
+
return OpenQuote(db_ref)
|
|
1666
|
+
|
|
1667
|
+
def builtin_openfind(self, combo_or_type: Any, index: int = 0) -> Any:
|
|
1668
|
+
"""Find open parameter by type or combo space.
|
|
1669
|
+
|
|
1670
|
+
Usage: OpenFind<string>(0) or OpenFind(&@comboSpace)
|
|
1671
|
+
"""
|
|
1672
|
+
from .cssl_types import Combo
|
|
1673
|
+
|
|
1674
|
+
if isinstance(combo_or_type, Combo):
|
|
1675
|
+
return combo_or_type.find_match([])
|
|
1676
|
+
return None
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
# Module-level convenience functions
|
|
1680
|
+
_default_builtins: Optional[CSSLBuiltins] = None
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
def get_builtins(runtime=None) -> CSSLBuiltins:
|
|
1684
|
+
"""Get default builtins instance"""
|
|
1685
|
+
global _default_builtins
|
|
1686
|
+
if _default_builtins is None or runtime is not None:
|
|
1687
|
+
_default_builtins = CSSLBuiltins(runtime)
|
|
1688
|
+
return _default_builtins
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
def call_builtin(name: str, *args, **kwargs) -> Any:
|
|
1692
|
+
"""Call a builtin function"""
|
|
1693
|
+
return get_builtins().call(name, *args, **kwargs)
|