@tishlang/tish 1.5.0 → 1.7.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 +1 -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 +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +64 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +989 -318
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +21 -8
- package/crates/tish_compile/src/resolve.rs +515 -94
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- 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 +40 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +398 -141
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -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 +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +38 -15
- package/crates/tish_native/src/lib.rs +76 -32
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +320 -116
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -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
|
@@ -8,11 +8,14 @@ use std::path::{Path, PathBuf};
|
|
|
8
8
|
use std::rc::Rc;
|
|
9
9
|
use std::sync::Arc;
|
|
10
10
|
|
|
11
|
-
use tishlang_ast::{
|
|
11
|
+
use tishlang_ast::{
|
|
12
|
+
BinOp, CompoundOp, ExportDeclaration, Expr, FunParam, ImportSpecifier, Literal,
|
|
13
|
+
LogicalAssignOp, MemberProp, Span, Statement, UnaryOp,
|
|
14
|
+
};
|
|
12
15
|
|
|
13
|
-
use crate::value::{PropMap, Value};
|
|
14
16
|
#[cfg(any(feature = "fs", feature = "process"))]
|
|
15
17
|
use crate::natives;
|
|
18
|
+
use crate::value::{PropMap, Value};
|
|
16
19
|
|
|
17
20
|
struct Scope {
|
|
18
21
|
vars: PropMap,
|
|
@@ -93,12 +96,24 @@ impl Evaluator {
|
|
|
93
96
|
console.insert("log".into(), Value::Native(natives::console_log));
|
|
94
97
|
console.insert("warn".into(), Value::Native(natives::console_warn));
|
|
95
98
|
console.insert("error".into(), Value::Native(natives::console_error));
|
|
96
|
-
s.set(
|
|
99
|
+
s.set(
|
|
100
|
+
"console".into(),
|
|
101
|
+
Value::Object(Rc::new(RefCell::new(console))),
|
|
102
|
+
true,
|
|
103
|
+
);
|
|
97
104
|
s.set("parseInt".into(), Value::Native(natives::parse_int), true);
|
|
98
|
-
s.set(
|
|
105
|
+
s.set(
|
|
106
|
+
"parseFloat".into(),
|
|
107
|
+
Value::Native(natives::parse_float),
|
|
108
|
+
true,
|
|
109
|
+
);
|
|
99
110
|
s.set("decodeURI".into(), Value::Native(natives::decode_uri), true);
|
|
100
111
|
s.set("encodeURI".into(), Value::Native(natives::encode_uri), true);
|
|
101
|
-
s.set(
|
|
112
|
+
s.set(
|
|
113
|
+
"Boolean".into(),
|
|
114
|
+
Value::Native(natives::boolean_native),
|
|
115
|
+
true,
|
|
116
|
+
);
|
|
102
117
|
s.set("isFinite".into(), Value::Native(natives::is_finite), true);
|
|
103
118
|
s.set("isNaN".into(), Value::Native(natives::is_nan), true);
|
|
104
119
|
s.set("Infinity".into(), Value::Number(f64::INFINITY), true);
|
|
@@ -122,32 +137,65 @@ impl Evaluator {
|
|
|
122
137
|
math.insert("trunc".into(), Value::Native(natives::math_trunc));
|
|
123
138
|
math.insert("PI".into(), Value::Number(std::f64::consts::PI));
|
|
124
139
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
125
|
-
s.set(
|
|
140
|
+
s.set(
|
|
141
|
+
"Math".into(),
|
|
142
|
+
Value::Object(Rc::new(RefCell::new(math))),
|
|
143
|
+
true,
|
|
144
|
+
);
|
|
126
145
|
|
|
127
146
|
let mut json = PropMap::with_capacity(2);
|
|
128
147
|
json.insert("parse".into(), Value::Native(Self::json_parse_native));
|
|
129
|
-
json.insert(
|
|
130
|
-
|
|
148
|
+
json.insert(
|
|
149
|
+
"stringify".into(),
|
|
150
|
+
Value::Native(Self::json_stringify_native),
|
|
151
|
+
);
|
|
152
|
+
s.set(
|
|
153
|
+
"JSON".into(),
|
|
154
|
+
Value::Object(Rc::new(RefCell::new(json))),
|
|
155
|
+
true,
|
|
156
|
+
);
|
|
131
157
|
|
|
132
158
|
let mut object = PropMap::with_capacity(5);
|
|
133
159
|
object.insert("keys".into(), Value::Native(Self::object_keys));
|
|
134
160
|
object.insert("values".into(), Value::Native(Self::object_values));
|
|
135
161
|
object.insert("entries".into(), Value::Native(Self::object_entries));
|
|
136
162
|
object.insert("assign".into(), Value::Native(Self::object_assign));
|
|
137
|
-
object.insert(
|
|
138
|
-
|
|
163
|
+
object.insert(
|
|
164
|
+
"fromEntries".into(),
|
|
165
|
+
Value::Native(Self::object_from_entries),
|
|
166
|
+
);
|
|
167
|
+
s.set(
|
|
168
|
+
"Object".into(),
|
|
169
|
+
Value::Object(Rc::new(RefCell::new(object))),
|
|
170
|
+
true,
|
|
171
|
+
);
|
|
139
172
|
|
|
140
173
|
let mut array_obj = PropMap::with_capacity(1);
|
|
141
174
|
array_obj.insert("isArray".into(), Value::Native(natives::array_is_array));
|
|
142
|
-
s.set(
|
|
175
|
+
s.set(
|
|
176
|
+
"Array".into(),
|
|
177
|
+
Value::Object(Rc::new(RefCell::new(array_obj))),
|
|
178
|
+
true,
|
|
179
|
+
);
|
|
143
180
|
|
|
144
181
|
let mut string_obj = PropMap::with_capacity(1);
|
|
145
|
-
string_obj.insert(
|
|
146
|
-
|
|
182
|
+
string_obj.insert(
|
|
183
|
+
"fromCharCode".into(),
|
|
184
|
+
Value::Native(natives::string_from_char_code),
|
|
185
|
+
);
|
|
186
|
+
s.set(
|
|
187
|
+
"String".into(),
|
|
188
|
+
Value::Object(Rc::new(RefCell::new(string_obj))),
|
|
189
|
+
true,
|
|
190
|
+
);
|
|
147
191
|
|
|
148
192
|
let mut date = PropMap::with_capacity(1);
|
|
149
193
|
date.insert("now".into(), Value::Native(natives::date_now));
|
|
150
|
-
s.set(
|
|
194
|
+
s.set(
|
|
195
|
+
"Date".into(),
|
|
196
|
+
Value::Object(Rc::new(RefCell::new(date))),
|
|
197
|
+
true,
|
|
198
|
+
);
|
|
151
199
|
|
|
152
200
|
s.set(
|
|
153
201
|
"Uint8Array".into(),
|
|
@@ -166,7 +214,11 @@ impl Evaluator {
|
|
|
166
214
|
|
|
167
215
|
#[cfg(feature = "regex")]
|
|
168
216
|
{
|
|
169
|
-
s.set(
|
|
217
|
+
s.set(
|
|
218
|
+
"RegExp".into(),
|
|
219
|
+
Value::Native(Self::regexp_constructor_native),
|
|
220
|
+
true,
|
|
221
|
+
);
|
|
170
222
|
}
|
|
171
223
|
|
|
172
224
|
// fs, http, process: use import { x } from 'tish:fs' etc. No globals.
|
|
@@ -225,16 +277,28 @@ impl Evaluator {
|
|
|
225
277
|
self.scope = prev;
|
|
226
278
|
Ok(last)
|
|
227
279
|
}
|
|
228
|
-
Statement::VarDecl {
|
|
280
|
+
Statement::VarDecl {
|
|
281
|
+
name,
|
|
282
|
+
mutable,
|
|
283
|
+
init,
|
|
284
|
+
..
|
|
285
|
+
} => {
|
|
229
286
|
let value = init
|
|
230
287
|
.as_ref()
|
|
231
288
|
.map(|e| self.eval_expr(e))
|
|
232
289
|
.transpose()?
|
|
233
290
|
.unwrap_or(Value::Null);
|
|
234
|
-
self.scope
|
|
291
|
+
self.scope
|
|
292
|
+
.borrow_mut()
|
|
293
|
+
.set(Arc::clone(name), value, *mutable);
|
|
235
294
|
Ok(Value::Null)
|
|
236
295
|
}
|
|
237
|
-
Statement::VarDeclDestructure {
|
|
296
|
+
Statement::VarDeclDestructure {
|
|
297
|
+
pattern,
|
|
298
|
+
mutable,
|
|
299
|
+
init,
|
|
300
|
+
..
|
|
301
|
+
} => {
|
|
238
302
|
let value = self.eval_expr(init)?;
|
|
239
303
|
self.bind_destruct_pattern(pattern, &value, *mutable)?;
|
|
240
304
|
Ok(Value::Null)
|
|
@@ -269,15 +333,21 @@ impl Evaluator {
|
|
|
269
333
|
}
|
|
270
334
|
Ok(Value::Null)
|
|
271
335
|
}
|
|
272
|
-
Statement::ForOf {
|
|
336
|
+
Statement::ForOf {
|
|
337
|
+
name,
|
|
338
|
+
iterable,
|
|
339
|
+
body,
|
|
340
|
+
..
|
|
341
|
+
} => {
|
|
273
342
|
let iter_val = self.eval_expr(iterable)?;
|
|
274
343
|
let elements = match &iter_val {
|
|
275
|
-
crate::value::Value::Array(arr) =>
|
|
276
|
-
|
|
277
|
-
s.chars()
|
|
278
|
-
.map(|c| crate::value::Value::String(Arc::from(c.to_string())))
|
|
279
|
-
.collect::<Vec<_>>()
|
|
344
|
+
crate::value::Value::Array(arr) => {
|
|
345
|
+
arr.borrow().iter().cloned().collect::<Vec<_>>()
|
|
280
346
|
}
|
|
347
|
+
crate::value::Value::String(s) => s
|
|
348
|
+
.chars()
|
|
349
|
+
.map(|c| crate::value::Value::String(Arc::from(c.to_string())))
|
|
350
|
+
.collect::<Vec<_>>(),
|
|
281
351
|
_ => {
|
|
282
352
|
return Err(EvalError::Error(format!(
|
|
283
353
|
"for-of requires iterable (array or string), got {}",
|
|
@@ -360,7 +430,12 @@ impl Evaluator {
|
|
|
360
430
|
self.scope.borrow_mut().set(Arc::clone(name), func, true);
|
|
361
431
|
Ok(Value::Null)
|
|
362
432
|
}
|
|
363
|
-
Statement::Switch {
|
|
433
|
+
Statement::Switch {
|
|
434
|
+
expr,
|
|
435
|
+
cases,
|
|
436
|
+
default_body,
|
|
437
|
+
..
|
|
438
|
+
} => {
|
|
364
439
|
let v = self.eval_expr(expr)?;
|
|
365
440
|
let mut matched = false;
|
|
366
441
|
for (case_expr, body) in cases {
|
|
@@ -438,7 +513,7 @@ impl Evaluator {
|
|
|
438
513
|
..
|
|
439
514
|
} => {
|
|
440
515
|
let try_result = self.eval_statement(body);
|
|
441
|
-
|
|
516
|
+
|
|
442
517
|
let result = match try_result {
|
|
443
518
|
Ok(v) => Ok(v),
|
|
444
519
|
Err(EvalError::Throw(thrown)) => {
|
|
@@ -459,18 +534,24 @@ impl Evaluator {
|
|
|
459
534
|
}
|
|
460
535
|
Err(e) => Err(e),
|
|
461
536
|
};
|
|
462
|
-
|
|
537
|
+
|
|
463
538
|
if let Some(finally_stmt) = finally_body {
|
|
464
539
|
let _ = self.eval_statement(finally_stmt);
|
|
465
540
|
}
|
|
466
|
-
|
|
541
|
+
|
|
467
542
|
result
|
|
468
543
|
}
|
|
469
|
-
Statement::Import {
|
|
544
|
+
Statement::Import {
|
|
545
|
+
specifiers, from, ..
|
|
546
|
+
} => {
|
|
470
547
|
let exports_val = self.load_module(from)?;
|
|
471
548
|
let exports = match &exports_val {
|
|
472
549
|
Value::Object(m) => m.borrow().clone(),
|
|
473
|
-
_ =>
|
|
550
|
+
_ => {
|
|
551
|
+
return Err(EvalError::Error(
|
|
552
|
+
"Module exports must be object".to_string(),
|
|
553
|
+
))
|
|
554
|
+
}
|
|
474
555
|
};
|
|
475
556
|
let mut scope = self.scope.borrow_mut();
|
|
476
557
|
for spec in specifiers {
|
|
@@ -512,6 +593,12 @@ impl Evaluator {
|
|
|
512
593
|
|
|
513
594
|
/// Load and evaluate a module, returning its exports object. Uses cache.
|
|
514
595
|
fn load_module(&mut self, from: &str) -> Result<Value, EvalError> {
|
|
596
|
+
if from.starts_with("cargo:") {
|
|
597
|
+
return Err(EvalError::Error(
|
|
598
|
+
"cargo:… imports are only supported by `tish build` with the Rust native backend."
|
|
599
|
+
.into(),
|
|
600
|
+
));
|
|
601
|
+
}
|
|
515
602
|
if from.starts_with("tish:") {
|
|
516
603
|
return self.load_builtin_module(from);
|
|
517
604
|
}
|
|
@@ -520,24 +607,24 @@ impl Evaluator {
|
|
|
520
607
|
return self.load_builtin_module(from);
|
|
521
608
|
}
|
|
522
609
|
let dir = self.current_dir.borrow().clone().ok_or_else(|| {
|
|
523
|
-
EvalError::Error(
|
|
610
|
+
EvalError::Error(
|
|
611
|
+
"Cannot resolve imports: no current file directory (use run_file)".to_string(),
|
|
612
|
+
)
|
|
524
613
|
})?;
|
|
525
614
|
let path = Self::resolve_import_path(from, &dir)?;
|
|
526
|
-
let path = path
|
|
527
|
-
|
|
528
|
-
|
|
615
|
+
let path = path
|
|
616
|
+
.canonicalize()
|
|
617
|
+
.map_err(|e| EvalError::Error(format!("Cannot resolve import '{}': {}", from, e)))?;
|
|
529
618
|
{
|
|
530
619
|
let cache = self.module_cache.borrow();
|
|
531
620
|
if let Some(m) = cache.get(&path) {
|
|
532
621
|
return Ok(m.clone());
|
|
533
622
|
}
|
|
534
623
|
}
|
|
535
|
-
let source = std::fs::read_to_string(&path)
|
|
536
|
-
EvalError::Error(format!("Cannot read {}: {}", path.display(), e))
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
EvalError::Error(format!("Parse error in {}: {}", path.display(), e))
|
|
540
|
-
})?;
|
|
624
|
+
let source = std::fs::read_to_string(&path)
|
|
625
|
+
.map_err(|e| EvalError::Error(format!("Cannot read {}: {}", path.display(), e)))?;
|
|
626
|
+
let program = tishlang_parser::parse(&source)
|
|
627
|
+
.map_err(|e| EvalError::Error(format!("Parse error in {}: {}", path.display(), e)))?;
|
|
541
628
|
let module_scope = Scope::child(Rc::clone(&self.scope));
|
|
542
629
|
let prev_scope = std::mem::replace(&mut self.scope, Rc::clone(&module_scope));
|
|
543
630
|
let parent_dir = self.current_dir.borrow().clone();
|
|
@@ -549,7 +636,9 @@ impl Evaluator {
|
|
|
549
636
|
match declaration.as_ref() {
|
|
550
637
|
ExportDeclaration::Named(s) => {
|
|
551
638
|
let _ = self.eval_statement(s);
|
|
552
|
-
if let Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } =
|
|
639
|
+
if let Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } =
|
|
640
|
+
s.as_ref()
|
|
641
|
+
{
|
|
553
642
|
export_names.push(name.to_string());
|
|
554
643
|
}
|
|
555
644
|
}
|
|
@@ -572,7 +661,9 @@ impl Evaluator {
|
|
|
572
661
|
*self.current_dir.borrow_mut() = parent_dir;
|
|
573
662
|
self.scope = prev_scope;
|
|
574
663
|
let exports_val = Value::Object(Rc::new(RefCell::new(exports)));
|
|
575
|
-
self.module_cache
|
|
664
|
+
self.module_cache
|
|
665
|
+
.borrow_mut()
|
|
666
|
+
.insert(path, exports_val.clone());
|
|
576
667
|
Ok(exports_val)
|
|
577
668
|
}
|
|
578
669
|
|
|
@@ -599,6 +690,11 @@ impl Evaluator {
|
|
|
599
690
|
|
|
600
691
|
/// Load built-in module (tish:fs, tish:http, tish:process, …) or a virtual module from native crates.
|
|
601
692
|
fn load_builtin_module(&self, spec: &str) -> Result<Value, EvalError> {
|
|
693
|
+
if spec.starts_with("cargo:") {
|
|
694
|
+
return Err(EvalError::Error(
|
|
695
|
+
"cargo:… imports are only supported when compiling with `tish build` and the Rust native backend. They link Cargo crates via package.json tish.rustDependencies and a generated native wrapper — not the interpreter or VM.".into(),
|
|
696
|
+
));
|
|
697
|
+
}
|
|
602
698
|
if let Some(v) = self.virtual_builtins.borrow().get(spec) {
|
|
603
699
|
return Ok(v.clone());
|
|
604
700
|
}
|
|
@@ -630,10 +726,22 @@ impl Evaluator {
|
|
|
630
726
|
exports.insert("fetchAll".into(), Value::Native(Self::fetch_all_native));
|
|
631
727
|
exports.insert("serve".into(), Value::Serve);
|
|
632
728
|
exports.insert("Promise".into(), Value::PromiseConstructor);
|
|
633
|
-
exports.insert(
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
729
|
+
exports.insert(
|
|
730
|
+
"setTimeout".into(),
|
|
731
|
+
Value::TimerBuiltin(Arc::from("setTimeout")),
|
|
732
|
+
);
|
|
733
|
+
exports.insert(
|
|
734
|
+
"setInterval".into(),
|
|
735
|
+
Value::TimerBuiltin(Arc::from("setInterval")),
|
|
736
|
+
);
|
|
737
|
+
exports.insert(
|
|
738
|
+
"clearTimeout".into(),
|
|
739
|
+
Value::Native(Self::clear_timeout_native),
|
|
740
|
+
);
|
|
741
|
+
exports.insert(
|
|
742
|
+
"clearInterval".into(),
|
|
743
|
+
Value::Native(Self::clear_interval_native),
|
|
744
|
+
);
|
|
637
745
|
return Ok(Value::Object(Rc::new(RefCell::new(exports))));
|
|
638
746
|
}
|
|
639
747
|
#[cfg(not(feature = "http"))]
|
|
@@ -647,10 +755,16 @@ impl Evaluator {
|
|
|
647
755
|
#[cfg(feature = "ws")]
|
|
648
756
|
{
|
|
649
757
|
let mut exports: PropMap = PropMap::default();
|
|
650
|
-
exports.insert(
|
|
758
|
+
exports.insert(
|
|
759
|
+
"WebSocket".into(),
|
|
760
|
+
Value::Native(Self::ws_web_socket_native),
|
|
761
|
+
);
|
|
651
762
|
exports.insert("Server".into(), Value::Native(Self::ws_server_native));
|
|
652
763
|
exports.insert("wsSend".into(), Value::Native(Self::ws_send_native));
|
|
653
|
-
exports.insert(
|
|
764
|
+
exports.insert(
|
|
765
|
+
"wsBroadcast".into(),
|
|
766
|
+
Value::Native(Self::ws_broadcast_native),
|
|
767
|
+
);
|
|
654
768
|
return Ok(Value::Object(Rc::new(RefCell::new(exports))));
|
|
655
769
|
}
|
|
656
770
|
#[cfg(not(feature = "ws"))]
|
|
@@ -667,21 +781,29 @@ impl Evaluator {
|
|
|
667
781
|
exports.insert("exit".into(), Value::Native(natives::process_exit));
|
|
668
782
|
exports.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
669
783
|
exports.insert("exec".into(), Value::Native(natives::process_exec));
|
|
670
|
-
let argv: Vec<Value> =
|
|
671
|
-
.map(|s| Value::String(s.into()))
|
|
672
|
-
|
|
673
|
-
|
|
784
|
+
let argv: Vec<Value> =
|
|
785
|
+
std::env::args().map(|s| Value::String(s.into())).collect();
|
|
786
|
+
exports.insert(
|
|
787
|
+
"argv".into(),
|
|
788
|
+
Value::Array(Rc::new(RefCell::new(argv.clone()))),
|
|
789
|
+
);
|
|
674
790
|
let env_obj: PropMap = std::env::vars()
|
|
675
791
|
.map(|(key, value)| (Arc::from(key.as_str()), Value::String(value.into())))
|
|
676
792
|
.collect();
|
|
677
|
-
exports.insert(
|
|
793
|
+
exports.insert(
|
|
794
|
+
"env".into(),
|
|
795
|
+
Value::Object(Rc::new(RefCell::new(env_obj.clone()))),
|
|
796
|
+
);
|
|
678
797
|
let mut process_obj = PropMap::default();
|
|
679
798
|
process_obj.insert("exit".into(), Value::Native(natives::process_exit));
|
|
680
799
|
process_obj.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
681
800
|
process_obj.insert("exec".into(), Value::Native(natives::process_exec));
|
|
682
801
|
process_obj.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv))));
|
|
683
802
|
process_obj.insert("env".into(), Value::Object(Rc::new(RefCell::new(env_obj))));
|
|
684
|
-
exports.insert(
|
|
803
|
+
exports.insert(
|
|
804
|
+
"process".into(),
|
|
805
|
+
Value::Object(Rc::new(RefCell::new(process_obj))),
|
|
806
|
+
);
|
|
685
807
|
return Ok(Value::Object(Rc::new(RefCell::new(exports))));
|
|
686
808
|
}
|
|
687
809
|
#[cfg(not(feature = "process"))]
|
|
@@ -1902,7 +2024,13 @@ impl Evaluator {
|
|
|
1902
2024
|
/// descending = false: checks for `(a, b) => a - b`
|
|
1903
2025
|
/// descending = true: checks for `(a, b) => b - a`
|
|
1904
2026
|
fn is_numeric_sort_comparator(f: &Value, descending: bool) -> bool {
|
|
1905
|
-
if let Value::Function {
|
|
2027
|
+
if let Value::Function {
|
|
2028
|
+
formals,
|
|
2029
|
+
body,
|
|
2030
|
+
rest_param,
|
|
2031
|
+
..
|
|
2032
|
+
} = f
|
|
2033
|
+
{
|
|
1906
2034
|
// Must have exactly 2 simple params, no defaults, no rest
|
|
1907
2035
|
if formals.len() != 2 || rest_param.is_some() {
|
|
1908
2036
|
return false;
|
|
@@ -1926,15 +2054,29 @@ impl Evaluator {
|
|
|
1926
2054
|
};
|
|
1927
2055
|
|
|
1928
2056
|
// Check for binary subtraction
|
|
1929
|
-
if let Expr::Binary {
|
|
2057
|
+
if let Expr::Binary {
|
|
2058
|
+
left,
|
|
2059
|
+
op: BinOp::Sub,
|
|
2060
|
+
right,
|
|
2061
|
+
..
|
|
2062
|
+
} = expr
|
|
2063
|
+
{
|
|
1930
2064
|
// Check left is Ident(a) and right is Ident(b)
|
|
1931
2065
|
let (expected_left, expected_right) = if descending {
|
|
1932
|
-
(param_b, param_a)
|
|
2066
|
+
(param_b, param_a) // b - a
|
|
1933
2067
|
} else {
|
|
1934
|
-
(param_a, param_b)
|
|
2068
|
+
(param_a, param_b) // a - b
|
|
1935
2069
|
};
|
|
1936
2070
|
|
|
1937
|
-
if let (
|
|
2071
|
+
if let (
|
|
2072
|
+
Expr::Ident {
|
|
2073
|
+
name: left_name, ..
|
|
2074
|
+
},
|
|
2075
|
+
Expr::Ident {
|
|
2076
|
+
name: right_name, ..
|
|
2077
|
+
},
|
|
2078
|
+
) = (left.as_ref(), right.as_ref())
|
|
2079
|
+
{
|
|
1938
2080
|
return left_name == expected_left && right_name == expected_right;
|
|
1939
2081
|
}
|
|
1940
2082
|
}
|
|
@@ -1989,8 +2131,17 @@ impl Evaluator {
|
|
|
1989
2131
|
|
|
1990
2132
|
/// Optimized callback invocation for array methods.
|
|
1991
2133
|
/// Creates a reusable scope that can be updated for each iteration.
|
|
1992
|
-
fn create_callback_scope(
|
|
1993
|
-
|
|
2134
|
+
fn create_callback_scope(
|
|
2135
|
+
&self,
|
|
2136
|
+
f: &Value,
|
|
2137
|
+
) -> Option<(Rc<RefCell<Scope>>, Arc<[Arc<str>]>, Arc<Statement>)> {
|
|
2138
|
+
if let Value::Function {
|
|
2139
|
+
formals,
|
|
2140
|
+
body,
|
|
2141
|
+
rest_param,
|
|
2142
|
+
..
|
|
2143
|
+
} = f
|
|
2144
|
+
{
|
|
1994
2145
|
if rest_param.is_some() {
|
|
1995
2146
|
return None;
|
|
1996
2147
|
}
|
|
@@ -2053,12 +2204,14 @@ impl Evaluator {
|
|
|
2053
2204
|
|
|
2054
2205
|
/// Try to evaluate a simple callback expression directly without creating a scope.
|
|
2055
2206
|
/// Returns Some(result) for simple patterns like `x => x * 2` or `x => x > 5`.
|
|
2056
|
-
fn eval_simple_callback(
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2207
|
+
fn eval_simple_callback(&self, f: &Value, args: &[Value]) -> Option<Result<Value, EvalError>> {
|
|
2208
|
+
if let Value::Function {
|
|
2209
|
+
formals,
|
|
2210
|
+
body,
|
|
2211
|
+
rest_param,
|
|
2212
|
+
..
|
|
2213
|
+
} = f
|
|
2214
|
+
{
|
|
2062
2215
|
if formals.len() != 1 || rest_param.is_some() {
|
|
2063
2216
|
return None;
|
|
2064
2217
|
}
|
|
@@ -2078,17 +2231,25 @@ impl Evaluator {
|
|
|
2078
2231
|
// Fast path for common patterns
|
|
2079
2232
|
match expr {
|
|
2080
2233
|
// x * constant or x + constant, etc.
|
|
2081
|
-
Expr::Binary {
|
|
2234
|
+
Expr::Binary {
|
|
2235
|
+
left, op, right, ..
|
|
2236
|
+
} => {
|
|
2082
2237
|
let left_val = self.eval_simple_operand(left, param_name, &arg)?;
|
|
2083
2238
|
let right_val = self.eval_simple_operand(right, param_name, &arg)?;
|
|
2084
|
-
Some(
|
|
2239
|
+
Some(
|
|
2240
|
+
self.eval_binop(&left_val, *op, &right_val)
|
|
2241
|
+
.map_err(EvalError::Error),
|
|
2242
|
+
)
|
|
2085
2243
|
}
|
|
2086
2244
|
// Just return the parameter
|
|
2087
|
-
Expr::Ident { name, .. } if name == param_name =>
|
|
2088
|
-
Some(Ok(arg))
|
|
2089
|
-
}
|
|
2245
|
+
Expr::Ident { name, .. } if name == param_name => Some(Ok(arg)),
|
|
2090
2246
|
// Property access: x.prop
|
|
2091
|
-
Expr::Member {
|
|
2247
|
+
Expr::Member {
|
|
2248
|
+
object,
|
|
2249
|
+
prop,
|
|
2250
|
+
optional,
|
|
2251
|
+
..
|
|
2252
|
+
} => {
|
|
2092
2253
|
if let Expr::Ident { name, .. } = object.as_ref() {
|
|
2093
2254
|
if name == param_name {
|
|
2094
2255
|
return self.eval_simple_member(&arg, prop, *optional);
|
|
@@ -2104,7 +2265,12 @@ impl Evaluator {
|
|
|
2104
2265
|
}
|
|
2105
2266
|
|
|
2106
2267
|
/// Evaluate a simple operand (identifier or literal).
|
|
2107
|
-
fn eval_simple_operand(
|
|
2268
|
+
fn eval_simple_operand(
|
|
2269
|
+
&self,
|
|
2270
|
+
expr: &Expr,
|
|
2271
|
+
param_name: &Arc<str>,
|
|
2272
|
+
param_val: &Value,
|
|
2273
|
+
) -> Option<Value> {
|
|
2108
2274
|
match expr {
|
|
2109
2275
|
Expr::Ident { name, .. } if name == param_name => Some(param_val.clone()),
|
|
2110
2276
|
Expr::Literal { value, .. } => match value {
|
|
@@ -2118,20 +2284,27 @@ impl Evaluator {
|
|
|
2118
2284
|
}
|
|
2119
2285
|
|
|
2120
2286
|
/// Evaluate simple member access.
|
|
2121
|
-
fn eval_simple_member(
|
|
2287
|
+
fn eval_simple_member(
|
|
2288
|
+
&self,
|
|
2289
|
+
obj: &Value,
|
|
2290
|
+
property: &MemberProp,
|
|
2291
|
+
_optional: bool,
|
|
2292
|
+
) -> Option<Result<Value, EvalError>> {
|
|
2122
2293
|
match property {
|
|
2123
|
-
MemberProp::Name(name) => {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2294
|
+
MemberProp::Name(name) => match obj {
|
|
2295
|
+
Value::Object(o) => {
|
|
2296
|
+
let result = o
|
|
2297
|
+
.borrow()
|
|
2298
|
+
.get(name.as_ref())
|
|
2299
|
+
.cloned()
|
|
2300
|
+
.unwrap_or(Value::Null);
|
|
2301
|
+
Some(Ok(result))
|
|
2302
|
+
}
|
|
2303
|
+
Value::Array(arr) if name.as_ref() == "length" => {
|
|
2304
|
+
Some(Ok(Value::Number(arr.borrow().len() as f64)))
|
|
2133
2305
|
}
|
|
2134
|
-
|
|
2306
|
+
_ => None,
|
|
2307
|
+
},
|
|
2135
2308
|
_ => None,
|
|
2136
2309
|
}
|
|
2137
2310
|
}
|
|
@@ -2159,9 +2332,7 @@ impl Evaluator {
|
|
|
2159
2332
|
|
|
2160
2333
|
fn call_func(&self, f: &Value, args: &[Value]) -> Result<Value, EvalError> {
|
|
2161
2334
|
match f {
|
|
2162
|
-
Value::Native(native_fn) =>
|
|
2163
|
-
native_fn(args).map_err(EvalError::Error)
|
|
2164
|
-
}
|
|
2335
|
+
Value::Native(native_fn) => native_fn(args).map_err(EvalError::Error),
|
|
2165
2336
|
#[cfg(feature = "http")]
|
|
2166
2337
|
Value::PromiseResolver(r) => {
|
|
2167
2338
|
let value = args.first().cloned().unwrap_or(Value::Null);
|
|
@@ -2170,7 +2341,12 @@ impl Evaluator {
|
|
|
2170
2341
|
.map_err(EvalError::Error)?;
|
|
2171
2342
|
for reaction in reactions {
|
|
2172
2343
|
match reaction {
|
|
2173
|
-
crate::promise::Reaction::Then(
|
|
2344
|
+
crate::promise::Reaction::Then(
|
|
2345
|
+
on_fulfilled,
|
|
2346
|
+
on_rejected,
|
|
2347
|
+
ref resolve,
|
|
2348
|
+
ref reject,
|
|
2349
|
+
) => {
|
|
2174
2350
|
let handler_result = if is_fulfilled {
|
|
2175
2351
|
if let Some(ref h) = on_fulfilled {
|
|
2176
2352
|
self.call_func(h, &[val.clone()])
|
|
@@ -2222,8 +2398,10 @@ impl Evaluator {
|
|
|
2222
2398
|
#[cfg(feature = "http")]
|
|
2223
2399
|
Value::Serve => self.run_http_server(args),
|
|
2224
2400
|
Value::CoreFn(f) => {
|
|
2225
|
-
let ca: Result<Vec<tishlang_core::Value>, String> =
|
|
2226
|
-
|
|
2401
|
+
let ca: Result<Vec<tishlang_core::Value>, String> = args
|
|
2402
|
+
.iter()
|
|
2403
|
+
.map(crate::value_convert::eval_to_core)
|
|
2404
|
+
.collect();
|
|
2227
2405
|
let ca = ca.map_err(EvalError::Error)?;
|
|
2228
2406
|
Ok(crate::value_convert::core_to_eval(f(&ca)))
|
|
2229
2407
|
}
|
|
@@ -2237,15 +2415,25 @@ impl Evaluator {
|
|
|
2237
2415
|
Value::TimerBuiltin(name) => self.run_timer_builtin(name.as_ref(), args),
|
|
2238
2416
|
Value::OpaqueMethod(opaque, method_name) => {
|
|
2239
2417
|
let method = opaque.get_method(method_name.as_ref()).ok_or_else(|| {
|
|
2240
|
-
EvalError::Error(format!(
|
|
2418
|
+
EvalError::Error(format!(
|
|
2419
|
+
"Method {} not found on {}",
|
|
2420
|
+
method_name,
|
|
2421
|
+
opaque.type_name()
|
|
2422
|
+
))
|
|
2241
2423
|
})?;
|
|
2242
|
-
let core_args: Result<Vec<tishlang_core::Value>, String> =
|
|
2243
|
-
|
|
2424
|
+
let core_args: Result<Vec<tishlang_core::Value>, String> = args
|
|
2425
|
+
.iter()
|
|
2426
|
+
.map(crate::value_convert::eval_to_core)
|
|
2427
|
+
.collect();
|
|
2244
2428
|
let core_args = core_args.map_err(EvalError::Error)?;
|
|
2245
2429
|
let result = method(&core_args);
|
|
2246
2430
|
Ok(crate::value_convert::core_to_eval(result))
|
|
2247
2431
|
}
|
|
2248
|
-
Value::Function {
|
|
2432
|
+
Value::Function {
|
|
2433
|
+
formals,
|
|
2434
|
+
rest_param,
|
|
2435
|
+
body,
|
|
2436
|
+
} => {
|
|
2249
2437
|
let scope = Scope::child(Rc::clone(&self.scope));
|
|
2250
2438
|
{
|
|
2251
2439
|
let mut s = scope.borrow_mut();
|
|
@@ -2279,8 +2467,13 @@ impl Evaluator {
|
|
|
2279
2467
|
}
|
|
2280
2468
|
}
|
|
2281
2469
|
if let Some(ref rest_name) = rest_param {
|
|
2282
|
-
let rest_vals: Vec<Value> =
|
|
2283
|
-
|
|
2470
|
+
let rest_vals: Vec<Value> =
|
|
2471
|
+
args.iter().skip(formals.len()).cloned().collect();
|
|
2472
|
+
s.set(
|
|
2473
|
+
Arc::clone(rest_name),
|
|
2474
|
+
Value::Array(Rc::new(RefCell::new(rest_vals))),
|
|
2475
|
+
true,
|
|
2476
|
+
);
|
|
2284
2477
|
}
|
|
2285
2478
|
}
|
|
2286
2479
|
let mut eval = Evaluator {
|
|
@@ -2294,8 +2487,12 @@ impl Evaluator {
|
|
|
2294
2487
|
Err(EvalError::Return(v)) => Ok(v),
|
|
2295
2488
|
Err(EvalError::Throw(v)) => Err(EvalError::Throw(v)),
|
|
2296
2489
|
Err(EvalError::Error(s)) => Err(EvalError::Error(s)),
|
|
2297
|
-
Err(EvalError::Break) =>
|
|
2298
|
-
|
|
2490
|
+
Err(EvalError::Break) => {
|
|
2491
|
+
Err(EvalError::Error("break outside loop".to_string()))
|
|
2492
|
+
}
|
|
2493
|
+
Err(EvalError::Continue) => {
|
|
2494
|
+
Err(EvalError::Error("continue outside loop".to_string()))
|
|
2495
|
+
}
|
|
2299
2496
|
}
|
|
2300
2497
|
}
|
|
2301
2498
|
_ => Err(EvalError::Error("Not a function".to_string())),
|
|
@@ -2310,14 +2507,15 @@ impl Evaluator {
|
|
|
2310
2507
|
args: &[Value],
|
|
2311
2508
|
) -> Result<Value, EvalError> {
|
|
2312
2509
|
match method {
|
|
2313
|
-
"then" =>
|
|
2314
|
-
promise_ref,
|
|
2315
|
-
|
|
2316
|
-
args.get(1).cloned(),
|
|
2317
|
-
),
|
|
2510
|
+
"then" => {
|
|
2511
|
+
self.run_promise_then_core(promise_ref, args.first().cloned(), args.get(1).cloned())
|
|
2512
|
+
}
|
|
2318
2513
|
"catch" => self.run_promise_then_core(promise_ref, None, args.first().cloned()),
|
|
2319
2514
|
"finally" => self.run_promise_finally(promise_ref, args.first().cloned()),
|
|
2320
|
-
_ => Err(EvalError::Error(format!(
|
|
2515
|
+
_ => Err(EvalError::Error(format!(
|
|
2516
|
+
"Unknown promise method: {}",
|
|
2517
|
+
method
|
|
2518
|
+
))),
|
|
2321
2519
|
}
|
|
2322
2520
|
}
|
|
2323
2521
|
|
|
@@ -2418,7 +2616,12 @@ impl Evaluator {
|
|
|
2418
2616
|
crate::promise::PromiseState::Pending { .. } => {
|
|
2419
2617
|
crate::promise::add_reaction(
|
|
2420
2618
|
state,
|
|
2421
|
-
crate::promise::Reaction::Then(
|
|
2619
|
+
crate::promise::Reaction::Then(
|
|
2620
|
+
on_fulfilled,
|
|
2621
|
+
on_rejected,
|
|
2622
|
+
resolve.clone(),
|
|
2623
|
+
reject.clone(),
|
|
2624
|
+
),
|
|
2422
2625
|
);
|
|
2423
2626
|
}
|
|
2424
2627
|
}
|
|
@@ -2527,8 +2730,11 @@ impl Evaluator {
|
|
|
2527
2730
|
let port = port;
|
|
2528
2731
|
std::thread::spawn(move || {
|
|
2529
2732
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
2530
|
-
if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port))
|
|
2531
|
-
|
|
2733
|
+
if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port))
|
|
2734
|
+
{
|
|
2735
|
+
let _ = stream.write_all(
|
|
2736
|
+
b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n",
|
|
2737
|
+
);
|
|
2532
2738
|
let _ = stream.shutdown(std::net::Shutdown::Write);
|
|
2533
2739
|
}
|
|
2534
2740
|
});
|
|
@@ -2593,7 +2799,11 @@ impl Evaluator {
|
|
|
2593
2799
|
tishlang_ast::DestructPattern::Array(elements) => {
|
|
2594
2800
|
let arr = match value {
|
|
2595
2801
|
Value::Array(a) => a.borrow().clone(),
|
|
2596
|
-
_ =>
|
|
2802
|
+
_ => {
|
|
2803
|
+
return Err(EvalError::Error(
|
|
2804
|
+
"Cannot destructure non-array value".to_string(),
|
|
2805
|
+
))
|
|
2806
|
+
}
|
|
2597
2807
|
};
|
|
2598
2808
|
|
|
2599
2809
|
for (i, elem) in elements.iter().enumerate() {
|
|
@@ -2623,7 +2833,11 @@ impl Evaluator {
|
|
|
2623
2833
|
tishlang_ast::DestructPattern::Object(props) => {
|
|
2624
2834
|
let obj = match value {
|
|
2625
2835
|
Value::Object(o) => o.borrow().clone(),
|
|
2626
|
-
_ =>
|
|
2836
|
+
_ => {
|
|
2837
|
+
return Err(EvalError::Error(
|
|
2838
|
+
"Cannot destructure non-object value".to_string(),
|
|
2839
|
+
))
|
|
2840
|
+
}
|
|
2627
2841
|
};
|
|
2628
2842
|
|
|
2629
2843
|
for prop in props {
|
|
@@ -2647,7 +2861,12 @@ impl Evaluator {
|
|
|
2647
2861
|
Ok(())
|
|
2648
2862
|
}
|
|
2649
2863
|
|
|
2650
|
-
fn bind_destruct_pattern(
|
|
2864
|
+
fn bind_destruct_pattern(
|
|
2865
|
+
&mut self,
|
|
2866
|
+
pattern: &tishlang_ast::DestructPattern,
|
|
2867
|
+
value: &Value,
|
|
2868
|
+
mutable: bool,
|
|
2869
|
+
) -> Result<(), EvalError> {
|
|
2651
2870
|
Self::bind_destruct_pattern_scoped(&self.scope, pattern, value, mutable)
|
|
2652
2871
|
}
|
|
2653
2872
|
|
|
@@ -2665,11 +2884,8 @@ impl Evaluator {
|
|
|
2665
2884
|
Some(Value::Bool(b)) => tishlang_core::Value::Bool(*b),
|
|
2666
2885
|
Some(_) => tishlang_core::Value::Number(0.0),
|
|
2667
2886
|
};
|
|
2668
|
-
let out =
|
|
2669
|
-
receiver.as_ref(),
|
|
2670
|
-
search,
|
|
2671
|
-
&position_core,
|
|
2672
|
-
);
|
|
2887
|
+
let out =
|
|
2888
|
+
tishlang_builtins::string::last_index_of_str(receiver.as_ref(), search, &position_core);
|
|
2673
2889
|
match out {
|
|
2674
2890
|
tishlang_core::Value::Number(n) => Value::Number(n),
|
|
2675
2891
|
_ => Value::Number(-1.0),
|
|
@@ -2953,7 +3169,9 @@ impl Evaluator {
|
|
|
2953
3169
|
Ok((Value::Bool(false), rest))
|
|
2954
3170
|
} else {
|
|
2955
3171
|
let end = s
|
|
2956
|
-
.find(|c: char|
|
|
3172
|
+
.find(|c: char| {
|
|
3173
|
+
!c.is_ascii_digit() && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E'
|
|
3174
|
+
})
|
|
2957
3175
|
.unwrap_or(s.len());
|
|
2958
3176
|
let num_str = &s[..end];
|
|
2959
3177
|
let n: f64 = num_str.parse().map_err(|_| ())?;
|
|
@@ -2981,7 +3199,11 @@ impl Evaluator {
|
|
|
2981
3199
|
.replace('\t', "\\t")
|
|
2982
3200
|
),
|
|
2983
3201
|
Value::Array(arr) => {
|
|
2984
|
-
let inner: Vec<String> = arr
|
|
3202
|
+
let inner: Vec<String> = arr
|
|
3203
|
+
.borrow()
|
|
3204
|
+
.iter()
|
|
3205
|
+
.map(Self::json_stringify_value)
|
|
3206
|
+
.collect();
|
|
2985
3207
|
format!("[{}]", inner.join(","))
|
|
2986
3208
|
}
|
|
2987
3209
|
Value::Object(map) => {
|
|
@@ -3000,7 +3222,14 @@ impl Evaluator {
|
|
|
3000
3222
|
})
|
|
3001
3223
|
.collect();
|
|
3002
3224
|
entries.sort_by(|a, b| a.0.cmp(&b.0));
|
|
3003
|
-
format!(
|
|
3225
|
+
format!(
|
|
3226
|
+
"{{{}}}",
|
|
3227
|
+
entries
|
|
3228
|
+
.into_iter()
|
|
3229
|
+
.map(|(_, s)| s)
|
|
3230
|
+
.collect::<Vec<_>>()
|
|
3231
|
+
.join(",")
|
|
3232
|
+
)
|
|
3004
3233
|
}
|
|
3005
3234
|
Value::Function { .. } | Value::Native(_) => "null".to_string(),
|
|
3006
3235
|
#[cfg(feature = "http")]
|
|
@@ -3011,7 +3240,8 @@ impl Evaluator {
|
|
|
3011
3240
|
| Value::Promise(_)
|
|
3012
3241
|
| Value::PromiseResolver(_)
|
|
3013
3242
|
| Value::PromiseConstructor
|
|
3014
|
-
| Value::BoundPromiseMethod(_, _)
|
|
3243
|
+
| Value::BoundPromiseMethod(_, _)
|
|
3244
|
+
| Value::TimerBuiltin(_) => "null".to_string(),
|
|
3015
3245
|
#[cfg(feature = "regex")]
|
|
3016
3246
|
Value::RegExp(_) => "null".to_string(),
|
|
3017
3247
|
Value::Opaque(_) | Value::OpaqueMethod(_, _) => "null".to_string(),
|
|
@@ -3031,7 +3261,11 @@ impl Evaluator {
|
|
|
3031
3261
|
|
|
3032
3262
|
fn object_keys(args: &[Value]) -> Result<Value, String> {
|
|
3033
3263
|
if let Some(Value::Object(obj)) = args.first() {
|
|
3034
|
-
let keys: Vec<Value> = obj
|
|
3264
|
+
let keys: Vec<Value> = obj
|
|
3265
|
+
.borrow()
|
|
3266
|
+
.keys()
|
|
3267
|
+
.map(|k| Value::String(Arc::clone(k)))
|
|
3268
|
+
.collect();
|
|
3035
3269
|
Ok(Value::Array(Rc::new(RefCell::new(keys))))
|
|
3036
3270
|
} else {
|
|
3037
3271
|
Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
|
|
@@ -3049,12 +3283,16 @@ impl Evaluator {
|
|
|
3049
3283
|
|
|
3050
3284
|
fn object_entries(args: &[Value]) -> Result<Value, String> {
|
|
3051
3285
|
if let Some(Value::Object(obj)) = args.first() {
|
|
3052
|
-
let entries: Vec<Value> = obj
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3286
|
+
let entries: Vec<Value> = obj
|
|
3287
|
+
.borrow()
|
|
3288
|
+
.iter()
|
|
3289
|
+
.map(|(k, v)| {
|
|
3290
|
+
Value::Array(Rc::new(RefCell::new(vec![
|
|
3291
|
+
Value::String(Arc::clone(k)),
|
|
3292
|
+
v.clone(),
|
|
3293
|
+
])))
|
|
3294
|
+
})
|
|
3295
|
+
.collect();
|
|
3058
3296
|
Ok(Value::Array(Rc::new(RefCell::new(entries))))
|
|
3059
3297
|
} else {
|
|
3060
3298
|
Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
|
|
@@ -3126,7 +3364,10 @@ impl Evaluator {
|
|
|
3126
3364
|
.ok_or_else(|| "Promise.all requires an iterable".to_string())?;
|
|
3127
3365
|
let values: Vec<Value> = match iterable {
|
|
3128
3366
|
Value::Array(arr) => arr.borrow().clone(),
|
|
3129
|
-
Value::String(s) => s
|
|
3367
|
+
Value::String(s) => s
|
|
3368
|
+
.chars()
|
|
3369
|
+
.map(|c| Value::String(c.to_string().into()))
|
|
3370
|
+
.collect(),
|
|
3130
3371
|
_ => return Err("Promise.all requires array or iterable".to_string()),
|
|
3131
3372
|
};
|
|
3132
3373
|
let mut results = Vec::with_capacity(values.len());
|
|
@@ -3136,7 +3377,8 @@ impl Evaluator {
|
|
|
3136
3377
|
crate::promise::PromiseAwaitResult::Fulfilled(x) => results.push(x),
|
|
3137
3378
|
crate::promise::PromiseAwaitResult::Rejected(x) => {
|
|
3138
3379
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3139
|
-
let (_, reject) =
|
|
3380
|
+
let (_, reject) =
|
|
3381
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3140
3382
|
let _ = crate::promise::settle_promise(&reject, x, false);
|
|
3141
3383
|
return Ok(promise);
|
|
3142
3384
|
}
|
|
@@ -3147,7 +3389,8 @@ impl Evaluator {
|
|
|
3147
3389
|
Ok(x) => results.push(crate::value_convert::core_to_eval(x)),
|
|
3148
3390
|
Err(x) => {
|
|
3149
3391
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3150
|
-
let (_, reject) =
|
|
3392
|
+
let (_, reject) =
|
|
3393
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3151
3394
|
let _ = crate::promise::settle_promise(
|
|
3152
3395
|
&reject,
|
|
3153
3396
|
crate::value_convert::core_to_eval(x),
|
|
@@ -3174,7 +3417,10 @@ impl Evaluator {
|
|
|
3174
3417
|
.ok_or_else(|| "Promise.race requires an iterable".to_string())?;
|
|
3175
3418
|
let values: Vec<Value> = match iterable {
|
|
3176
3419
|
Value::Array(arr) => arr.borrow().clone(),
|
|
3177
|
-
Value::String(s) => s
|
|
3420
|
+
Value::String(s) => s
|
|
3421
|
+
.chars()
|
|
3422
|
+
.map(|c| Value::String(c.to_string().into()))
|
|
3423
|
+
.collect(),
|
|
3178
3424
|
_ => return Err("Promise.race requires array or iterable".to_string()),
|
|
3179
3425
|
};
|
|
3180
3426
|
for v in values {
|
|
@@ -3182,7 +3428,8 @@ impl Evaluator {
|
|
|
3182
3428
|
match p.block_until_settled() {
|
|
3183
3429
|
Ok(x) => {
|
|
3184
3430
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3185
|
-
let (resolve, _) =
|
|
3431
|
+
let (resolve, _) =
|
|
3432
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3186
3433
|
crate::promise::settle_promise(
|
|
3187
3434
|
&resolve,
|
|
3188
3435
|
crate::value_convert::core_to_eval(x),
|
|
@@ -3192,7 +3439,8 @@ impl Evaluator {
|
|
|
3192
3439
|
}
|
|
3193
3440
|
Err(x) => {
|
|
3194
3441
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3195
|
-
let (_, reject) =
|
|
3442
|
+
let (_, reject) =
|
|
3443
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3196
3444
|
crate::promise::settle_promise(
|
|
3197
3445
|
&reject,
|
|
3198
3446
|
crate::value_convert::core_to_eval(x),
|
|
@@ -3206,13 +3454,15 @@ impl Evaluator {
|
|
|
3206
3454
|
match crate::promise::block_until_settled(p) {
|
|
3207
3455
|
crate::promise::PromiseAwaitResult::Fulfilled(x) => {
|
|
3208
3456
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3209
|
-
let (resolve, _) =
|
|
3457
|
+
let (resolve, _) =
|
|
3458
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3210
3459
|
crate::promise::settle_promise(&resolve, x, true)?;
|
|
3211
3460
|
return Ok(promise);
|
|
3212
3461
|
}
|
|
3213
3462
|
crate::promise::PromiseAwaitResult::Rejected(x) => {
|
|
3214
3463
|
let (promise, resolve_val, reject_val) = crate::promise::create_promise();
|
|
3215
|
-
let (_, reject) =
|
|
3464
|
+
let (_, reject) =
|
|
3465
|
+
crate::promise::extract_resolvers(&resolve_val, &reject_val);
|
|
3216
3466
|
crate::promise::settle_promise(&reject, x, false)?;
|
|
3217
3467
|
return Ok(promise);
|
|
3218
3468
|
}
|
|
@@ -3229,7 +3479,9 @@ impl Evaluator {
|
|
|
3229
3479
|
for a in args {
|
|
3230
3480
|
cv.push(crate::value_convert::eval_to_core(a)?);
|
|
3231
3481
|
}
|
|
3232
|
-
Ok(crate::value_convert::core_to_eval(
|
|
3482
|
+
Ok(crate::value_convert::core_to_eval(
|
|
3483
|
+
tishlang_runtime::web_socket_client(&cv),
|
|
3484
|
+
))
|
|
3233
3485
|
}
|
|
3234
3486
|
|
|
3235
3487
|
#[cfg(feature = "ws")]
|
|
@@ -3238,7 +3490,9 @@ impl Evaluator {
|
|
|
3238
3490
|
for a in args {
|
|
3239
3491
|
cv.push(crate::value_convert::eval_to_core(a)?);
|
|
3240
3492
|
}
|
|
3241
|
-
Ok(crate::value_convert::core_to_eval(
|
|
3493
|
+
Ok(crate::value_convert::core_to_eval(
|
|
3494
|
+
tishlang_runtime::web_socket_server_construct(&cv),
|
|
3495
|
+
))
|
|
3242
3496
|
}
|
|
3243
3497
|
|
|
3244
3498
|
#[cfg(feature = "ws")]
|
|
@@ -3246,7 +3500,9 @@ impl Evaluator {
|
|
|
3246
3500
|
let conn = args.first().ok_or("wsSend(conn, data) requires conn")?;
|
|
3247
3501
|
let conn_core = crate::value_convert::eval_to_core(conn)?;
|
|
3248
3502
|
let data = args.get(1).map(|v| v.to_string()).unwrap_or_default();
|
|
3249
|
-
Ok(Value::Bool(tishlang_runtime::ws_send_native(
|
|
3503
|
+
Ok(Value::Bool(tishlang_runtime::ws_send_native(
|
|
3504
|
+
&conn_core, &data,
|
|
3505
|
+
)))
|
|
3250
3506
|
}
|
|
3251
3507
|
|
|
3252
3508
|
#[cfg(feature = "ws")]
|
|
@@ -3255,7 +3511,9 @@ impl Evaluator {
|
|
|
3255
3511
|
for a in args {
|
|
3256
3512
|
cv.push(crate::value_convert::eval_to_core(a)?);
|
|
3257
3513
|
}
|
|
3258
|
-
Ok(crate::value_convert::core_to_eval(
|
|
3514
|
+
Ok(crate::value_convert::core_to_eval(
|
|
3515
|
+
tishlang_runtime::ws_broadcast_native(&cv),
|
|
3516
|
+
))
|
|
3259
3517
|
}
|
|
3260
3518
|
|
|
3261
3519
|
#[cfg(feature = "http")]
|
|
@@ -3309,7 +3567,6 @@ impl Evaluator {
|
|
|
3309
3567
|
"await requires the http feature".to_string(),
|
|
3310
3568
|
))
|
|
3311
3569
|
}
|
|
3312
|
-
|
|
3313
3570
|
}
|
|
3314
3571
|
|
|
3315
3572
|
#[derive(Debug)]
|