@tishlang/tish 1.6.0 → 1.8.0
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.
- package/Cargo.toml +2 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/justfile +8 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
#[cfg(feature = "regex")]
|
|
7
7
|
use std::cell::RefCell;
|
|
8
|
+
use std::fmt;
|
|
8
9
|
#[cfg(feature = "regex")]
|
|
9
10
|
use std::rc::Rc;
|
|
10
|
-
use std::fmt;
|
|
11
11
|
use std::sync::OnceLock;
|
|
12
12
|
use tishlang_builtins::helpers::extract_num;
|
|
13
13
|
#[cfg(feature = "fs")]
|
|
@@ -15,79 +15,77 @@ use tishlang_builtins::helpers::make_error_value;
|
|
|
15
15
|
|
|
16
16
|
pub use tishlang_core::ObjectMap;
|
|
17
17
|
pub use tishlang_core::Value;
|
|
18
|
+
// Re-export the shared-mutable wrapper so the Rust code emitted by
|
|
19
|
+
// `tishlang_compile::codegen` can write `VmRef::new(...)` without needing
|
|
20
|
+
// a direct dependency on `tishlang_core` from the generated crate.
|
|
21
|
+
pub use tishlang_core::{VmReadGuard, VmRef, VmWriteGuard};
|
|
18
22
|
|
|
19
23
|
pub use tishlang_builtins::construct::{
|
|
20
|
-
audio_context_constructor_value as tish_audio_context_constructor,
|
|
21
|
-
construct as tish_construct,
|
|
24
|
+
audio_context_constructor_value as tish_audio_context_constructor, construct as tish_construct,
|
|
22
25
|
uint8_array_constructor_value as tish_uint8_array_constructor,
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
// Re-export array methods from tishlang_builtins
|
|
26
29
|
pub use tishlang_builtins::array::{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
reverse as array_reverse,
|
|
35
|
-
shuffle as array_shuffle,
|
|
36
|
-
splice as array_splice_impl,
|
|
37
|
-
slice as array_slice_impl,
|
|
38
|
-
concat as array_concat_impl,
|
|
39
|
-
flat as array_flat_impl,
|
|
40
|
-
map as array_map,
|
|
41
|
-
filter as array_filter,
|
|
42
|
-
reduce as array_reduce,
|
|
43
|
-
for_each as array_for_each,
|
|
44
|
-
find as array_find,
|
|
45
|
-
find_index as array_find_index,
|
|
46
|
-
some as array_some,
|
|
47
|
-
every as array_every,
|
|
48
|
-
flat_map as array_flat_map,
|
|
49
|
-
sort_default as array_sort_default,
|
|
50
|
-
sort_with_comparator as array_sort_with_comparator,
|
|
51
|
-
sort_numeric_asc as array_sort_numeric_asc,
|
|
30
|
+
concat as array_concat_impl, every as array_every, filter as array_filter, find as array_find,
|
|
31
|
+
find_index as array_find_index, flat as array_flat_impl, flat_map as array_flat_map,
|
|
32
|
+
for_each as array_for_each, includes as array_includes_impl, index_of as array_index_of_impl,
|
|
33
|
+
join as array_join_impl, map as array_map, pop as array_pop, push as array_push_impl,
|
|
34
|
+
reduce as array_reduce, reverse as array_reverse, shift as array_shift,
|
|
35
|
+
shuffle as array_shuffle, slice as array_slice_impl, some as array_some,
|
|
36
|
+
sort_default as array_sort_default, sort_numeric_asc as array_sort_numeric_asc,
|
|
52
37
|
sort_numeric_desc as array_sort_numeric_desc,
|
|
38
|
+
sort_with_comparator as array_sort_with_comparator, splice as array_splice_impl,
|
|
39
|
+
unshift as array_unshift_impl,
|
|
53
40
|
};
|
|
54
41
|
|
|
55
42
|
// Re-export string methods from tishlang_builtins
|
|
56
43
|
pub use tishlang_builtins::string::{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
char_at as string_char_at_impl, char_code_at as string_char_code_at_impl,
|
|
45
|
+
ends_with as string_ends_with_impl, escape_html as string_escape_html_impl,
|
|
46
|
+
includes as string_includes_impl, index_of as string_index_of_impl,
|
|
47
|
+
last_index_of as string_last_index_of_impl, pad_end as string_pad_end_impl,
|
|
48
|
+
pad_start as string_pad_start_impl, repeat as string_repeat_impl,
|
|
49
|
+
replace as string_replace_impl, replace_all as string_replace_all_impl,
|
|
50
|
+
slice as string_slice_impl, split as string_split_impl,
|
|
51
|
+
starts_with as string_starts_with_impl, substring as string_substring_impl,
|
|
52
|
+
to_lower_case as string_to_lower_case, to_upper_case as string_to_upper_case,
|
|
62
53
|
trim as string_trim,
|
|
63
|
-
to_upper_case as string_to_upper_case,
|
|
64
|
-
to_lower_case as string_to_lower_case,
|
|
65
|
-
starts_with as string_starts_with_impl,
|
|
66
|
-
ends_with as string_ends_with_impl,
|
|
67
|
-
replace as string_replace_impl,
|
|
68
|
-
replace_all as string_replace_all_impl,
|
|
69
|
-
char_at as string_char_at_impl,
|
|
70
|
-
char_code_at as string_char_code_at_impl,
|
|
71
|
-
repeat as string_repeat_impl,
|
|
72
|
-
pad_start as string_pad_start_impl,
|
|
73
|
-
pad_end as string_pad_end_impl,
|
|
74
|
-
last_index_of as string_last_index_of_impl,
|
|
75
54
|
};
|
|
76
55
|
|
|
77
56
|
// Wrapper functions to maintain API compatibility
|
|
78
|
-
pub fn array_push(arr: &Value, args: &[Value]) -> Value {
|
|
79
|
-
|
|
80
|
-
|
|
57
|
+
pub fn array_push(arr: &Value, args: &[Value]) -> Value {
|
|
58
|
+
array_push_impl(arr, args)
|
|
59
|
+
}
|
|
60
|
+
pub fn array_unshift(arr: &Value, args: &[Value]) -> Value {
|
|
61
|
+
array_unshift_impl(arr, args)
|
|
62
|
+
}
|
|
63
|
+
pub fn array_index_of(arr: &Value, search: &Value) -> Value {
|
|
64
|
+
array_index_of_impl(arr, search)
|
|
65
|
+
}
|
|
81
66
|
pub fn array_includes(arr: &Value, search: &Value, from: &Value) -> Value {
|
|
82
67
|
array_includes_impl(arr, search, Some(from))
|
|
83
68
|
}
|
|
84
|
-
pub fn array_join(arr: &Value, sep: &Value) -> Value {
|
|
85
|
-
|
|
69
|
+
pub fn array_join(arr: &Value, sep: &Value) -> Value {
|
|
70
|
+
array_join_impl(arr, sep)
|
|
71
|
+
}
|
|
72
|
+
pub fn array_splice(
|
|
73
|
+
arr: &Value,
|
|
74
|
+
start: &Value,
|
|
75
|
+
delete_count: Option<&Value>,
|
|
76
|
+
items: &[Value],
|
|
77
|
+
) -> Value {
|
|
86
78
|
array_splice_impl(arr, start, delete_count, items)
|
|
87
79
|
}
|
|
88
|
-
pub fn array_slice(arr: &Value, start: &Value, end: &Value) -> Value {
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
pub fn array_slice(arr: &Value, start: &Value, end: &Value) -> Value {
|
|
81
|
+
array_slice_impl(arr, start, end)
|
|
82
|
+
}
|
|
83
|
+
pub fn array_concat(arr: &Value, args: &[Value]) -> Value {
|
|
84
|
+
array_concat_impl(arr, args)
|
|
85
|
+
}
|
|
86
|
+
pub fn array_flat(arr: &Value, depth: &Value) -> Value {
|
|
87
|
+
array_flat_impl(arr, depth)
|
|
88
|
+
}
|
|
91
89
|
|
|
92
90
|
pub fn array_sort(arr: &Value, comparator: Option<&Value>) -> Value {
|
|
93
91
|
match comparator {
|
|
@@ -102,11 +100,21 @@ pub fn string_index_of(s: &Value, search: &Value, from: &Value) -> Value {
|
|
|
102
100
|
pub fn string_includes(s: &Value, search: &Value, from: &Value) -> Value {
|
|
103
101
|
string_includes_impl(s, search, Some(from))
|
|
104
102
|
}
|
|
105
|
-
pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
pub fn
|
|
109
|
-
|
|
103
|
+
pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value {
|
|
104
|
+
string_slice_impl(s, start, end)
|
|
105
|
+
}
|
|
106
|
+
pub fn string_substring(s: &Value, start: &Value, end: &Value) -> Value {
|
|
107
|
+
string_substring_impl(s, start, end)
|
|
108
|
+
}
|
|
109
|
+
pub fn string_split(s: &Value, sep: &Value) -> Value {
|
|
110
|
+
string_split_impl(s, sep)
|
|
111
|
+
}
|
|
112
|
+
pub fn string_starts_with(s: &Value, search: &Value) -> Value {
|
|
113
|
+
string_starts_with_impl(s, search)
|
|
114
|
+
}
|
|
115
|
+
pub fn string_ends_with(s: &Value, search: &Value) -> Value {
|
|
116
|
+
string_ends_with_impl(s, search)
|
|
117
|
+
}
|
|
110
118
|
pub fn string_replace(s: &Value, search: &Value, replacement: &Value) -> Value {
|
|
111
119
|
#[cfg(feature = "regex")]
|
|
112
120
|
if matches!(search, Value::RegExp(_)) {
|
|
@@ -114,12 +122,24 @@ pub fn string_replace(s: &Value, search: &Value, replacement: &Value) -> Value {
|
|
|
114
122
|
}
|
|
115
123
|
string_replace_impl(s, search, replacement)
|
|
116
124
|
}
|
|
117
|
-
pub fn string_replace_all(s: &Value, search: &Value, replacement: &Value) -> Value {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
pub fn
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
pub fn string_replace_all(s: &Value, search: &Value, replacement: &Value) -> Value {
|
|
126
|
+
string_replace_all_impl(s, search, replacement)
|
|
127
|
+
}
|
|
128
|
+
pub fn string_char_at(s: &Value, idx: &Value) -> Value {
|
|
129
|
+
string_char_at_impl(s, idx)
|
|
130
|
+
}
|
|
131
|
+
pub fn string_char_code_at(s: &Value, idx: &Value) -> Value {
|
|
132
|
+
string_char_code_at_impl(s, idx)
|
|
133
|
+
}
|
|
134
|
+
pub fn string_repeat(s: &Value, count: &Value) -> Value {
|
|
135
|
+
string_repeat_impl(s, count)
|
|
136
|
+
}
|
|
137
|
+
pub fn string_pad_start(s: &Value, target_len: &Value, pad: &Value) -> Value {
|
|
138
|
+
string_pad_start_impl(s, target_len, pad)
|
|
139
|
+
}
|
|
140
|
+
pub fn string_pad_end(s: &Value, target_len: &Value, pad: &Value) -> Value {
|
|
141
|
+
string_pad_end_impl(s, target_len, pad)
|
|
142
|
+
}
|
|
123
143
|
pub fn string_last_index_of(s: &Value, search: &Value, position: &Value) -> Value {
|
|
124
144
|
string_last_index_of_impl(s, search, position)
|
|
125
145
|
}
|
|
@@ -244,23 +264,59 @@ pub mod ops {
|
|
|
244
264
|
}
|
|
245
265
|
|
|
246
266
|
use tishlang_builtins::globals::{
|
|
247
|
-
array_is_array as builtins_array_is_array,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
is_nan as builtins_is_nan,
|
|
253
|
-
object_assign as builtins_object_assign,
|
|
254
|
-
object_entries as builtins_object_entries,
|
|
255
|
-
object_from_entries as builtins_object_from_entries,
|
|
256
|
-
object_keys as builtins_object_keys,
|
|
267
|
+
array_is_array as builtins_array_is_array, boolean as builtins_boolean,
|
|
268
|
+
decode_uri as builtins_decode_uri, encode_uri as builtins_encode_uri,
|
|
269
|
+
is_finite as builtins_is_finite, is_nan as builtins_is_nan,
|
|
270
|
+
object_assign as builtins_object_assign, object_entries as builtins_object_entries,
|
|
271
|
+
object_from_entries as builtins_object_from_entries, object_keys as builtins_object_keys,
|
|
257
272
|
object_values as builtins_object_values,
|
|
258
273
|
string_from_char_code as builtins_string_from_char_code,
|
|
259
274
|
};
|
|
260
|
-
use tishlang_core::{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
275
|
+
use tishlang_core::{json_parse as core_json_parse, json_stringify as core_json_stringify};
|
|
276
|
+
|
|
277
|
+
/// Public JSON helpers used by codegen-emitted code (specifically the
|
|
278
|
+
/// `_tish_write_json` impls on user-declared `type` aliases). Re-exporting
|
|
279
|
+
/// from the runtime keeps the generated source decoupled from
|
|
280
|
+
/// `tishlang_core` — generated code only ever names `tishlang_runtime`.
|
|
281
|
+
pub mod json {
|
|
282
|
+
pub use tishlang_core::json_stringify_into as stringify_into;
|
|
283
|
+
/// Append the JSON-escaped contents of `s` (without surrounding
|
|
284
|
+
/// quotes) to `buf`. Used by typed-struct serialisers for `String`
|
|
285
|
+
/// fields. Falls through to `tishlang_core::json_stringify_into`'s
|
|
286
|
+
/// internal helper via a `Value::String` round-trip when the inner
|
|
287
|
+
/// helper isn't directly exposed.
|
|
288
|
+
pub fn escape_into(buf: &mut String, s: &str) {
|
|
289
|
+
// Inline the same escape rules as tishlang_core::json::
|
|
290
|
+
// `escape_json_string_into`. Kept locally so we don't widen
|
|
291
|
+
// tishlang_core's public surface unnecessarily.
|
|
292
|
+
let bytes = s.as_bytes();
|
|
293
|
+
let mut start = 0usize;
|
|
294
|
+
for (i, &b) in bytes.iter().enumerate() {
|
|
295
|
+
if b < 0x20 || b == b'"' || b == b'\\' {
|
|
296
|
+
if start < i {
|
|
297
|
+
buf.push_str(&s[start..i]);
|
|
298
|
+
}
|
|
299
|
+
match b {
|
|
300
|
+
b'"' => buf.push_str("\\\""),
|
|
301
|
+
b'\\' => buf.push_str("\\\\"),
|
|
302
|
+
b'\n' => buf.push_str("\\n"),
|
|
303
|
+
b'\r' => buf.push_str("\\r"),
|
|
304
|
+
b'\t' => buf.push_str("\\t"),
|
|
305
|
+
b'\x08' => buf.push_str("\\b"),
|
|
306
|
+
b'\x0c' => buf.push_str("\\f"),
|
|
307
|
+
_ => {
|
|
308
|
+
use std::fmt::Write;
|
|
309
|
+
let _ = write!(buf, "\\u{:04x}", b as u32);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
start = i + 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if start < bytes.len() {
|
|
316
|
+
buf.push_str(&s[start..]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
264
320
|
|
|
265
321
|
/// Error type for Tish throw/catch.
|
|
266
322
|
#[derive(Debug, Clone)]
|
|
@@ -290,14 +346,12 @@ enum LogLevel {
|
|
|
290
346
|
static LOG_LEVEL: OnceLock<LogLevel> = OnceLock::new();
|
|
291
347
|
|
|
292
348
|
fn get_log_level() -> LogLevel {
|
|
293
|
-
*LOG_LEVEL.get_or_init(|| {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
_ => LogLevel::Log,
|
|
300
|
-
}
|
|
349
|
+
*LOG_LEVEL.get_or_init(|| match std::env::var("TISH_LOG_LEVEL").as_deref() {
|
|
350
|
+
Ok("debug") => LogLevel::Debug,
|
|
351
|
+
Ok("info") => LogLevel::Info,
|
|
352
|
+
Ok("warn") => LogLevel::Warn,
|
|
353
|
+
Ok("error") => LogLevel::Error,
|
|
354
|
+
_ => LogLevel::Log,
|
|
301
355
|
})
|
|
302
356
|
}
|
|
303
357
|
|
|
@@ -363,39 +417,59 @@ pub fn encode_uri(args: &[Value]) -> Value {
|
|
|
363
417
|
|
|
364
418
|
// Math functions - use tishlang_builtins::math
|
|
365
419
|
pub use tishlang_builtins::math::{
|
|
366
|
-
abs as tish_math_abs_impl,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
sin as tish_math_sin_impl,
|
|
372
|
-
cos as tish_math_cos_impl,
|
|
373
|
-
tan as tish_math_tan_impl,
|
|
374
|
-
exp as tish_math_exp_impl,
|
|
375
|
-
trunc as tish_math_trunc_impl,
|
|
376
|
-
min as tish_math_min_impl,
|
|
377
|
-
max as tish_math_max_impl,
|
|
378
|
-
pow as tish_math_pow_impl,
|
|
379
|
-
sign as tish_math_sign_impl,
|
|
380
|
-
random as tish_math_random_impl,
|
|
420
|
+
abs as tish_math_abs_impl, ceil as tish_math_ceil_impl, cos as tish_math_cos_impl,
|
|
421
|
+
exp as tish_math_exp_impl, floor as tish_math_floor_impl, max as tish_math_max_impl,
|
|
422
|
+
min as tish_math_min_impl, pow as tish_math_pow_impl, random as tish_math_random_impl,
|
|
423
|
+
round as tish_math_round_impl, sign as tish_math_sign_impl, sin as tish_math_sin_impl,
|
|
424
|
+
sqrt as tish_math_sqrt_impl, tan as tish_math_tan_impl, trunc as tish_math_trunc_impl,
|
|
381
425
|
};
|
|
382
426
|
|
|
383
427
|
// Wrapper functions to maintain API (existing callers use math_* naming)
|
|
384
|
-
pub fn math_abs(args: &[Value]) -> Value {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
pub fn
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
pub fn
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
pub fn
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
pub fn
|
|
397
|
-
|
|
398
|
-
|
|
428
|
+
pub fn math_abs(args: &[Value]) -> Value {
|
|
429
|
+
tish_math_abs_impl(args)
|
|
430
|
+
}
|
|
431
|
+
pub fn math_sqrt(args: &[Value]) -> Value {
|
|
432
|
+
tish_math_sqrt_impl(args)
|
|
433
|
+
}
|
|
434
|
+
pub fn math_floor(args: &[Value]) -> Value {
|
|
435
|
+
tish_math_floor_impl(args)
|
|
436
|
+
}
|
|
437
|
+
pub fn math_ceil(args: &[Value]) -> Value {
|
|
438
|
+
tish_math_ceil_impl(args)
|
|
439
|
+
}
|
|
440
|
+
pub fn math_round(args: &[Value]) -> Value {
|
|
441
|
+
tish_math_round_impl(args)
|
|
442
|
+
}
|
|
443
|
+
pub fn math_min(args: &[Value]) -> Value {
|
|
444
|
+
tish_math_min_impl(args)
|
|
445
|
+
}
|
|
446
|
+
pub fn math_max(args: &[Value]) -> Value {
|
|
447
|
+
tish_math_max_impl(args)
|
|
448
|
+
}
|
|
449
|
+
pub fn math_sin(args: &[Value]) -> Value {
|
|
450
|
+
tish_math_sin_impl(args)
|
|
451
|
+
}
|
|
452
|
+
pub fn math_cos(args: &[Value]) -> Value {
|
|
453
|
+
tish_math_cos_impl(args)
|
|
454
|
+
}
|
|
455
|
+
pub fn math_tan(args: &[Value]) -> Value {
|
|
456
|
+
tish_math_tan_impl(args)
|
|
457
|
+
}
|
|
458
|
+
pub fn math_exp(args: &[Value]) -> Value {
|
|
459
|
+
tish_math_exp_impl(args)
|
|
460
|
+
}
|
|
461
|
+
pub fn math_trunc(args: &[Value]) -> Value {
|
|
462
|
+
tish_math_trunc_impl(args)
|
|
463
|
+
}
|
|
464
|
+
pub fn math_pow(args: &[Value]) -> Value {
|
|
465
|
+
tish_math_pow_impl(args)
|
|
466
|
+
}
|
|
467
|
+
pub fn math_sign(args: &[Value]) -> Value {
|
|
468
|
+
tish_math_sign_impl(args)
|
|
469
|
+
}
|
|
470
|
+
pub fn math_random(args: &[Value]) -> Value {
|
|
471
|
+
tish_math_random_impl(args)
|
|
472
|
+
}
|
|
399
473
|
|
|
400
474
|
pub fn math_log(args: &[Value]) -> Value {
|
|
401
475
|
let n = extract_num(args.first()).unwrap_or(f64::NAN);
|
|
@@ -408,7 +482,10 @@ pub fn json_stringify(args: &[Value]) -> Value {
|
|
|
408
482
|
}
|
|
409
483
|
|
|
410
484
|
pub fn json_parse(args: &[Value]) -> Value {
|
|
411
|
-
let s = args
|
|
485
|
+
let s = args
|
|
486
|
+
.first()
|
|
487
|
+
.map(|v| v.to_display_string())
|
|
488
|
+
.unwrap_or_default();
|
|
412
489
|
core_json_parse(&s).unwrap_or(Value::Null)
|
|
413
490
|
}
|
|
414
491
|
|
|
@@ -431,10 +508,13 @@ pub fn string_from_char_code(args: &[Value]) -> Value {
|
|
|
431
508
|
|
|
432
509
|
#[cfg(feature = "process")]
|
|
433
510
|
pub fn process_exit(args: &[Value]) -> Value {
|
|
434
|
-
let code = args
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
511
|
+
let code = args
|
|
512
|
+
.first()
|
|
513
|
+
.and_then(|v| match v {
|
|
514
|
+
Value::Number(n) => Some(*n as i32),
|
|
515
|
+
_ => None,
|
|
516
|
+
})
|
|
517
|
+
.unwrap_or(0);
|
|
438
518
|
std::process::exit(code);
|
|
439
519
|
}
|
|
440
520
|
|
|
@@ -449,7 +529,10 @@ pub fn process_cwd(_args: &[Value]) -> Value {
|
|
|
449
529
|
#[cfg(feature = "process")]
|
|
450
530
|
pub fn process_exec(args: &[Value]) -> Value {
|
|
451
531
|
use std::process::Command;
|
|
452
|
-
let cmd = args
|
|
532
|
+
let cmd = args
|
|
533
|
+
.first()
|
|
534
|
+
.map(|v| v.to_display_string())
|
|
535
|
+
.unwrap_or_default();
|
|
453
536
|
if cmd.is_empty() {
|
|
454
537
|
return Value::Number(0.0);
|
|
455
538
|
}
|
|
@@ -461,7 +544,10 @@ pub fn process_exec(args: &[Value]) -> Value {
|
|
|
461
544
|
|
|
462
545
|
#[cfg(feature = "fs")]
|
|
463
546
|
pub fn read_file(args: &[Value]) -> Value {
|
|
464
|
-
let path = args
|
|
547
|
+
let path = args
|
|
548
|
+
.first()
|
|
549
|
+
.map(|v| v.to_display_string())
|
|
550
|
+
.unwrap_or_default();
|
|
465
551
|
match std::fs::read_to_string(&path) {
|
|
466
552
|
Ok(content) => Value::String(content.into()),
|
|
467
553
|
Err(e) => make_error_value(e),
|
|
@@ -470,8 +556,14 @@ pub fn read_file(args: &[Value]) -> Value {
|
|
|
470
556
|
|
|
471
557
|
#[cfg(feature = "fs")]
|
|
472
558
|
pub fn write_file(args: &[Value]) -> Value {
|
|
473
|
-
let path = args
|
|
474
|
-
|
|
559
|
+
let path = args
|
|
560
|
+
.first()
|
|
561
|
+
.map(|v| v.to_display_string())
|
|
562
|
+
.unwrap_or_default();
|
|
563
|
+
let content = args
|
|
564
|
+
.get(1)
|
|
565
|
+
.map(|v| v.to_display_string())
|
|
566
|
+
.unwrap_or_default();
|
|
475
567
|
match std::fs::write(&path, &content) {
|
|
476
568
|
Ok(()) => Value::Bool(true),
|
|
477
569
|
Err(e) => make_error_value(e),
|
|
@@ -480,13 +572,19 @@ pub fn write_file(args: &[Value]) -> Value {
|
|
|
480
572
|
|
|
481
573
|
#[cfg(feature = "fs")]
|
|
482
574
|
pub fn file_exists(args: &[Value]) -> Value {
|
|
483
|
-
let path = args
|
|
575
|
+
let path = args
|
|
576
|
+
.first()
|
|
577
|
+
.map(|v| v.to_display_string())
|
|
578
|
+
.unwrap_or_default();
|
|
484
579
|
Value::Bool(std::path::Path::new(&path).exists())
|
|
485
580
|
}
|
|
486
581
|
|
|
487
582
|
#[cfg(feature = "fs")]
|
|
488
583
|
pub fn is_dir(args: &[Value]) -> Value {
|
|
489
|
-
let path = args
|
|
584
|
+
let path = args
|
|
585
|
+
.first()
|
|
586
|
+
.map(|v| v.to_display_string())
|
|
587
|
+
.unwrap_or_default();
|
|
490
588
|
Value::Bool(std::path::Path::new(&path).is_dir())
|
|
491
589
|
}
|
|
492
590
|
|
|
@@ -494,14 +592,17 @@ pub fn is_dir(args: &[Value]) -> Value {
|
|
|
494
592
|
pub fn read_dir(args: &[Value]) -> Value {
|
|
495
593
|
use std::cell::RefCell;
|
|
496
594
|
use std::rc::Rc;
|
|
497
|
-
let path = args
|
|
595
|
+
let path = args
|
|
596
|
+
.first()
|
|
597
|
+
.map(|v| v.to_display_string())
|
|
598
|
+
.unwrap_or_else(|| ".".to_string());
|
|
498
599
|
match std::fs::read_dir(&path) {
|
|
499
600
|
Ok(entries) => {
|
|
500
601
|
let files: Vec<Value> = entries
|
|
501
602
|
.filter_map(|e| e.ok())
|
|
502
603
|
.map(|e| Value::String(e.file_name().to_string_lossy().into()))
|
|
503
604
|
.collect();
|
|
504
|
-
Value::Array(
|
|
605
|
+
Value::Array(VmRef::new(files))
|
|
505
606
|
}
|
|
506
607
|
Err(e) => make_error_value(e),
|
|
507
608
|
}
|
|
@@ -509,7 +610,10 @@ pub fn read_dir(args: &[Value]) -> Value {
|
|
|
509
610
|
|
|
510
611
|
#[cfg(feature = "fs")]
|
|
511
612
|
pub fn mkdir(args: &[Value]) -> Value {
|
|
512
|
-
let path = args
|
|
613
|
+
let path = args
|
|
614
|
+
.first()
|
|
615
|
+
.map(|v| v.to_display_string())
|
|
616
|
+
.unwrap_or_default();
|
|
513
617
|
match std::fs::create_dir_all(&path) {
|
|
514
618
|
Ok(()) => Value::Bool(true),
|
|
515
619
|
Err(e) => make_error_value(e),
|
|
@@ -523,8 +627,12 @@ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
|
523
627
|
let key = key.as_ref();
|
|
524
628
|
match obj {
|
|
525
629
|
Value::Object(map) => {
|
|
526
|
-
|
|
527
|
-
|
|
630
|
+
// The map's key type is `Arc<str>`, which implements
|
|
631
|
+
// `Borrow<str>` — so we can look up with a borrowed `&str`
|
|
632
|
+
// directly. Previously we allocated a fresh `Arc<str>` on
|
|
633
|
+
// every call (one heap alloc per `r.field` read in tight
|
|
634
|
+
// handler loops); this version is alloc-free on the hit path.
|
|
635
|
+
map.borrow().get(key).cloned().unwrap_or(Value::Null)
|
|
528
636
|
}
|
|
529
637
|
Value::Array(arr) => {
|
|
530
638
|
if key == "length" {
|
|
@@ -544,17 +652,17 @@ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
|
|
|
544
652
|
}
|
|
545
653
|
#[cfg(feature = "regex")]
|
|
546
654
|
Value::RegExp(re) => {
|
|
547
|
-
let re =
|
|
655
|
+
let re = re.clone();
|
|
548
656
|
if key == "exec" {
|
|
549
|
-
Value::
|
|
657
|
+
Value::native(move |args: &[Value]| {
|
|
550
658
|
let input = args.first().unwrap_or(&Value::Null);
|
|
551
|
-
regexp_exec(&Value::RegExp(
|
|
552
|
-
})
|
|
659
|
+
regexp_exec(&Value::RegExp(re.clone()), input)
|
|
660
|
+
})
|
|
553
661
|
} else if key == "test" {
|
|
554
|
-
Value::
|
|
662
|
+
Value::native(move |args: &[Value]| {
|
|
555
663
|
let input = args.first().unwrap_or(&Value::Null);
|
|
556
|
-
regexp_test(&Value::RegExp(
|
|
557
|
-
})
|
|
664
|
+
regexp_test(&Value::RegExp(re.clone()), input)
|
|
665
|
+
})
|
|
558
666
|
} else {
|
|
559
667
|
Value::Null
|
|
560
668
|
}
|
|
@@ -593,7 +701,15 @@ pub fn get_index(obj: &Value, index: &Value) -> Value {
|
|
|
593
701
|
pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
|
|
594
702
|
match obj {
|
|
595
703
|
Value::Object(map) => {
|
|
596
|
-
|
|
704
|
+
// Try the in-place update path first: if the key already
|
|
705
|
+
// exists we re-use the existing `Arc<str>` and skip the
|
|
706
|
+
// alloc. Only newly-inserted keys pay for `Arc::from(key)`.
|
|
707
|
+
let mut m = map.borrow_mut();
|
|
708
|
+
if let Some(slot) = m.get_mut(key) {
|
|
709
|
+
*slot = val.clone();
|
|
710
|
+
} else {
|
|
711
|
+
m.insert(Arc::from(key), val.clone());
|
|
712
|
+
}
|
|
597
713
|
val
|
|
598
714
|
}
|
|
599
715
|
_ => panic!("Cannot assign property on non-object"),
|
|
@@ -634,7 +750,7 @@ pub fn in_operator(key: &Value, obj: &Value) -> Value {
|
|
|
634
750
|
Value::Number(n) => n.to_string().into(),
|
|
635
751
|
_ => return Value::Bool(false),
|
|
636
752
|
};
|
|
637
|
-
|
|
753
|
+
|
|
638
754
|
let result = match obj {
|
|
639
755
|
Value::Object(map) => map.borrow().contains_key(&key_str),
|
|
640
756
|
Value::Array(arr) => {
|
|
@@ -647,7 +763,7 @@ pub fn in_operator(key: &Value, obj: &Value) -> Value {
|
|
|
647
763
|
}
|
|
648
764
|
_ => false,
|
|
649
765
|
};
|
|
650
|
-
|
|
766
|
+
|
|
651
767
|
Value::Bool(result)
|
|
652
768
|
}
|
|
653
769
|
|
|
@@ -679,10 +795,18 @@ mod promise_io;
|
|
|
679
795
|
#[cfg(feature = "http")]
|
|
680
796
|
mod http;
|
|
681
797
|
|
|
798
|
+
#[cfg(feature = "http")]
|
|
799
|
+
mod http_prefork;
|
|
800
|
+
|
|
801
|
+
#[cfg(feature = "http-io-uring")]
|
|
802
|
+
mod http_io_uring;
|
|
803
|
+
|
|
804
|
+
#[cfg(feature = "http-hyper")]
|
|
805
|
+
mod http_hyper;
|
|
806
|
+
|
|
682
807
|
#[cfg(feature = "http")]
|
|
683
808
|
mod http_fetch;
|
|
684
809
|
|
|
685
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
686
810
|
mod timers;
|
|
687
811
|
|
|
688
812
|
#[cfg(feature = "http")]
|
|
@@ -696,29 +820,88 @@ mod ws;
|
|
|
696
820
|
|
|
697
821
|
#[cfg(feature = "ws")]
|
|
698
822
|
pub use ws::{
|
|
699
|
-
web_socket_client, web_socket_server_accept, web_socket_server_construct,
|
|
700
|
-
ws_broadcast_native, ws_send_native,
|
|
823
|
+
web_socket_client, web_socket_server_accept, web_socket_server_construct,
|
|
824
|
+
web_socket_server_listen, ws_broadcast_native, ws_send_native,
|
|
701
825
|
};
|
|
702
826
|
|
|
703
827
|
#[cfg(feature = "http")]
|
|
704
828
|
pub use http::{
|
|
705
|
-
await_fetch as http_await_fetch,
|
|
706
|
-
|
|
707
|
-
serve as http_serve,
|
|
829
|
+
await_fetch as http_await_fetch, await_fetch_all as http_await_fetch_all,
|
|
830
|
+
register_static_route,
|
|
708
831
|
};
|
|
709
832
|
|
|
710
|
-
|
|
711
|
-
|
|
833
|
+
// `serve` is the user-facing entry point for Tish's HTTP server. By default
|
|
834
|
+
// it uses the tiny_http + SO_REUSEPORT path in `http.rs`. When compiled with
|
|
835
|
+
// `--features http-hyper` and the `TISH_HTTP_BACKEND=hyper` env var is set
|
|
836
|
+
// at runtime, it dispatches to the hyper backend in `http_hyper.rs`.
|
|
837
|
+
//
|
|
838
|
+
// The env-var switch (rather than a cargo feature switch) means one built
|
|
839
|
+
// binary can toggle backends for A/B benchmarking and production rollout
|
|
840
|
+
// without rebuilding. When `http-hyper` is not compiled in, the switch is a
|
|
841
|
+
// no-op and the tiny_http path is used unconditionally.
|
|
842
|
+
#[cfg(feature = "http")]
|
|
843
|
+
pub fn http_serve<F>(args: &[tishlang_core::Value], handler: F) -> tishlang_core::Value
|
|
844
|
+
where
|
|
845
|
+
F: Fn(&[tishlang_core::Value]) -> tishlang_core::Value + Send + Sync + 'static,
|
|
846
|
+
{
|
|
847
|
+
#[cfg(feature = "http-hyper")]
|
|
848
|
+
{
|
|
849
|
+
if http_hyper::is_enabled_via_env() {
|
|
850
|
+
return http_hyper::serve(args, handler);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
http::serve(args, handler)
|
|
854
|
+
}
|
|
712
855
|
|
|
856
|
+
/// `serve(port, { onWorker: (workerId) => handler })` — the object form of
|
|
857
|
+
/// `serve`. Picks up `onWorker`, invokes it once per HTTP accept thread to
|
|
858
|
+
/// build that thread's handler, then enters the normal parallel accept
|
|
859
|
+
/// loop. See [`http::serve_per_worker`] for the full doc.
|
|
860
|
+
///
|
|
861
|
+
/// This is broadly useful for any Tish app that wants per-worker state —
|
|
862
|
+
/// DB connection pools, in-process caches, counters, etc. — without a
|
|
863
|
+
/// global `RwLock` or forcing everything through the single-thread
|
|
864
|
+
/// dispatcher. It also plays nicely with prefork: `onWorker` sees global
|
|
865
|
+
/// worker ids across processes so logs and sharded state are easy to key.
|
|
713
866
|
#[cfg(feature = "http")]
|
|
714
|
-
pub
|
|
867
|
+
pub fn http_serve_per_worker(
|
|
868
|
+
args: &[tishlang_core::Value],
|
|
869
|
+
factory_value: tishlang_core::Value,
|
|
870
|
+
) -> tishlang_core::Value {
|
|
871
|
+
use tishlang_core::Value;
|
|
872
|
+
// factory_value should be Value::Function (passed down by codegen after
|
|
873
|
+
// extracting `onWorker` from the options object).
|
|
874
|
+
let Value::Function(factory) = factory_value else {
|
|
875
|
+
eprintln!("[tish http] serve: onWorker must be a function (id) => handler");
|
|
876
|
+
return Value::Null;
|
|
877
|
+
};
|
|
878
|
+
let factory: tishlang_core::NativeFn = factory;
|
|
879
|
+
http::serve_per_worker(args, move |worker_id| {
|
|
880
|
+
let handler_val = factory(&[Value::Number(worker_id as f64)]);
|
|
881
|
+
match handler_val {
|
|
882
|
+
Value::Function(f) => f,
|
|
883
|
+
_ => panic!(
|
|
884
|
+
"onWorker returned {:?} for worker {}; must return a function",
|
|
885
|
+
handler_val, worker_id
|
|
886
|
+
),
|
|
887
|
+
}
|
|
888
|
+
})
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
pub use timers::{
|
|
892
|
+
clear_interval as timer_clear_interval, clear_timeout as timer_clear_timeout, drain_timers,
|
|
893
|
+
set_interval as timer_set_interval, set_timeout as timer_set_timeout,
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
#[cfg(feature = "http")]
|
|
897
|
+
pub use promise::{promise_instance_catch, promise_instance_then, promise_object};
|
|
715
898
|
|
|
716
899
|
#[cfg(feature = "http")]
|
|
717
900
|
pub use native_promise::{await_promise, fetch_all_promise, fetch_async_promise, fetch_promise};
|
|
718
901
|
|
|
719
902
|
// RegExp Support
|
|
720
903
|
#[cfg(feature = "regex")]
|
|
721
|
-
pub use tishlang_core::{
|
|
904
|
+
pub use tishlang_core::{RegExpFlags, TishRegExp};
|
|
722
905
|
|
|
723
906
|
#[cfg(feature = "regex")]
|
|
724
907
|
pub fn regexp_new(args: &[Value]) -> Value {
|
|
@@ -735,7 +918,7 @@ pub fn regexp_new(args: &[Value]) -> Value {
|
|
|
735
918
|
};
|
|
736
919
|
|
|
737
920
|
match TishRegExp::new(&pattern, &flags) {
|
|
738
|
-
Ok(re) => Value::RegExp(
|
|
921
|
+
Ok(re) => Value::RegExp(VmRef::new(re)),
|
|
739
922
|
Err(e) => {
|
|
740
923
|
eprintln!("RegExp error: {}", e);
|
|
741
924
|
Value::Null
|
|
@@ -816,7 +999,7 @@ fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
|
|
|
816
999
|
};
|
|
817
1000
|
}
|
|
818
1001
|
|
|
819
|
-
Value::Object(
|
|
1002
|
+
Value::Object(VmRef::new(obj))
|
|
820
1003
|
}
|
|
821
1004
|
Ok(None) | Err(_) => {
|
|
822
1005
|
if re.flags.global || re.flags.sticky {
|
|
@@ -831,12 +1014,12 @@ fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
|
|
|
831
1014
|
pub fn string_split_regex(s: &Value, separator: &Value, limit: Option<usize>) -> Value {
|
|
832
1015
|
let input = match s {
|
|
833
1016
|
Value::String(s) => s.as_ref(),
|
|
834
|
-
_ => return Value::Array(
|
|
1017
|
+
_ => return Value::Array(VmRef::new(vec![s.clone()])),
|
|
835
1018
|
};
|
|
836
|
-
|
|
1019
|
+
|
|
837
1020
|
let max = limit.unwrap_or(usize::MAX);
|
|
838
1021
|
if max == 0 {
|
|
839
|
-
return Value::Array(
|
|
1022
|
+
return Value::Array(VmRef::new(Vec::new()));
|
|
840
1023
|
}
|
|
841
1024
|
|
|
842
1025
|
match separator {
|
|
@@ -844,32 +1027,34 @@ pub fn string_split_regex(s: &Value, separator: &Value, limit: Option<usize>) ->
|
|
|
844
1027
|
let re = re.borrow();
|
|
845
1028
|
let mut result = Vec::new();
|
|
846
1029
|
let mut last_end = 0;
|
|
847
|
-
|
|
1030
|
+
|
|
848
1031
|
for mat in re.regex.find_iter(input) {
|
|
849
1032
|
match mat {
|
|
850
1033
|
Ok(m) => {
|
|
851
|
-
if result.len() >= max - 1 {
|
|
1034
|
+
if result.len() >= max - 1 {
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
852
1037
|
result.push(Value::String(input[last_end..m.start()].into()));
|
|
853
1038
|
last_end = m.end();
|
|
854
1039
|
}
|
|
855
1040
|
Err(_) => break,
|
|
856
1041
|
}
|
|
857
1042
|
}
|
|
858
|
-
|
|
1043
|
+
|
|
859
1044
|
if result.len() < max {
|
|
860
1045
|
result.push(Value::String(input[last_end..].into()));
|
|
861
1046
|
}
|
|
862
|
-
|
|
863
|
-
Value::Array(
|
|
1047
|
+
|
|
1048
|
+
Value::Array(VmRef::new(result))
|
|
864
1049
|
}
|
|
865
1050
|
Value::String(sep) => {
|
|
866
1051
|
let parts: Vec<Value> = input
|
|
867
1052
|
.splitn(max, sep.as_ref())
|
|
868
1053
|
.map(|s| Value::String(s.into()))
|
|
869
1054
|
.collect();
|
|
870
|
-
Value::Array(
|
|
1055
|
+
Value::Array(VmRef::new(parts))
|
|
871
1056
|
}
|
|
872
|
-
_ => Value::Array(
|
|
1057
|
+
_ => Value::Array(VmRef::new(vec![Value::String(input.into())])),
|
|
873
1058
|
}
|
|
874
1059
|
}
|
|
875
1060
|
|
|
@@ -883,11 +1068,11 @@ pub fn string_match_regex(s: &Value, regexp: &Value) -> Value {
|
|
|
883
1068
|
match regexp {
|
|
884
1069
|
Value::RegExp(re) => {
|
|
885
1070
|
let mut re = re.borrow_mut();
|
|
886
|
-
|
|
1071
|
+
|
|
887
1072
|
if re.flags.global {
|
|
888
1073
|
let mut matches = Vec::new();
|
|
889
1074
|
re.last_index = 0;
|
|
890
|
-
|
|
1075
|
+
|
|
891
1076
|
while let Ok(Some(m)) = re.regex.find_from_pos(input, re.last_index) {
|
|
892
1077
|
matches.push(Value::String(m.as_str().into()));
|
|
893
1078
|
if m.start() == m.end() {
|
|
@@ -895,22 +1080,26 @@ pub fn string_match_regex(s: &Value, regexp: &Value) -> Value {
|
|
|
895
1080
|
} else {
|
|
896
1081
|
re.last_index = m.end();
|
|
897
1082
|
}
|
|
898
|
-
if re.last_index > input.len() {
|
|
1083
|
+
if re.last_index > input.len() {
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
899
1086
|
}
|
|
900
|
-
|
|
1087
|
+
|
|
901
1088
|
re.last_index = 0;
|
|
902
|
-
|
|
903
|
-
if matches.is_empty() {
|
|
1089
|
+
|
|
1090
|
+
if matches.is_empty() {
|
|
1091
|
+
Value::Null
|
|
1092
|
+
} else {
|
|
1093
|
+
Value::Array(VmRef::new(matches))
|
|
1094
|
+
}
|
|
904
1095
|
} else {
|
|
905
1096
|
regexp_exec_impl(&mut re, input)
|
|
906
1097
|
}
|
|
907
1098
|
}
|
|
908
|
-
Value::String(pattern) => {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
}
|
|
1099
|
+
Value::String(pattern) => match tishlang_core::TishRegExp::new(pattern, "") {
|
|
1100
|
+
Ok(mut re) => regexp_exec_impl(&mut re, input),
|
|
1101
|
+
Err(_) => Value::Null,
|
|
1102
|
+
},
|
|
914
1103
|
_ => Value::Null,
|
|
915
1104
|
}
|
|
916
1105
|
}
|
|
@@ -997,19 +1186,16 @@ pub fn string_search_regex(s: &Value, regexp: &Value) -> Value {
|
|
|
997
1186
|
_ => Value::Number(-1.0),
|
|
998
1187
|
}
|
|
999
1188
|
}
|
|
1000
|
-
Value::String(pattern) => {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
Err(_) => Value::Number(-1.0),
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1189
|
+
Value::String(pattern) => match tishlang_core::TishRegExp::new(pattern, "") {
|
|
1190
|
+
Ok(re) => match re.regex.find(input) {
|
|
1191
|
+
Ok(Some(m)) => {
|
|
1192
|
+
let char_index = input[..m.start()].chars().count();
|
|
1193
|
+
Value::Number(char_index as f64)
|
|
1194
|
+
}
|
|
1195
|
+
_ => Value::Number(-1.0),
|
|
1196
|
+
},
|
|
1197
|
+
Err(_) => Value::Number(-1.0),
|
|
1198
|
+
},
|
|
1013
1199
|
_ => Value::Number(-1.0),
|
|
1014
1200
|
}
|
|
1015
1201
|
}
|