@tishlang/tish 1.0.7 → 1.0.11

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.
Files changed (127) hide show
  1. package/Cargo.toml +43 -0
  2. package/LICENSE +13 -0
  3. package/README.md +66 -0
  4. package/crates/js_to_tish/Cargo.toml +9 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +61 -0
  7. package/crates/js_to_tish/src/lib.rs +11 -0
  8. package/crates/js_to_tish/src/span_util.rs +35 -0
  9. package/crates/js_to_tish/src/transform/expr.rs +608 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +474 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +44 -0
  13. package/crates/tish/src/main.rs +585 -0
  14. package/crates/tish/src/repl_completion.rs +200 -0
  15. package/crates/tish/tests/integration_test.rs +726 -0
  16. package/crates/tish_ast/Cargo.toml +7 -0
  17. package/crates/tish_ast/src/ast.rs +494 -0
  18. package/crates/tish_ast/src/lib.rs +5 -0
  19. package/crates/tish_build_utils/Cargo.toml +5 -0
  20. package/crates/tish_build_utils/src/lib.rs +175 -0
  21. package/crates/tish_builtins/Cargo.toml +12 -0
  22. package/crates/tish_builtins/src/array.rs +410 -0
  23. package/crates/tish_builtins/src/globals.rs +197 -0
  24. package/crates/tish_builtins/src/helpers.rs +38 -0
  25. package/crates/tish_builtins/src/lib.rs +14 -0
  26. package/crates/tish_builtins/src/math.rs +80 -0
  27. package/crates/tish_builtins/src/object.rs +36 -0
  28. package/crates/tish_builtins/src/string.rs +253 -0
  29. package/crates/tish_bytecode/Cargo.toml +15 -0
  30. package/crates/tish_bytecode/src/chunk.rs +97 -0
  31. package/crates/tish_bytecode/src/compiler.rs +1361 -0
  32. package/crates/tish_bytecode/src/encoding.rs +100 -0
  33. package/crates/tish_bytecode/src/lib.rs +19 -0
  34. package/crates/tish_bytecode/src/opcode.rs +110 -0
  35. package/crates/tish_bytecode/src/peephole.rs +159 -0
  36. package/crates/tish_bytecode/src/serialize.rs +163 -0
  37. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  38. package/crates/tish_bytecode/tests/shortcircuit.rs +49 -0
  39. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  40. package/crates/tish_compile/Cargo.toml +21 -0
  41. package/crates/tish_compile/src/codegen.rs +3316 -0
  42. package/crates/tish_compile/src/lib.rs +71 -0
  43. package/crates/tish_compile/src/resolve.rs +631 -0
  44. package/crates/tish_compile/src/types.rs +304 -0
  45. package/crates/tish_compile_js/Cargo.toml +16 -0
  46. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  47. package/crates/tish_compile_js/src/codegen.rs +794 -0
  48. package/crates/tish_compile_js/src/error.rs +20 -0
  49. package/crates/tish_compile_js/src/js_intrinsics.rs +82 -0
  50. package/crates/tish_compile_js/src/lib.rs +27 -0
  51. package/crates/tish_compile_js/src/tests_jsx.rs +32 -0
  52. package/crates/tish_compiler_wasm/Cargo.toml +19 -0
  53. package/crates/tish_compiler_wasm/src/lib.rs +55 -0
  54. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +462 -0
  55. package/crates/tish_core/Cargo.toml +11 -0
  56. package/crates/tish_core/src/console_style.rs +128 -0
  57. package/crates/tish_core/src/json.rs +327 -0
  58. package/crates/tish_core/src/lib.rs +15 -0
  59. package/crates/tish_core/src/macros.rs +37 -0
  60. package/crates/tish_core/src/uri.rs +115 -0
  61. package/crates/tish_core/src/value.rs +376 -0
  62. package/crates/tish_cranelift/Cargo.toml +17 -0
  63. package/crates/tish_cranelift/src/lib.rs +41 -0
  64. package/crates/tish_cranelift/src/link.rs +120 -0
  65. package/crates/tish_cranelift/src/lower.rs +77 -0
  66. package/crates/tish_cranelift_runtime/Cargo.toml +19 -0
  67. package/crates/tish_cranelift_runtime/src/lib.rs +43 -0
  68. package/crates/tish_eval/Cargo.toml +26 -0
  69. package/crates/tish_eval/src/eval.rs +3205 -0
  70. package/crates/tish_eval/src/http.rs +122 -0
  71. package/crates/tish_eval/src/lib.rs +59 -0
  72. package/crates/tish_eval/src/natives.rs +301 -0
  73. package/crates/tish_eval/src/promise.rs +173 -0
  74. package/crates/tish_eval/src/regex.rs +298 -0
  75. package/crates/tish_eval/src/timers.rs +111 -0
  76. package/crates/tish_eval/src/value.rs +224 -0
  77. package/crates/tish_eval/src/value_convert.rs +85 -0
  78. package/crates/tish_fmt/Cargo.toml +16 -0
  79. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  80. package/crates/tish_fmt/src/lib.rs +884 -0
  81. package/crates/tish_jsx_web/Cargo.toml +7 -0
  82. package/crates/tish_jsx_web/README.md +18 -0
  83. package/crates/tish_jsx_web/src/lib.rs +157 -0
  84. package/crates/tish_jsx_web/vendor/Lattish.tish +347 -0
  85. package/crates/tish_lexer/Cargo.toml +7 -0
  86. package/crates/tish_lexer/src/lib.rs +430 -0
  87. package/crates/tish_lexer/src/token.rs +155 -0
  88. package/crates/tish_lint/Cargo.toml +17 -0
  89. package/crates/tish_lint/src/bin/tish-lint.rs +77 -0
  90. package/crates/tish_lint/src/lib.rs +278 -0
  91. package/crates/tish_llvm/Cargo.toml +11 -0
  92. package/crates/tish_llvm/src/lib.rs +106 -0
  93. package/crates/tish_lsp/Cargo.toml +22 -0
  94. package/crates/tish_lsp/README.md +26 -0
  95. package/crates/tish_lsp/src/main.rs +615 -0
  96. package/crates/tish_native/Cargo.toml +14 -0
  97. package/crates/tish_native/src/build.rs +102 -0
  98. package/crates/tish_native/src/lib.rs +237 -0
  99. package/crates/tish_opt/Cargo.toml +11 -0
  100. package/crates/tish_opt/src/lib.rs +896 -0
  101. package/crates/tish_parser/Cargo.toml +9 -0
  102. package/crates/tish_parser/src/lib.rs +123 -0
  103. package/crates/tish_parser/src/parser.rs +1714 -0
  104. package/crates/tish_runtime/Cargo.toml +26 -0
  105. package/crates/tish_runtime/src/http.rs +308 -0
  106. package/crates/tish_runtime/src/http_fetch.rs +453 -0
  107. package/crates/tish_runtime/src/lib.rs +1004 -0
  108. package/crates/tish_runtime/src/native_promise.rs +26 -0
  109. package/crates/tish_runtime/src/promise.rs +77 -0
  110. package/crates/tish_runtime/src/promise_io.rs +41 -0
  111. package/crates/tish_runtime/src/timers.rs +125 -0
  112. package/crates/tish_runtime/src/ws.rs +725 -0
  113. package/crates/tish_runtime/tests/fetch_readable_stream.rs +99 -0
  114. package/crates/tish_vm/Cargo.toml +31 -0
  115. package/crates/tish_vm/src/lib.rs +39 -0
  116. package/crates/tish_vm/src/vm.rs +1399 -0
  117. package/crates/tish_wasm/Cargo.toml +13 -0
  118. package/crates/tish_wasm/src/lib.rs +358 -0
  119. package/crates/tish_wasm_runtime/Cargo.toml +25 -0
  120. package/crates/tish_wasm_runtime/src/lib.rs +36 -0
  121. package/justfile +260 -0
  122. package/package.json +8 -3
  123. package/platform/darwin-arm64/tish +0 -0
  124. package/platform/darwin-x64/tish +0 -0
  125. package/platform/linux-arm64/tish +0 -0
  126. package/platform/linux-x64/tish +0 -0
  127. package/platform/win32-x64/tish.exe +0 -0
@@ -0,0 +1,1004 @@
1
+ //! Minimal runtime for Tish compiled output.
2
+ //!
3
+ //! Re-exports core types from tish_core and provides console, Math,
4
+ //! and other builtin functions for compiled Tish programs.
5
+
6
+ #[cfg(feature = "regex")]
7
+ use std::cell::RefCell;
8
+ #[cfg(feature = "regex")]
9
+ use std::rc::Rc;
10
+ use std::fmt;
11
+ use std::sync::OnceLock;
12
+ use tish_builtins::helpers::extract_num;
13
+ #[cfg(feature = "fs")]
14
+ use tish_builtins::helpers::make_error_value;
15
+
16
+ pub use tish_core::Value;
17
+
18
+ // Re-export array methods from tish_builtins
19
+ pub use tish_builtins::array::{
20
+ push as array_push_impl,
21
+ pop as array_pop,
22
+ shift as array_shift,
23
+ unshift as array_unshift_impl,
24
+ index_of as array_index_of_impl,
25
+ includes as array_includes_impl,
26
+ join as array_join_impl,
27
+ reverse as array_reverse,
28
+ shuffle as array_shuffle,
29
+ splice as array_splice_impl,
30
+ slice as array_slice_impl,
31
+ concat as array_concat_impl,
32
+ flat as array_flat_impl,
33
+ map as array_map,
34
+ filter as array_filter,
35
+ reduce as array_reduce,
36
+ for_each as array_for_each,
37
+ find as array_find,
38
+ find_index as array_find_index,
39
+ some as array_some,
40
+ every as array_every,
41
+ flat_map as array_flat_map,
42
+ sort_default as array_sort_default,
43
+ sort_with_comparator as array_sort_with_comparator,
44
+ sort_numeric_asc as array_sort_numeric_asc,
45
+ sort_numeric_desc as array_sort_numeric_desc,
46
+ };
47
+
48
+ // Re-export string methods from tish_builtins
49
+ pub use tish_builtins::string::{
50
+ index_of as string_index_of_impl,
51
+ includes as string_includes_impl,
52
+ slice as string_slice_impl,
53
+ substring as string_substring_impl,
54
+ split as string_split_impl,
55
+ trim as string_trim,
56
+ to_upper_case as string_to_upper_case,
57
+ to_lower_case as string_to_lower_case,
58
+ starts_with as string_starts_with_impl,
59
+ ends_with as string_ends_with_impl,
60
+ replace as string_replace_impl,
61
+ replace_all as string_replace_all_impl,
62
+ char_at as string_char_at_impl,
63
+ char_code_at as string_char_code_at_impl,
64
+ repeat as string_repeat_impl,
65
+ pad_start as string_pad_start_impl,
66
+ pad_end as string_pad_end_impl,
67
+ };
68
+
69
+ // Wrapper functions to maintain API compatibility
70
+ pub fn array_push(arr: &Value, args: &[Value]) -> Value { array_push_impl(arr, args) }
71
+ pub fn array_unshift(arr: &Value, args: &[Value]) -> Value { array_unshift_impl(arr, args) }
72
+ pub fn array_index_of(arr: &Value, search: &Value) -> Value { array_index_of_impl(arr, search) }
73
+ pub fn array_includes(arr: &Value, search: &Value, from: &Value) -> Value {
74
+ array_includes_impl(arr, search, Some(from))
75
+ }
76
+ pub fn array_join(arr: &Value, sep: &Value) -> Value { array_join_impl(arr, sep) }
77
+ pub fn array_splice(arr: &Value, start: &Value, delete_count: Option<&Value>, items: &[Value]) -> Value {
78
+ array_splice_impl(arr, start, delete_count, items)
79
+ }
80
+ pub fn array_slice(arr: &Value, start: &Value, end: &Value) -> Value { array_slice_impl(arr, start, end) }
81
+ pub fn array_concat(arr: &Value, args: &[Value]) -> Value { array_concat_impl(arr, args) }
82
+ pub fn array_flat(arr: &Value, depth: &Value) -> Value { array_flat_impl(arr, depth) }
83
+
84
+ pub fn array_sort(arr: &Value, comparator: Option<&Value>) -> Value {
85
+ match comparator {
86
+ Some(cmp) => array_sort_with_comparator(arr, cmp),
87
+ None => array_sort_default(arr),
88
+ }
89
+ }
90
+
91
+ pub fn string_index_of(s: &Value, search: &Value, from: &Value) -> Value {
92
+ string_index_of_impl(s, search, Some(from))
93
+ }
94
+ pub fn string_includes(s: &Value, search: &Value, from: &Value) -> Value {
95
+ string_includes_impl(s, search, Some(from))
96
+ }
97
+ pub fn string_slice(s: &Value, start: &Value, end: &Value) -> Value { string_slice_impl(s, start, end) }
98
+ pub fn string_substring(s: &Value, start: &Value, end: &Value) -> Value { string_substring_impl(s, start, end) }
99
+ pub fn string_split(s: &Value, sep: &Value) -> Value { string_split_impl(s, sep) }
100
+ pub fn string_starts_with(s: &Value, search: &Value) -> Value { string_starts_with_impl(s, search) }
101
+ pub fn string_ends_with(s: &Value, search: &Value) -> Value { string_ends_with_impl(s, search) }
102
+ pub fn string_replace(s: &Value, search: &Value, replacement: &Value) -> Value {
103
+ #[cfg(feature = "regex")]
104
+ if matches!(search, Value::RegExp(_)) {
105
+ return string_replace_regex_or_callback(s, search, replacement);
106
+ }
107
+ string_replace_impl(s, search, replacement)
108
+ }
109
+ pub fn string_replace_all(s: &Value, search: &Value, replacement: &Value) -> Value { string_replace_all_impl(s, search, replacement) }
110
+ pub fn string_char_at(s: &Value, idx: &Value) -> Value { string_char_at_impl(s, idx) }
111
+ pub fn string_char_code_at(s: &Value, idx: &Value) -> Value { string_char_code_at_impl(s, idx) }
112
+ pub fn string_repeat(s: &Value, count: &Value) -> Value { string_repeat_impl(s, count) }
113
+ pub fn string_pad_start(s: &Value, target_len: &Value, pad: &Value) -> Value { string_pad_start_impl(s, target_len, pad) }
114
+ pub fn string_pad_end(s: &Value, target_len: &Value, pad: &Value) -> Value { string_pad_end_impl(s, target_len, pad) }
115
+
116
+ /// Number.prototype.toFixed(digits) - format number with fixed decimal places (0-20)
117
+ pub fn number_to_fixed(n: &Value, digits: &Value) -> Value {
118
+ let num = match n {
119
+ Value::Number(x) => *x,
120
+ _ => f64::NAN,
121
+ };
122
+ let d = match digits {
123
+ Value::Number(x) => (*x as i32).clamp(0, 20),
124
+ _ => 0,
125
+ };
126
+ Value::String(format!("{:.*}", d as usize, num).into())
127
+ }
128
+
129
+ /// Operators module for compound assignment operations
130
+ pub mod ops {
131
+ use tish_core::Value;
132
+
133
+ #[inline]
134
+ pub fn add(left: &Value, right: &Value) -> Result<Value, Box<dyn std::error::Error>> {
135
+ match (left, right) {
136
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
137
+ (Value::String(a), Value::String(b)) => {
138
+ let mut s = String::with_capacity(a.len() + b.len());
139
+ s.push_str(a);
140
+ s.push_str(b);
141
+ Ok(Value::String(s.into()))
142
+ }
143
+ (Value::String(a), b) => {
144
+ let b_str = b.to_display_string();
145
+ let mut s = String::with_capacity(a.len() + b_str.len());
146
+ s.push_str(a);
147
+ s.push_str(&b_str);
148
+ Ok(Value::String(s.into()))
149
+ }
150
+ (a, Value::String(b)) => {
151
+ let a_str = a.to_display_string();
152
+ let mut s = String::with_capacity(a_str.len() + b.len());
153
+ s.push_str(&a_str);
154
+ s.push_str(b);
155
+ Ok(Value::String(s.into()))
156
+ }
157
+ _ => Err(format!("Cannot add {:?} and {:?}", left, right).into()),
158
+ }
159
+ }
160
+
161
+ #[inline]
162
+ pub fn sub(left: &Value, right: &Value) -> Result<Value, Box<dyn std::error::Error>> {
163
+ match (left, right) {
164
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a - b)),
165
+ _ => Err(format!("Cannot subtract {:?} from {:?}", right, left).into()),
166
+ }
167
+ }
168
+
169
+ #[inline]
170
+ pub fn mul(left: &Value, right: &Value) -> Result<Value, Box<dyn std::error::Error>> {
171
+ match (left, right) {
172
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a * b)),
173
+ _ => Err(format!("Cannot multiply {:?} and {:?}", left, right).into()),
174
+ }
175
+ }
176
+
177
+ #[inline]
178
+ pub fn div(left: &Value, right: &Value) -> Result<Value, Box<dyn std::error::Error>> {
179
+ match (left, right) {
180
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a / b)),
181
+ _ => Err(format!("Cannot divide {:?} by {:?}", left, right).into()),
182
+ }
183
+ }
184
+
185
+ /// Compare two values for <. Supports number vs number and string vs string.
186
+ #[inline]
187
+ pub fn lt(left: &Value, right: &Value) -> Value {
188
+ let b = match (left, right) {
189
+ (Value::Number(a), Value::Number(b)) => a < b,
190
+ (Value::String(a), Value::String(b)) => a.as_ref() < b.as_ref(),
191
+ _ => false,
192
+ };
193
+ Value::Bool(b)
194
+ }
195
+
196
+ #[inline]
197
+ pub fn le(left: &Value, right: &Value) -> Value {
198
+ let b = match (left, right) {
199
+ (Value::Number(a), Value::Number(b)) => a <= b,
200
+ (Value::String(a), Value::String(b)) => a.as_ref() <= b.as_ref(),
201
+ _ => false,
202
+ };
203
+ Value::Bool(b)
204
+ }
205
+
206
+ #[inline]
207
+ pub fn gt(left: &Value, right: &Value) -> Value {
208
+ let b = match (left, right) {
209
+ (Value::Number(a), Value::Number(b)) => a > b,
210
+ (Value::String(a), Value::String(b)) => a.as_ref() > b.as_ref(),
211
+ _ => false,
212
+ };
213
+ Value::Bool(b)
214
+ }
215
+
216
+ #[inline]
217
+ pub fn ge(left: &Value, right: &Value) -> Value {
218
+ let b = match (left, right) {
219
+ (Value::Number(a), Value::Number(b)) => a >= b,
220
+ (Value::String(a), Value::String(b)) => a.as_ref() >= b.as_ref(),
221
+ _ => false,
222
+ };
223
+ Value::Bool(b)
224
+ }
225
+
226
+ #[inline]
227
+ pub fn modulo(left: &Value, right: &Value) -> Result<Value, Box<dyn std::error::Error>> {
228
+ match (left, right) {
229
+ (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a % b)),
230
+ _ => Err(format!("Cannot modulo {:?} by {:?}", left, right).into()),
231
+ }
232
+ }
233
+ }
234
+
235
+ use tish_builtins::globals::{
236
+ array_is_array as builtins_array_is_array,
237
+ boolean as builtins_boolean,
238
+ decode_uri as builtins_decode_uri,
239
+ encode_uri as builtins_encode_uri,
240
+ is_finite as builtins_is_finite,
241
+ is_nan as builtins_is_nan,
242
+ object_assign as builtins_object_assign,
243
+ object_entries as builtins_object_entries,
244
+ object_from_entries as builtins_object_from_entries,
245
+ object_keys as builtins_object_keys,
246
+ object_values as builtins_object_values,
247
+ string_from_char_code as builtins_string_from_char_code,
248
+ };
249
+ use tish_core::{
250
+ json_parse as core_json_parse,
251
+ json_stringify as core_json_stringify,
252
+ };
253
+
254
+ /// Error type for Tish throw/catch.
255
+ #[derive(Debug, Clone)]
256
+ pub enum TishError {
257
+ Throw(Value),
258
+ }
259
+
260
+ impl fmt::Display for TishError {
261
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262
+ match self {
263
+ TishError::Throw(v) => write!(f, "{}", v.to_display_string()),
264
+ }
265
+ }
266
+ }
267
+
268
+ impl std::error::Error for TishError {}
269
+
270
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
271
+ enum LogLevel {
272
+ Debug = 0,
273
+ Info = 1,
274
+ Log = 2,
275
+ Warn = 3,
276
+ Error = 4,
277
+ }
278
+
279
+ static LOG_LEVEL: OnceLock<LogLevel> = OnceLock::new();
280
+
281
+ fn get_log_level() -> LogLevel {
282
+ *LOG_LEVEL.get_or_init(|| {
283
+ match std::env::var("TISH_LOG_LEVEL").as_deref() {
284
+ Ok("debug") => LogLevel::Debug,
285
+ Ok("info") => LogLevel::Info,
286
+ Ok("warn") => LogLevel::Warn,
287
+ Ok("error") => LogLevel::Error,
288
+ _ => LogLevel::Log,
289
+ }
290
+ })
291
+ }
292
+
293
+ fn format_args(args: &[Value]) -> String {
294
+ tish_core::format_values_for_console(args, tish_core::use_console_colors())
295
+ }
296
+
297
+ pub fn console_debug(args: &[Value]) {
298
+ if get_log_level() <= LogLevel::Debug {
299
+ println!("{}", format_args(args));
300
+ }
301
+ }
302
+
303
+ pub fn console_info(args: &[Value]) {
304
+ if get_log_level() <= LogLevel::Info {
305
+ println!("{}", format_args(args));
306
+ }
307
+ }
308
+
309
+ pub fn console_log(args: &[Value]) {
310
+ if get_log_level() <= LogLevel::Log {
311
+ println!("{}", format_args(args));
312
+ }
313
+ }
314
+
315
+ pub fn console_warn(args: &[Value]) {
316
+ if get_log_level() <= LogLevel::Warn {
317
+ eprintln!("{}", format_args(args));
318
+ }
319
+ }
320
+
321
+ pub fn console_error(args: &[Value]) {
322
+ eprintln!("{}", format_args(args));
323
+ }
324
+
325
+ pub fn parse_int(args: &[Value]) -> Value {
326
+ tish_builtins::globals::parse_int(args)
327
+ }
328
+
329
+ pub fn parse_float(args: &[Value]) -> Value {
330
+ tish_builtins::globals::parse_float(args)
331
+ }
332
+
333
+ pub fn is_finite(args: &[Value]) -> Value {
334
+ builtins_is_finite(args)
335
+ }
336
+
337
+ pub fn is_nan(args: &[Value]) -> Value {
338
+ builtins_is_nan(args)
339
+ }
340
+
341
+ pub fn boolean(args: &[Value]) -> Value {
342
+ builtins_boolean(args)
343
+ }
344
+
345
+ pub fn decode_uri(args: &[Value]) -> Value {
346
+ builtins_decode_uri(args)
347
+ }
348
+
349
+ pub fn encode_uri(args: &[Value]) -> Value {
350
+ builtins_encode_uri(args)
351
+ }
352
+
353
+ // Math functions - use tish_builtins::math
354
+ pub use tish_builtins::math::{
355
+ abs as tish_math_abs_impl,
356
+ sqrt as tish_math_sqrt_impl,
357
+ floor as tish_math_floor_impl,
358
+ ceil as tish_math_ceil_impl,
359
+ round as tish_math_round_impl,
360
+ sin as tish_math_sin_impl,
361
+ cos as tish_math_cos_impl,
362
+ tan as tish_math_tan_impl,
363
+ exp as tish_math_exp_impl,
364
+ trunc as tish_math_trunc_impl,
365
+ min as tish_math_min_impl,
366
+ max as tish_math_max_impl,
367
+ pow as tish_math_pow_impl,
368
+ sign as tish_math_sign_impl,
369
+ random as tish_math_random_impl,
370
+ };
371
+
372
+ // Wrapper functions to maintain API (existing callers use math_* naming)
373
+ pub fn math_abs(args: &[Value]) -> Value { tish_math_abs_impl(args) }
374
+ pub fn math_sqrt(args: &[Value]) -> Value { tish_math_sqrt_impl(args) }
375
+ pub fn math_floor(args: &[Value]) -> Value { tish_math_floor_impl(args) }
376
+ pub fn math_ceil(args: &[Value]) -> Value { tish_math_ceil_impl(args) }
377
+ pub fn math_round(args: &[Value]) -> Value { tish_math_round_impl(args) }
378
+ pub fn math_min(args: &[Value]) -> Value { tish_math_min_impl(args) }
379
+ pub fn math_max(args: &[Value]) -> Value { tish_math_max_impl(args) }
380
+ pub fn math_sin(args: &[Value]) -> Value { tish_math_sin_impl(args) }
381
+ pub fn math_cos(args: &[Value]) -> Value { tish_math_cos_impl(args) }
382
+ pub fn math_tan(args: &[Value]) -> Value { tish_math_tan_impl(args) }
383
+ pub fn math_exp(args: &[Value]) -> Value { tish_math_exp_impl(args) }
384
+ pub fn math_trunc(args: &[Value]) -> Value { tish_math_trunc_impl(args) }
385
+ pub fn math_pow(args: &[Value]) -> Value { tish_math_pow_impl(args) }
386
+ pub fn math_sign(args: &[Value]) -> Value { tish_math_sign_impl(args) }
387
+ pub fn math_random(args: &[Value]) -> Value { tish_math_random_impl(args) }
388
+
389
+ pub fn math_log(args: &[Value]) -> Value {
390
+ let n = extract_num(args.first()).unwrap_or(f64::NAN);
391
+ Value::Number(n.ln())
392
+ }
393
+
394
+ pub fn json_stringify(args: &[Value]) -> Value {
395
+ let v = args.first().cloned().unwrap_or(Value::Null);
396
+ Value::String(core_json_stringify(&v).into())
397
+ }
398
+
399
+ pub fn json_parse(args: &[Value]) -> Value {
400
+ let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
401
+ core_json_parse(&s).unwrap_or(Value::Null)
402
+ }
403
+
404
+ pub fn date_now(_args: &[Value]) -> Value {
405
+ use std::time::{SystemTime, UNIX_EPOCH};
406
+ let now = SystemTime::now()
407
+ .duration_since(UNIX_EPOCH)
408
+ .map(|d| d.as_millis() as f64)
409
+ .unwrap_or(0.0);
410
+ Value::Number(now)
411
+ }
412
+
413
+ pub fn array_is_array(args: &[Value]) -> Value {
414
+ builtins_array_is_array(args)
415
+ }
416
+
417
+ pub fn string_from_char_code(args: &[Value]) -> Value {
418
+ builtins_string_from_char_code(args)
419
+ }
420
+
421
+ #[cfg(feature = "process")]
422
+ pub fn process_exit(args: &[Value]) -> Value {
423
+ let code = args.first().and_then(|v| match v {
424
+ Value::Number(n) => Some(*n as i32),
425
+ _ => None,
426
+ }).unwrap_or(0);
427
+ std::process::exit(code);
428
+ }
429
+
430
+ #[cfg(feature = "process")]
431
+ pub fn process_cwd(_args: &[Value]) -> Value {
432
+ let cwd = std::env::current_dir()
433
+ .map(|p| p.display().to_string())
434
+ .unwrap_or_default();
435
+ Value::String(cwd.into())
436
+ }
437
+
438
+ #[cfg(feature = "process")]
439
+ pub fn process_exec(args: &[Value]) -> Value {
440
+ use std::process::Command;
441
+ let cmd = args.first().map(|v| v.to_display_string()).unwrap_or_default();
442
+ if cmd.is_empty() {
443
+ return Value::Number(0.0);
444
+ }
445
+ match Command::new("sh").arg("-c").arg(&cmd).status() {
446
+ Ok(status) => Value::Number(status.code().unwrap_or(1) as f64),
447
+ Err(_) => Value::Number(1.0),
448
+ }
449
+ }
450
+
451
+ #[cfg(feature = "fs")]
452
+ pub fn read_file(args: &[Value]) -> Value {
453
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
454
+ match std::fs::read_to_string(&path) {
455
+ Ok(content) => Value::String(content.into()),
456
+ Err(e) => make_error_value(e),
457
+ }
458
+ }
459
+
460
+ #[cfg(feature = "fs")]
461
+ pub fn write_file(args: &[Value]) -> Value {
462
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
463
+ let content = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
464
+ match std::fs::write(&path, &content) {
465
+ Ok(()) => Value::Bool(true),
466
+ Err(e) => make_error_value(e),
467
+ }
468
+ }
469
+
470
+ #[cfg(feature = "fs")]
471
+ pub fn file_exists(args: &[Value]) -> Value {
472
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
473
+ Value::Bool(std::path::Path::new(&path).exists())
474
+ }
475
+
476
+ #[cfg(feature = "fs")]
477
+ pub fn is_dir(args: &[Value]) -> Value {
478
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
479
+ Value::Bool(std::path::Path::new(&path).is_dir())
480
+ }
481
+
482
+ #[cfg(feature = "fs")]
483
+ pub fn read_dir(args: &[Value]) -> Value {
484
+ use std::cell::RefCell;
485
+ use std::rc::Rc;
486
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_else(|| ".".to_string());
487
+ match std::fs::read_dir(&path) {
488
+ Ok(entries) => {
489
+ let files: Vec<Value> = entries
490
+ .filter_map(|e| e.ok())
491
+ .map(|e| Value::String(e.file_name().to_string_lossy().into()))
492
+ .collect();
493
+ Value::Array(Rc::new(RefCell::new(files)))
494
+ }
495
+ Err(e) => make_error_value(e),
496
+ }
497
+ }
498
+
499
+ #[cfg(feature = "fs")]
500
+ pub fn mkdir(args: &[Value]) -> Value {
501
+ let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
502
+ match std::fs::create_dir_all(&path) {
503
+ Ok(()) => Value::Bool(true),
504
+ Err(e) => make_error_value(e),
505
+ }
506
+ }
507
+
508
+ use std::sync::Arc;
509
+
510
+ #[inline]
511
+ pub fn get_prop(obj: &Value, key: impl AsRef<str>) -> Value {
512
+ let key = key.as_ref();
513
+ match obj {
514
+ Value::Object(map) => {
515
+ let k: Arc<str> = key.into();
516
+ map.borrow().get(&k).cloned().unwrap_or(Value::Null)
517
+ }
518
+ Value::Array(arr) => {
519
+ if key == "length" {
520
+ Value::Number(arr.borrow().len() as f64)
521
+ } else if let Ok(idx) = key.parse::<usize>() {
522
+ arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
523
+ } else {
524
+ Value::Null
525
+ }
526
+ }
527
+ Value::String(s) => {
528
+ if key == "length" {
529
+ Value::Number(s.chars().count() as f64)
530
+ } else {
531
+ Value::Null
532
+ }
533
+ }
534
+ #[cfg(feature = "regex")]
535
+ Value::RegExp(re) => {
536
+ let re = Rc::clone(re);
537
+ if key == "exec" {
538
+ Value::Function(Rc::new(move |args: &[Value]| {
539
+ let input = args.first().unwrap_or(&Value::Null);
540
+ regexp_exec(&Value::RegExp(Rc::clone(&re)), input)
541
+ }))
542
+ } else if key == "test" {
543
+ Value::Function(Rc::new(move |args: &[Value]| {
544
+ let input = args.first().unwrap_or(&Value::Null);
545
+ regexp_test(&Value::RegExp(Rc::clone(&re)), input)
546
+ }))
547
+ } else {
548
+ Value::Null
549
+ }
550
+ }
551
+ Value::Opaque(o) => o
552
+ .get_method(key)
553
+ .map(Value::Function)
554
+ .unwrap_or(Value::Null),
555
+ _ => Value::Null,
556
+ }
557
+ }
558
+
559
+ #[inline]
560
+ pub fn get_index(obj: &Value, index: &Value) -> Value {
561
+ match obj {
562
+ Value::Array(arr) => {
563
+ let idx = match index {
564
+ Value::Number(n) => *n as usize,
565
+ _ => return Value::Null,
566
+ };
567
+ arr.borrow().get(idx).cloned().unwrap_or(Value::Null)
568
+ }
569
+ Value::Object(map) => {
570
+ let key: Arc<str> = match index {
571
+ Value::Number(n) => n.to_string().into(),
572
+ Value::String(s) => Arc::clone(s),
573
+ _ => return Value::Null,
574
+ };
575
+ map.borrow().get(&key).cloned().unwrap_or(Value::Null)
576
+ }
577
+ _ => Value::Null,
578
+ }
579
+ }
580
+
581
+ #[inline]
582
+ pub fn set_prop(obj: &Value, key: &str, val: Value) -> Value {
583
+ match obj {
584
+ Value::Object(map) => {
585
+ map.borrow_mut().insert(Arc::from(key), val.clone());
586
+ val
587
+ }
588
+ _ => panic!("Cannot assign property on non-object"),
589
+ }
590
+ }
591
+
592
+ #[inline]
593
+ pub fn set_index(obj: &Value, idx: &Value, val: Value) -> Value {
594
+ match obj {
595
+ Value::Array(arr) => {
596
+ let index = match idx {
597
+ Value::Number(n) => *n as usize,
598
+ _ => panic!("Array index must be number"),
599
+ };
600
+ let mut arr_mut = arr.borrow_mut();
601
+ while arr_mut.len() <= index {
602
+ arr_mut.push(Value::Null);
603
+ }
604
+ arr_mut[index] = val.clone();
605
+ val
606
+ }
607
+ Value::Object(map) => {
608
+ let key: Arc<str> = match idx {
609
+ Value::Number(n) => n.to_string().into(),
610
+ Value::String(s) => Arc::clone(s),
611
+ _ => panic!("Object key must be string or number"),
612
+ };
613
+ map.borrow_mut().insert(key, val.clone());
614
+ val
615
+ }
616
+ _ => panic!("Cannot index assign on non-array/object"),
617
+ }
618
+ }
619
+
620
+ pub fn in_operator(key: &Value, obj: &Value) -> Value {
621
+ let key_str: Arc<str> = match key {
622
+ Value::String(s) => Arc::clone(s),
623
+ Value::Number(n) => n.to_string().into(),
624
+ _ => return Value::Bool(false),
625
+ };
626
+
627
+ let result = match obj {
628
+ Value::Object(map) => map.borrow().contains_key(&key_str),
629
+ Value::Array(arr) => {
630
+ key_str.as_ref() == "length"
631
+ || key_str
632
+ .parse::<usize>()
633
+ .ok()
634
+ .map(|i| i < arr.borrow().len())
635
+ .unwrap_or(false)
636
+ }
637
+ _ => false,
638
+ };
639
+
640
+ Value::Bool(result)
641
+ }
642
+
643
+ // Object functions - delegate to tish_builtins::globals
644
+ pub fn object_assign(args: &[Value]) -> Value {
645
+ builtins_object_assign(args)
646
+ }
647
+
648
+ pub fn object_keys(args: &[Value]) -> Value {
649
+ builtins_object_keys(args)
650
+ }
651
+
652
+ pub fn object_values(args: &[Value]) -> Value {
653
+ builtins_object_values(args)
654
+ }
655
+
656
+ pub fn object_entries(args: &[Value]) -> Value {
657
+ builtins_object_entries(args)
658
+ }
659
+
660
+ pub fn object_from_entries(args: &[Value]) -> Value {
661
+ builtins_object_from_entries(args)
662
+ }
663
+
664
+ // HTTP Support
665
+ #[cfg(feature = "http")]
666
+ mod promise_io;
667
+
668
+ #[cfg(feature = "http")]
669
+ mod http;
670
+
671
+ #[cfg(feature = "http")]
672
+ mod http_fetch;
673
+
674
+ #[cfg(any(feature = "http", feature = "ws"))]
675
+ mod timers;
676
+
677
+ #[cfg(feature = "http")]
678
+ mod promise;
679
+
680
+ #[cfg(feature = "http")]
681
+ mod native_promise;
682
+
683
+ #[cfg(feature = "ws")]
684
+ mod ws;
685
+
686
+ #[cfg(feature = "ws")]
687
+ pub use ws::{
688
+ web_socket_client, web_socket_server_accept, web_socket_server_construct, web_socket_server_listen,
689
+ ws_broadcast_native, ws_send_native,
690
+ };
691
+
692
+ #[cfg(feature = "http")]
693
+ pub use http::{
694
+ await_fetch as http_await_fetch,
695
+ await_fetch_all as http_await_fetch_all,
696
+ serve as http_serve,
697
+ };
698
+
699
+ #[cfg(any(feature = "http", feature = "ws"))]
700
+ pub use timers::{set_timeout as timer_set_timeout, clear_timeout as timer_clear_timeout};
701
+
702
+ #[cfg(feature = "http")]
703
+ pub use promise::promise_object;
704
+
705
+ #[cfg(feature = "http")]
706
+ pub use native_promise::{await_promise, fetch_all_promise, fetch_async_promise, fetch_promise};
707
+
708
+ // RegExp Support
709
+ #[cfg(feature = "regex")]
710
+ pub use tish_core::{TishRegExp, RegExpFlags};
711
+
712
+ #[cfg(feature = "regex")]
713
+ pub fn regexp_new(args: &[Value]) -> Value {
714
+ let pattern = match args.first() {
715
+ Some(Value::String(s)) => s.to_string(),
716
+ Some(v) => v.to_display_string(),
717
+ None => String::new(),
718
+ };
719
+
720
+ let flags = match args.get(1) {
721
+ Some(Value::String(s)) => s.to_string(),
722
+ Some(Value::Null) | None => String::new(),
723
+ Some(v) => v.to_display_string(),
724
+ };
725
+
726
+ match TishRegExp::new(&pattern, &flags) {
727
+ Ok(re) => Value::RegExp(Rc::new(RefCell::new(re))),
728
+ Err(e) => {
729
+ eprintln!("RegExp error: {}", e);
730
+ Value::Null
731
+ }
732
+ }
733
+ }
734
+
735
+ #[cfg(feature = "regex")]
736
+ pub fn regexp_test(re: &Value, input: &Value) -> Value {
737
+ if let Value::RegExp(re) = re {
738
+ let input_str = input.to_display_string();
739
+ Value::Bool(re.borrow_mut().test(&input_str))
740
+ } else {
741
+ Value::Bool(false)
742
+ }
743
+ }
744
+
745
+ #[cfg(feature = "regex")]
746
+ pub fn regexp_exec(re: &Value, input: &Value) -> Value {
747
+ if let Value::RegExp(re) = re {
748
+ let input_str = input.to_display_string();
749
+ regexp_exec_impl(&mut re.borrow_mut(), &input_str)
750
+ } else {
751
+ Value::Null
752
+ }
753
+ }
754
+
755
+ #[cfg(feature = "regex")]
756
+ fn regexp_exec_impl(re: &mut tish_core::TishRegExp, input: &str) -> Value {
757
+ use std::collections::HashMap;
758
+
759
+ let start = if re.flags.global || re.flags.sticky {
760
+ re.last_index
761
+ } else {
762
+ 0
763
+ };
764
+
765
+ let char_count = input.chars().count();
766
+ if start > char_count {
767
+ if re.flags.global || re.flags.sticky {
768
+ re.last_index = 0;
769
+ }
770
+ return Value::Null;
771
+ }
772
+
773
+ let byte_start: usize = input.chars().take(start).map(|c| c.len_utf8()).sum();
774
+ let search_str = &input[byte_start..];
775
+
776
+ match re.regex.captures(search_str) {
777
+ Ok(Some(caps)) => {
778
+ let full_match = caps.get(0).unwrap();
779
+
780
+ if re.flags.sticky && full_match.start() != 0 {
781
+ re.last_index = 0;
782
+ return Value::Null;
783
+ }
784
+
785
+ let match_byte_start = byte_start + full_match.start();
786
+ let match_char_index = input[..match_byte_start].chars().count();
787
+
788
+ let mut obj: HashMap<std::sync::Arc<str>, Value> = HashMap::new();
789
+ obj.insert(Arc::from("0"), Value::String(full_match.as_str().into()));
790
+ for i in 1..caps.len() {
791
+ let val = match caps.get(i) {
792
+ Some(m) => Value::String(m.as_str().into()),
793
+ None => Value::Null,
794
+ };
795
+ obj.insert(Arc::from(i.to_string().as_str()), val);
796
+ }
797
+ obj.insert(Arc::from("index"), Value::Number(match_char_index as f64));
798
+
799
+ if re.flags.global || re.flags.sticky {
800
+ let match_end_chars = input[..byte_start + full_match.end()].chars().count();
801
+ re.last_index = if full_match.start() == full_match.end() {
802
+ match_end_chars + 1
803
+ } else {
804
+ match_end_chars
805
+ };
806
+ }
807
+
808
+ Value::Object(Rc::new(RefCell::new(obj)))
809
+ }
810
+ Ok(None) | Err(_) => {
811
+ if re.flags.global || re.flags.sticky {
812
+ re.last_index = 0;
813
+ }
814
+ Value::Null
815
+ }
816
+ }
817
+ }
818
+
819
+ #[cfg(feature = "regex")]
820
+ pub fn string_split_regex(s: &Value, separator: &Value, limit: Option<usize>) -> Value {
821
+ let input = match s {
822
+ Value::String(s) => s.as_ref(),
823
+ _ => return Value::Array(Rc::new(RefCell::new(vec![s.clone()]))),
824
+ };
825
+
826
+ let max = limit.unwrap_or(usize::MAX);
827
+ if max == 0 {
828
+ return Value::Array(Rc::new(RefCell::new(Vec::new())));
829
+ }
830
+
831
+ match separator {
832
+ Value::RegExp(re) => {
833
+ let re = re.borrow();
834
+ let mut result = Vec::new();
835
+ let mut last_end = 0;
836
+
837
+ for mat in re.regex.find_iter(input) {
838
+ match mat {
839
+ Ok(m) => {
840
+ if result.len() >= max - 1 { break; }
841
+ result.push(Value::String(input[last_end..m.start()].into()));
842
+ last_end = m.end();
843
+ }
844
+ Err(_) => break,
845
+ }
846
+ }
847
+
848
+ if result.len() < max {
849
+ result.push(Value::String(input[last_end..].into()));
850
+ }
851
+
852
+ Value::Array(Rc::new(RefCell::new(result)))
853
+ }
854
+ Value::String(sep) => {
855
+ let parts: Vec<Value> = input
856
+ .splitn(max, sep.as_ref())
857
+ .map(|s| Value::String(s.into()))
858
+ .collect();
859
+ Value::Array(Rc::new(RefCell::new(parts)))
860
+ }
861
+ _ => Value::Array(Rc::new(RefCell::new(vec![Value::String(input.into())]))),
862
+ }
863
+ }
864
+
865
+ #[cfg(feature = "regex")]
866
+ pub fn string_match_regex(s: &Value, regexp: &Value) -> Value {
867
+ let input = match s {
868
+ Value::String(s) => s.as_ref(),
869
+ _ => return Value::Null,
870
+ };
871
+
872
+ match regexp {
873
+ Value::RegExp(re) => {
874
+ let mut re = re.borrow_mut();
875
+
876
+ if re.flags.global {
877
+ let mut matches = Vec::new();
878
+ re.last_index = 0;
879
+
880
+ while let Ok(Some(m)) = re.regex.find_from_pos(input, re.last_index) {
881
+ matches.push(Value::String(m.as_str().into()));
882
+ if m.start() == m.end() {
883
+ re.last_index = m.end() + 1;
884
+ } else {
885
+ re.last_index = m.end();
886
+ }
887
+ if re.last_index > input.len() { break; }
888
+ }
889
+
890
+ re.last_index = 0;
891
+
892
+ if matches.is_empty() { Value::Null } else { Value::Array(Rc::new(RefCell::new(matches))) }
893
+ } else {
894
+ regexp_exec_impl(&mut re, input)
895
+ }
896
+ }
897
+ Value::String(pattern) => {
898
+ match tish_core::TishRegExp::new(pattern, "") {
899
+ Ok(mut re) => regexp_exec_impl(&mut re, input),
900
+ Err(_) => Value::Null,
901
+ }
902
+ }
903
+ _ => Value::Null,
904
+ }
905
+ }
906
+
907
+ #[cfg(feature = "regex")]
908
+ fn string_replace_regex_or_callback(s: &Value, search: &Value, replacement: &Value) -> Value {
909
+ let input = match s {
910
+ Value::String(s) => s.as_ref(),
911
+ _ => return s.clone(),
912
+ };
913
+
914
+ let Value::RegExp(re) = search else {
915
+ return s.clone();
916
+ };
917
+ let re_guard = re.borrow();
918
+
919
+ if let Value::Function(cb) = replacement {
920
+ let limit = if re_guard.flags.global { usize::MAX } else { 1 };
921
+ let mut result = String::new();
922
+ let mut last_end: usize = 0;
923
+ for (count, cap_result) in re_guard.regex.captures_iter(input).enumerate() {
924
+ if count >= limit {
925
+ break;
926
+ }
927
+ let Ok(caps) = cap_result else {
928
+ break;
929
+ };
930
+ let full = caps.get(0).unwrap();
931
+ let match_str = full.as_str();
932
+ let byte_start = full.start();
933
+ let char_index = input[..byte_start].chars().count();
934
+
935
+ let mut args = vec![Value::String(match_str.into())];
936
+ for i in 1..caps.len() {
937
+ let val = match caps.get(i) {
938
+ Some(m) => Value::String(m.as_str().into()),
939
+ None => Value::Null,
940
+ };
941
+ args.push(val);
942
+ }
943
+ args.push(Value::Number(char_index as f64));
944
+ args.push(Value::String(input.into()));
945
+
946
+ let repl_val = cb(&args);
947
+ let repl_str = repl_val.to_display_string();
948
+ result.push_str(&input[last_end..byte_start]);
949
+ result.push_str(&repl_str);
950
+ last_end = full.end();
951
+ }
952
+
953
+ result.push_str(&input[last_end..]);
954
+ Value::String(result.into())
955
+ } else {
956
+ let replacement_str = replacement.to_display_string();
957
+ if re_guard.flags.global {
958
+ match re_guard.regex.replace_all(input, replacement_str.as_str()) {
959
+ std::borrow::Cow::Borrowed(x) => Value::String(x.into()),
960
+ std::borrow::Cow::Owned(x) => Value::String(x.into()),
961
+ }
962
+ } else {
963
+ match re_guard.regex.replace(input, replacement_str.as_str()) {
964
+ std::borrow::Cow::Borrowed(x) => Value::String(x.into()),
965
+ std::borrow::Cow::Owned(x) => Value::String(x.into()),
966
+ }
967
+ }
968
+ }
969
+ }
970
+
971
+ #[cfg(feature = "regex")]
972
+ pub fn string_search_regex(s: &Value, regexp: &Value) -> Value {
973
+ let input = match s {
974
+ Value::String(s) => s.as_ref(),
975
+ _ => return Value::Number(-1.0),
976
+ };
977
+
978
+ match regexp {
979
+ Value::RegExp(re) => {
980
+ let re = re.borrow();
981
+ match re.regex.find(input) {
982
+ Ok(Some(m)) => {
983
+ let char_index = input[..m.start()].chars().count();
984
+ Value::Number(char_index as f64)
985
+ }
986
+ _ => Value::Number(-1.0),
987
+ }
988
+ }
989
+ Value::String(pattern) => {
990
+ match tish_core::TishRegExp::new(pattern, "") {
991
+ Ok(re) => match re.regex.find(input) {
992
+ Ok(Some(m)) => {
993
+ let char_index = input[..m.start()].chars().count();
994
+ Value::Number(char_index as f64)
995
+ }
996
+ _ => Value::Number(-1.0),
997
+ },
998
+ Err(_) => Value::Number(-1.0),
999
+ }
1000
+ }
1001
+ _ => Value::Number(-1.0),
1002
+ }
1003
+ }
1004
+