@tishlang/tish 1.0.29 → 1.0.34
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/crates/js_to_tish/src/transform/expr.rs +15 -6
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/main.rs +1 -1
- package/crates/tish/tests/integration_test.rs +4 -3
- package/crates/tish_ast/src/ast.rs +65 -2
- package/crates/tish_build_utils/src/lib.rs +10 -2
- package/crates/tish_builtins/src/construct.rs +177 -0
- package/crates/tish_builtins/src/globals.rs +3 -5
- package/crates/tish_builtins/src/helpers.rs +2 -3
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +3 -4
- package/crates/tish_bytecode/src/compiler.rs +85 -11
- package/crates/tish_bytecode/src/opcode.rs +7 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +604 -106
- package/crates/tish_compile/src/infer.rs +236 -0
- package/crates/tish_compile/src/lib.rs +52 -5
- package/crates/tish_compile/src/types.rs +42 -5
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +38 -94
- package/crates/tish_compile_js/src/lib.rs +0 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +68 -0
- package/crates/tish_core/Cargo.toml +4 -0
- package/crates/tish_core/src/console_style.rs +7 -1
- package/crates/tish_core/src/json.rs +1 -2
- package/crates/tish_core/src/macros.rs +2 -3
- package/crates/tish_core/src/value.rs +13 -5
- package/crates/tish_cranelift/src/lib.rs +6 -4
- package/crates/tish_cranelift/src/lower.rs +5 -3
- package/crates/tish_cranelift_runtime/src/lib.rs +4 -2
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +172 -79
- package/crates/tish_eval/src/http.rs +3 -4
- package/crates/tish_eval/src/lib.rs +7 -0
- package/crates/tish_eval/src/regex.rs +3 -2
- package/crates/tish_eval/src/value.rs +11 -13
- package/crates/tish_eval/src/value_convert.rs +4 -8
- package/crates/tish_fmt/src/lib.rs +49 -10
- package/crates/tish_lexer/src/token.rs +2 -0
- package/crates/tish_lint/src/lib.rs +9 -0
- package/crates/tish_llvm/src/lib.rs +4 -4
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_native/src/build.rs +16 -2
- package/crates/tish_native/src/lib.rs +10 -11
- package/crates/tish_opt/src/lib.rs +15 -0
- package/crates/tish_parser/src/lib.rs +101 -1
- package/crates/tish_parser/src/parser.rs +168 -51
- package/crates/tish_runtime/src/http.rs +4 -5
- package/crates/tish_runtime/src/http_fetch.rs +17 -10
- package/crates/tish_runtime/src/lib.rs +9 -2
- package/crates/tish_runtime/src/promise.rs +2 -3
- package/crates/tish_runtime/src/promise_io.rs +2 -3
- package/crates/tish_runtime/src/ws.rs +7 -7
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +390 -0
- package/crates/tish_ui/src/lib.rs +16 -0
- package/crates/tish_ui/src/runtime/hooks.rs +122 -0
- package/crates/tish_ui/src/runtime/mod.rs +173 -0
- package/crates/tish_vm/src/vm.rs +121 -27
- package/justfile +3 -3
- 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
- package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
|
@@ -8,14 +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::{BinOp, CompoundOp, ExportDeclaration, Expr, ImportSpecifier, Literal, LogicalAssignOp, MemberProp, Span, Statement, UnaryOp};
|
|
11
|
+
use tishlang_ast::{BinOp, CompoundOp, ExportDeclaration, Expr, FunParam, ImportSpecifier, Literal, LogicalAssignOp, MemberProp, Span, Statement, UnaryOp};
|
|
12
12
|
|
|
13
|
-
use crate::value::Value;
|
|
13
|
+
use crate::value::{PropMap, Value};
|
|
14
14
|
#[cfg(any(feature = "fs", feature = "process"))]
|
|
15
15
|
use crate::natives;
|
|
16
16
|
|
|
17
17
|
struct Scope {
|
|
18
|
-
vars:
|
|
18
|
+
vars: PropMap,
|
|
19
19
|
consts: std::collections::HashSet<Arc<str>>,
|
|
20
20
|
parent: Option<Rc<std::cell::RefCell<Scope>>>,
|
|
21
21
|
}
|
|
@@ -23,7 +23,7 @@ struct Scope {
|
|
|
23
23
|
impl Scope {
|
|
24
24
|
fn new() -> Rc<std::cell::RefCell<Self>> {
|
|
25
25
|
Rc::new(std::cell::RefCell::new(Self {
|
|
26
|
-
vars:
|
|
26
|
+
vars: PropMap::default(),
|
|
27
27
|
consts: std::collections::HashSet::new(),
|
|
28
28
|
parent: None,
|
|
29
29
|
}))
|
|
@@ -31,7 +31,7 @@ impl Scope {
|
|
|
31
31
|
|
|
32
32
|
fn child(parent: Rc<std::cell::RefCell<Scope>>) -> Rc<std::cell::RefCell<Self>> {
|
|
33
33
|
Rc::new(std::cell::RefCell::new(Self {
|
|
34
|
-
vars:
|
|
34
|
+
vars: PropMap::default(),
|
|
35
35
|
consts: std::collections::HashSet::new(),
|
|
36
36
|
parent: Some(parent),
|
|
37
37
|
}))
|
|
@@ -75,6 +75,8 @@ pub struct Evaluator {
|
|
|
75
75
|
module_cache: Rc<RefCell<HashMap<PathBuf, Value>>>,
|
|
76
76
|
/// Directory of the file currently being evaluated (for resolving relative imports)
|
|
77
77
|
current_dir: RefCell<Option<PathBuf>>,
|
|
78
|
+
/// Extra `tish:*` builtins from `TishNativeModule::virtual_builtin_modules` (shared across nested evaluators).
|
|
79
|
+
virtual_builtins: Rc<RefCell<HashMap<Arc<str>, Value>>>,
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
impl Evaluator {
|
|
@@ -85,7 +87,7 @@ impl Evaluator {
|
|
|
85
87
|
let scope = Scope::new();
|
|
86
88
|
{
|
|
87
89
|
let mut s = scope.borrow_mut();
|
|
88
|
-
let mut console =
|
|
90
|
+
let mut console = PropMap::with_capacity(5);
|
|
89
91
|
console.insert("debug".into(), Value::Native(natives::console_debug));
|
|
90
92
|
console.insert("info".into(), Value::Native(natives::console_info));
|
|
91
93
|
console.insert("log".into(), Value::Native(natives::console_log));
|
|
@@ -101,7 +103,7 @@ impl Evaluator {
|
|
|
101
103
|
s.set("isNaN".into(), Value::Native(natives::is_nan), true);
|
|
102
104
|
s.set("Infinity".into(), Value::Number(f64::INFINITY), true);
|
|
103
105
|
s.set("NaN".into(), Value::Number(f64::NAN), true);
|
|
104
|
-
let mut math =
|
|
106
|
+
let mut math = PropMap::with_capacity(18);
|
|
105
107
|
math.insert("abs".into(), Value::Native(natives::math_abs));
|
|
106
108
|
math.insert("sqrt".into(), Value::Native(natives::math_sqrt));
|
|
107
109
|
math.insert("min".into(), Value::Native(natives::math_min));
|
|
@@ -122,12 +124,12 @@ impl Evaluator {
|
|
|
122
124
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
123
125
|
s.set("Math".into(), Value::Object(Rc::new(RefCell::new(math))), true);
|
|
124
126
|
|
|
125
|
-
let mut json =
|
|
127
|
+
let mut json = PropMap::with_capacity(2);
|
|
126
128
|
json.insert("parse".into(), Value::Native(Self::json_parse_native));
|
|
127
129
|
json.insert("stringify".into(), Value::Native(Self::json_stringify_native));
|
|
128
130
|
s.set("JSON".into(), Value::Object(Rc::new(RefCell::new(json))), true);
|
|
129
131
|
|
|
130
|
-
let mut object =
|
|
132
|
+
let mut object = PropMap::with_capacity(5);
|
|
131
133
|
object.insert("keys".into(), Value::Native(Self::object_keys));
|
|
132
134
|
object.insert("values".into(), Value::Native(Self::object_values));
|
|
133
135
|
object.insert("entries".into(), Value::Native(Self::object_entries));
|
|
@@ -135,18 +137,33 @@ impl Evaluator {
|
|
|
135
137
|
object.insert("fromEntries".into(), Value::Native(Self::object_from_entries));
|
|
136
138
|
s.set("Object".into(), Value::Object(Rc::new(RefCell::new(object))), true);
|
|
137
139
|
|
|
138
|
-
let mut array_obj =
|
|
140
|
+
let mut array_obj = PropMap::with_capacity(1);
|
|
139
141
|
array_obj.insert("isArray".into(), Value::Native(natives::array_is_array));
|
|
140
142
|
s.set("Array".into(), Value::Object(Rc::new(RefCell::new(array_obj))), true);
|
|
141
143
|
|
|
142
|
-
let mut string_obj =
|
|
144
|
+
let mut string_obj = PropMap::with_capacity(1);
|
|
143
145
|
string_obj.insert("fromCharCode".into(), Value::Native(natives::string_from_char_code));
|
|
144
146
|
s.set("String".into(), Value::Object(Rc::new(RefCell::new(string_obj))), true);
|
|
145
147
|
|
|
146
|
-
let mut date =
|
|
148
|
+
let mut date = PropMap::with_capacity(1);
|
|
147
149
|
date.insert("now".into(), Value::Native(natives::date_now));
|
|
148
150
|
s.set("Date".into(), Value::Object(Rc::new(RefCell::new(date))), true);
|
|
149
151
|
|
|
152
|
+
s.set(
|
|
153
|
+
"Uint8Array".into(),
|
|
154
|
+
crate::value_convert::core_to_eval(
|
|
155
|
+
tishlang_builtins::construct::uint8_array_constructor_value(),
|
|
156
|
+
),
|
|
157
|
+
true,
|
|
158
|
+
);
|
|
159
|
+
s.set(
|
|
160
|
+
"AudioContext".into(),
|
|
161
|
+
crate::value_convert::core_to_eval(
|
|
162
|
+
tishlang_builtins::construct::audio_context_constructor_value(),
|
|
163
|
+
),
|
|
164
|
+
true,
|
|
165
|
+
);
|
|
166
|
+
|
|
150
167
|
#[cfg(feature = "regex")]
|
|
151
168
|
{
|
|
152
169
|
s.set("RegExp".into(), Value::Native(Self::regexp_constructor_native), true);
|
|
@@ -158,6 +175,7 @@ impl Evaluator {
|
|
|
158
175
|
scope,
|
|
159
176
|
module_cache: Rc::new(RefCell::new(HashMap::new())),
|
|
160
177
|
current_dir: RefCell::new(None),
|
|
178
|
+
virtual_builtins: Rc::new(RefCell::new(HashMap::new())),
|
|
161
179
|
}
|
|
162
180
|
}
|
|
163
181
|
|
|
@@ -172,6 +190,14 @@ impl Evaluator {
|
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
192
|
}
|
|
193
|
+
{
|
|
194
|
+
let mut vb = eval.virtual_builtins.borrow_mut();
|
|
195
|
+
for module in modules {
|
|
196
|
+
for (spec, value) in module.virtual_builtin_modules() {
|
|
197
|
+
vb.insert(Arc::from(spec), value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
175
201
|
eval
|
|
176
202
|
}
|
|
177
203
|
|
|
@@ -323,14 +349,11 @@ impl Evaluator {
|
|
|
323
349
|
body,
|
|
324
350
|
..
|
|
325
351
|
} => {
|
|
326
|
-
|
|
327
|
-
let param_names: Arc<[Arc<str>]> = params.iter().map(|p| Arc::clone(&p.name)).collect();
|
|
328
|
-
let defaults: Arc<[Option<Expr>]> = params.iter().map(|p| p.default.clone()).collect();
|
|
352
|
+
let formals: Arc<[FunParam]> = Arc::from(params.clone());
|
|
329
353
|
let rest_param_name = rest_param.as_ref().map(|p| Arc::clone(&p.name));
|
|
330
354
|
let body = Arc::new(body.as_ref().clone());
|
|
331
355
|
let func = Value::Function {
|
|
332
|
-
|
|
333
|
-
defaults,
|
|
356
|
+
formals,
|
|
334
357
|
rest_param: rest_param_name,
|
|
335
358
|
body,
|
|
336
359
|
};
|
|
@@ -490,7 +513,7 @@ impl Evaluator {
|
|
|
490
513
|
/// Load and evaluate a module, returning its exports object. Uses cache.
|
|
491
514
|
fn load_module(&mut self, from: &str) -> Result<Value, EvalError> {
|
|
492
515
|
if from.starts_with("tish:") {
|
|
493
|
-
return
|
|
516
|
+
return self.load_builtin_module(from);
|
|
494
517
|
}
|
|
495
518
|
let dir = self.current_dir.borrow().clone().ok_or_else(|| {
|
|
496
519
|
EvalError::Error("Cannot resolve imports: no current file directory (use run_file)".to_string())
|
|
@@ -536,7 +559,7 @@ impl Evaluator {
|
|
|
536
559
|
let _ = self.eval_statement(stmt);
|
|
537
560
|
}
|
|
538
561
|
}
|
|
539
|
-
let mut exports:
|
|
562
|
+
let mut exports: PropMap = PropMap::default();
|
|
540
563
|
for name in export_names {
|
|
541
564
|
if let Some(v) = module_scope.borrow().get(&name) {
|
|
542
565
|
exports.insert(Arc::from(name.as_str()), v);
|
|
@@ -570,13 +593,16 @@ impl Evaluator {
|
|
|
570
593
|
Ok(path)
|
|
571
594
|
}
|
|
572
595
|
|
|
573
|
-
/// Load built-in module (tish:fs, tish:http, tish:process)
|
|
574
|
-
fn load_builtin_module(spec: &str) -> Result<Value, EvalError> {
|
|
596
|
+
/// Load built-in module (tish:fs, tish:http, tish:process, …) or a virtual module from native crates.
|
|
597
|
+
fn load_builtin_module(&self, spec: &str) -> Result<Value, EvalError> {
|
|
598
|
+
if let Some(v) = self.virtual_builtins.borrow().get(spec) {
|
|
599
|
+
return Ok(v.clone());
|
|
600
|
+
}
|
|
575
601
|
match spec {
|
|
576
602
|
"tish:fs" => {
|
|
577
603
|
#[cfg(feature = "fs")]
|
|
578
604
|
{
|
|
579
|
-
let mut exports:
|
|
605
|
+
let mut exports: PropMap = PropMap::default();
|
|
580
606
|
exports.insert("readFile".into(), Value::Native(natives::read_file));
|
|
581
607
|
exports.insert("writeFile".into(), Value::Native(natives::write_file));
|
|
582
608
|
exports.insert("fileExists".into(), Value::Native(natives::file_exists));
|
|
@@ -595,7 +621,7 @@ impl Evaluator {
|
|
|
595
621
|
"tish:http" => {
|
|
596
622
|
#[cfg(feature = "http")]
|
|
597
623
|
{
|
|
598
|
-
let mut exports:
|
|
624
|
+
let mut exports: PropMap = PropMap::default();
|
|
599
625
|
exports.insert("fetch".into(), Value::Native(Self::fetch_native));
|
|
600
626
|
exports.insert("fetchAll".into(), Value::Native(Self::fetch_all_native));
|
|
601
627
|
exports.insert("serve".into(), Value::Serve);
|
|
@@ -616,7 +642,7 @@ impl Evaluator {
|
|
|
616
642
|
"tish:ws" => {
|
|
617
643
|
#[cfg(feature = "ws")]
|
|
618
644
|
{
|
|
619
|
-
let mut exports:
|
|
645
|
+
let mut exports: PropMap = PropMap::default();
|
|
620
646
|
exports.insert("WebSocket".into(), Value::Native(Self::ws_web_socket_native));
|
|
621
647
|
exports.insert("Server".into(), Value::Native(Self::ws_server_native));
|
|
622
648
|
exports.insert("wsSend".into(), Value::Native(Self::ws_send_native));
|
|
@@ -633,7 +659,7 @@ impl Evaluator {
|
|
|
633
659
|
"tish:process" => {
|
|
634
660
|
#[cfg(feature = "process")]
|
|
635
661
|
{
|
|
636
|
-
let mut exports:
|
|
662
|
+
let mut exports: PropMap = PropMap::default();
|
|
637
663
|
exports.insert("exit".into(), Value::Native(natives::process_exit));
|
|
638
664
|
exports.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
639
665
|
exports.insert("exec".into(), Value::Native(natives::process_exec));
|
|
@@ -641,11 +667,11 @@ impl Evaluator {
|
|
|
641
667
|
.map(|s| Value::String(s.into()))
|
|
642
668
|
.collect();
|
|
643
669
|
exports.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv.clone()))));
|
|
644
|
-
let env_obj:
|
|
670
|
+
let env_obj: PropMap = std::env::vars()
|
|
645
671
|
.map(|(key, value)| (Arc::from(key.as_str()), Value::String(value.into())))
|
|
646
672
|
.collect();
|
|
647
673
|
exports.insert("env".into(), Value::Object(Rc::new(RefCell::new(env_obj.clone()))));
|
|
648
|
-
let mut process_obj =
|
|
674
|
+
let mut process_obj = PropMap::default();
|
|
649
675
|
process_obj.insert("exit".into(), Value::Native(natives::process_exit));
|
|
650
676
|
process_obj.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
651
677
|
process_obj.insert("exec".into(), Value::Native(natives::process_exec));
|
|
@@ -663,15 +689,15 @@ impl Evaluator {
|
|
|
663
689
|
}
|
|
664
690
|
_ => {
|
|
665
691
|
return Err(EvalError::Error(format!(
|
|
666
|
-
"Unknown built-in module: {}. Supported: tish:fs, tish:http, tish:process, tish:ws",
|
|
692
|
+
"Unknown built-in module: {}. Supported: tish:fs, tish:http, tish:process, tish:ws (plus any registered by native modules)",
|
|
667
693
|
spec
|
|
668
694
|
)));
|
|
669
695
|
}
|
|
670
696
|
}
|
|
671
697
|
}
|
|
672
698
|
|
|
673
|
-
fn load_builtin_export(spec: &str, export_name: &str) -> Result<Value, EvalError> {
|
|
674
|
-
let module =
|
|
699
|
+
fn load_builtin_export(&self, spec: &str, export_name: &str) -> Result<Value, EvalError> {
|
|
700
|
+
let module = self.load_builtin_module(spec)?;
|
|
675
701
|
let exports = match &module {
|
|
676
702
|
Value::Object(m) => m.borrow().clone(),
|
|
677
703
|
_ => return Err(EvalError::Error("Built-in module must be object".into())),
|
|
@@ -1534,7 +1560,7 @@ impl Evaluator {
|
|
|
1534
1560
|
Ok(Value::Array(Rc::new(RefCell::new(vals))))
|
|
1535
1561
|
}
|
|
1536
1562
|
Expr::Object { props, .. } => {
|
|
1537
|
-
let mut map =
|
|
1563
|
+
let mut map = PropMap::default();
|
|
1538
1564
|
for prop in props {
|
|
1539
1565
|
match prop {
|
|
1540
1566
|
tishlang_ast::ObjectProp::KeyValue(k, v) => {
|
|
@@ -1561,11 +1587,16 @@ impl Evaluator {
|
|
|
1561
1587
|
}
|
|
1562
1588
|
}
|
|
1563
1589
|
Expr::Await { operand, .. } => self.eval_await(operand),
|
|
1590
|
+
Expr::New { callee, args, .. } => {
|
|
1591
|
+
let c = self.eval_expr(callee)?;
|
|
1592
|
+
let arg_vals = self.eval_call_args(args)?;
|
|
1593
|
+
self.construct_value(&c, &arg_vals)
|
|
1594
|
+
}
|
|
1564
1595
|
Expr::JsxElement { .. } | Expr::JsxFragment { .. } => Err(EvalError::Error(
|
|
1565
1596
|
"JSX is not supported in the interpreter. Use 'tish compile --target js' to compile to JavaScript.".to_string(),
|
|
1566
1597
|
)),
|
|
1567
1598
|
Expr::NativeModuleLoad { spec, export_name, .. } => {
|
|
1568
|
-
|
|
1599
|
+
self.load_builtin_export(spec.as_ref(), export_name.as_ref())
|
|
1569
1600
|
}
|
|
1570
1601
|
Expr::TypeOf { operand, .. } => {
|
|
1571
1602
|
let v = self.eval_expr(operand)?;
|
|
@@ -1577,7 +1608,6 @@ impl Evaluator {
|
|
|
1577
1608
|
Value::Array(_) => "object".into(),
|
|
1578
1609
|
Value::Object(_) => "object".into(),
|
|
1579
1610
|
Value::Function { .. } | Value::Native(_) => "function".into(),
|
|
1580
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
1581
1611
|
Value::CoreFn(_) => "function".into(),
|
|
1582
1612
|
#[cfg(feature = "http")]
|
|
1583
1613
|
Value::CorePromise(_) => "object".into(),
|
|
@@ -1755,9 +1785,7 @@ impl Evaluator {
|
|
|
1755
1785
|
}
|
|
1756
1786
|
Expr::ArrowFunction { params, body, .. } => {
|
|
1757
1787
|
use tishlang_ast::ArrowBody;
|
|
1758
|
-
|
|
1759
|
-
let param_names: Arc<[Arc<str>]> = params.iter().map(|p| Arc::clone(&p.name)).collect();
|
|
1760
|
-
let defaults: Arc<[Option<tishlang_ast::Expr>]> = params.iter().map(|p| p.default.clone()).collect();
|
|
1788
|
+
let formals: Arc<[FunParam]> = Arc::from(params.clone());
|
|
1761
1789
|
let body_stmt = match body {
|
|
1762
1790
|
ArrowBody::Expr(expr) => {
|
|
1763
1791
|
// Expression body: wrap in implicit return
|
|
@@ -1769,8 +1797,7 @@ impl Evaluator {
|
|
|
1769
1797
|
ArrowBody::Block(stmt) => stmt.as_ref().clone(),
|
|
1770
1798
|
};
|
|
1771
1799
|
Ok(Value::Function {
|
|
1772
|
-
|
|
1773
|
-
defaults,
|
|
1800
|
+
formals,
|
|
1774
1801
|
rest_param: None,
|
|
1775
1802
|
body: Arc::new(body_stmt),
|
|
1776
1803
|
})
|
|
@@ -1862,18 +1889,21 @@ impl Evaluator {
|
|
|
1862
1889
|
/// descending = false: checks for `(a, b) => a - b`
|
|
1863
1890
|
/// descending = true: checks for `(a, b) => b - a`
|
|
1864
1891
|
fn is_numeric_sort_comparator(f: &Value, descending: bool) -> bool {
|
|
1865
|
-
if let Value::Function {
|
|
1866
|
-
// Must have exactly 2 params, no defaults, no rest
|
|
1867
|
-
if
|
|
1868
|
-
return false;
|
|
1869
|
-
}
|
|
1870
|
-
if defaults.iter().any(|d| d.is_some()) {
|
|
1892
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
1893
|
+
// Must have exactly 2 simple params, no defaults, no rest
|
|
1894
|
+
if formals.len() != 2 || rest_param.is_some() {
|
|
1871
1895
|
return false;
|
|
1872
1896
|
}
|
|
1897
|
+
let (param_a, param_b) = match (&formals[0], &formals[1]) {
|
|
1898
|
+
(FunParam::Simple(a), FunParam::Simple(b))
|
|
1899
|
+
if a.default.is_none() && b.default.is_none() =>
|
|
1900
|
+
{
|
|
1901
|
+
(&a.name, &b.name)
|
|
1902
|
+
}
|
|
1903
|
+
_ => return false,
|
|
1904
|
+
};
|
|
1873
1905
|
|
|
1874
1906
|
// Body must be a return of a - b (or b - a for descending)
|
|
1875
|
-
let param_a = ¶ms[0];
|
|
1876
|
-
let param_b = ¶ms[1];
|
|
1877
1907
|
|
|
1878
1908
|
// Check for both Statement::Return and Statement::ExprStmt (arrow implicit return)
|
|
1879
1909
|
let expr = match body.as_ref() {
|
|
@@ -1947,20 +1977,32 @@ impl Evaluator {
|
|
|
1947
1977
|
/// Optimized callback invocation for array methods.
|
|
1948
1978
|
/// Creates a reusable scope that can be updated for each iteration.
|
|
1949
1979
|
fn create_callback_scope(&self, f: &Value) -> Option<(Rc<RefCell<Scope>>, Arc<[Arc<str>]>, Arc<Statement>)> {
|
|
1950
|
-
if let Value::Function {
|
|
1951
|
-
|
|
1952
|
-
if rest_param.is_some() || defaults.iter().any(|d| d.is_some()) {
|
|
1980
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
1981
|
+
if rest_param.is_some() {
|
|
1953
1982
|
return None;
|
|
1954
1983
|
}
|
|
1984
|
+
for fp in formals.iter() {
|
|
1985
|
+
match fp {
|
|
1986
|
+
FunParam::Simple(tp) if tp.default.is_none() => {}
|
|
1987
|
+
_ => return None,
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1955
1990
|
let scope = Scope::child(Rc::clone(&self.scope));
|
|
1956
|
-
// Pre-initialize parameters to Null
|
|
1957
1991
|
{
|
|
1958
1992
|
let mut s = scope.borrow_mut();
|
|
1959
|
-
for
|
|
1960
|
-
|
|
1993
|
+
for fp in formals.iter() {
|
|
1994
|
+
for n in fp.bound_names() {
|
|
1995
|
+
s.set(n, Value::Null, true);
|
|
1996
|
+
}
|
|
1961
1997
|
}
|
|
1962
1998
|
}
|
|
1963
|
-
|
|
1999
|
+
let flat_names: Arc<[Arc<str>]> = Arc::from(
|
|
2000
|
+
formals
|
|
2001
|
+
.iter()
|
|
2002
|
+
.flat_map(|fp| fp.bound_names())
|
|
2003
|
+
.collect::<Vec<_>>(),
|
|
2004
|
+
);
|
|
2005
|
+
return Some((scope, flat_names, Arc::clone(body)));
|
|
1964
2006
|
}
|
|
1965
2007
|
None
|
|
1966
2008
|
}
|
|
@@ -1987,6 +2029,7 @@ impl Evaluator {
|
|
|
1987
2029
|
scope: Rc::clone(scope),
|
|
1988
2030
|
module_cache: Rc::clone(&self.module_cache),
|
|
1989
2031
|
current_dir: RefCell::new(self.current_dir.borrow().clone()),
|
|
2032
|
+
virtual_builtins: Rc::clone(&self.virtual_builtins),
|
|
1990
2033
|
};
|
|
1991
2034
|
match eval.eval_statement(body) {
|
|
1992
2035
|
Ok(v) => Ok(v),
|
|
@@ -2002,12 +2045,14 @@ impl Evaluator {
|
|
|
2002
2045
|
f: &Value,
|
|
2003
2046
|
args: &[Value],
|
|
2004
2047
|
) -> Option<Result<Value, EvalError>> {
|
|
2005
|
-
if let Value::Function {
|
|
2006
|
-
|
|
2007
|
-
if params.len() != 1 || rest_param.is_some() || defaults.iter().any(|d| d.is_some()) {
|
|
2048
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
2049
|
+
if formals.len() != 1 || rest_param.is_some() {
|
|
2008
2050
|
return None;
|
|
2009
2051
|
}
|
|
2010
|
-
let param_name = &
|
|
2052
|
+
let param_name = match &formals[0] {
|
|
2053
|
+
FunParam::Simple(tp) if tp.default.is_none() => &tp.name,
|
|
2054
|
+
_ => return None,
|
|
2055
|
+
};
|
|
2011
2056
|
let arg = args.first().cloned().unwrap_or(Value::Null);
|
|
2012
2057
|
|
|
2013
2058
|
// Get the expression from the body
|
|
@@ -2078,6 +2123,27 @@ impl Evaluator {
|
|
|
2078
2123
|
}
|
|
2079
2124
|
}
|
|
2080
2125
|
|
|
2126
|
+
/// Host `new`: `__construct` on objects; otherwise same callables as `call_func`, else null.
|
|
2127
|
+
fn construct_value(&self, callee: &Value, args: &[Value]) -> Result<Value, EvalError> {
|
|
2128
|
+
if let Value::Object(o) = callee {
|
|
2129
|
+
if let Some(ctor) = o.borrow().get(&Arc::from("__construct")) {
|
|
2130
|
+
return self.call_func(ctor, args);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
match callee {
|
|
2134
|
+
Value::Native(_) | Value::Function { .. } | Value::CoreFn(_) => {
|
|
2135
|
+
self.call_func(callee, args)
|
|
2136
|
+
}
|
|
2137
|
+
#[cfg(feature = "http")]
|
|
2138
|
+
Value::PromiseConstructor
|
|
2139
|
+
| Value::Serve
|
|
2140
|
+
| Value::BoundPromiseMethod(_, _)
|
|
2141
|
+
| Value::TimerBuiltin(_) => self.call_func(callee, args),
|
|
2142
|
+
Value::OpaqueMethod(_, _) => self.call_func(callee, args),
|
|
2143
|
+
_ => Ok(Value::Null),
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2081
2147
|
fn call_func(&self, f: &Value, args: &[Value]) -> Result<Value, EvalError> {
|
|
2082
2148
|
match f {
|
|
2083
2149
|
Value::Native(native_fn) => {
|
|
@@ -2142,7 +2208,6 @@ impl Evaluator {
|
|
|
2142
2208
|
}
|
|
2143
2209
|
#[cfg(feature = "http")]
|
|
2144
2210
|
Value::Serve => self.run_http_server(args),
|
|
2145
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
2146
2211
|
Value::CoreFn(f) => {
|
|
2147
2212
|
let ca: Result<Vec<tishlang_core::Value>, String> =
|
|
2148
2213
|
args.iter().map(crate::value_convert::eval_to_core).collect();
|
|
@@ -2167,15 +2232,19 @@ impl Evaluator {
|
|
|
2167
2232
|
let result = method(&core_args);
|
|
2168
2233
|
Ok(crate::value_convert::core_to_eval(result))
|
|
2169
2234
|
}
|
|
2170
|
-
Value::Function {
|
|
2235
|
+
Value::Function { formals, rest_param, body } => {
|
|
2171
2236
|
let scope = Scope::child(Rc::clone(&self.scope));
|
|
2172
2237
|
{
|
|
2173
2238
|
let mut s = scope.borrow_mut();
|
|
2174
|
-
for (i,
|
|
2239
|
+
for (i, formal) in formals.iter().enumerate() {
|
|
2175
2240
|
let val = match args.get(i) {
|
|
2176
2241
|
Some(v) => v.clone(),
|
|
2177
2242
|
None => {
|
|
2178
|
-
|
|
2243
|
+
let def = match formal {
|
|
2244
|
+
FunParam::Simple(tp) => tp.default.as_ref(),
|
|
2245
|
+
FunParam::Destructure { default, .. } => default.as_ref(),
|
|
2246
|
+
};
|
|
2247
|
+
if let Some(default_expr) = def {
|
|
2179
2248
|
drop(s);
|
|
2180
2249
|
let default_val = self.eval_expr(default_expr)?;
|
|
2181
2250
|
s = scope.borrow_mut();
|
|
@@ -2185,10 +2254,19 @@ impl Evaluator {
|
|
|
2185
2254
|
}
|
|
2186
2255
|
}
|
|
2187
2256
|
};
|
|
2188
|
-
|
|
2257
|
+
match formal {
|
|
2258
|
+
FunParam::Simple(tp) => {
|
|
2259
|
+
s.set(Arc::clone(&tp.name), val, true);
|
|
2260
|
+
}
|
|
2261
|
+
FunParam::Destructure { pattern, .. } => {
|
|
2262
|
+
drop(s);
|
|
2263
|
+
Self::bind_destruct_pattern_scoped(&scope, pattern, &val, true)?;
|
|
2264
|
+
s = scope.borrow_mut();
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2189
2267
|
}
|
|
2190
2268
|
if let Some(ref rest_name) = rest_param {
|
|
2191
|
-
let rest_vals: Vec<Value> = args.iter().skip(
|
|
2269
|
+
let rest_vals: Vec<Value> = args.iter().skip(formals.len()).cloned().collect();
|
|
2192
2270
|
s.set(Arc::clone(rest_name), Value::Array(Rc::new(RefCell::new(rest_vals))), true);
|
|
2193
2271
|
}
|
|
2194
2272
|
}
|
|
@@ -2196,6 +2274,7 @@ impl Evaluator {
|
|
|
2196
2274
|
scope,
|
|
2197
2275
|
module_cache: Rc::clone(&self.module_cache),
|
|
2198
2276
|
current_dir: RefCell::new(self.current_dir.borrow().clone()),
|
|
2277
|
+
virtual_builtins: Rc::clone(&self.virtual_builtins),
|
|
2199
2278
|
};
|
|
2200
2279
|
match eval.eval_statement(body) {
|
|
2201
2280
|
Ok(v) => Ok(v),
|
|
@@ -2449,13 +2528,13 @@ impl Evaluator {
|
|
|
2449
2528
|
let response_value = match self.call_func(&handler, &[req_value]) {
|
|
2450
2529
|
Ok(v) => v,
|
|
2451
2530
|
Err(EvalError::Throw(v)) => {
|
|
2452
|
-
let mut err_obj:
|
|
2531
|
+
let mut err_obj: PropMap = PropMap::with_capacity(2);
|
|
2453
2532
|
err_obj.insert(Arc::from("status"), Value::Number(500.0));
|
|
2454
2533
|
err_obj.insert(Arc::from("body"), Value::String(v.to_string().into()));
|
|
2455
2534
|
Value::Object(Rc::new(RefCell::new(err_obj)))
|
|
2456
2535
|
}
|
|
2457
2536
|
Err(e) => {
|
|
2458
|
-
let mut err_obj:
|
|
2537
|
+
let mut err_obj: PropMap = PropMap::with_capacity(2);
|
|
2459
2538
|
err_obj.insert(Arc::from("status"), Value::Number(500.0));
|
|
2460
2539
|
err_obj.insert(Arc::from("body"), Value::String(e.to_string().into()));
|
|
2461
2540
|
Value::Object(Rc::new(RefCell::new(err_obj)))
|
|
@@ -2491,28 +2570,37 @@ impl Evaluator {
|
|
|
2491
2570
|
Ok(result)
|
|
2492
2571
|
}
|
|
2493
2572
|
|
|
2494
|
-
fn
|
|
2573
|
+
fn bind_destruct_pattern_scoped(
|
|
2574
|
+
scope: &Rc<RefCell<Scope>>,
|
|
2575
|
+
pattern: &tishlang_ast::DestructPattern,
|
|
2576
|
+
value: &Value,
|
|
2577
|
+
mutable: bool,
|
|
2578
|
+
) -> Result<(), EvalError> {
|
|
2495
2579
|
match pattern {
|
|
2496
2580
|
tishlang_ast::DestructPattern::Array(elements) => {
|
|
2497
2581
|
let arr = match value {
|
|
2498
2582
|
Value::Array(a) => a.borrow().clone(),
|
|
2499
2583
|
_ => return Err(EvalError::Error("Cannot destructure non-array value".to_string())),
|
|
2500
2584
|
};
|
|
2501
|
-
|
|
2585
|
+
|
|
2502
2586
|
for (i, elem) in elements.iter().enumerate() {
|
|
2503
2587
|
if let Some(el) = elem {
|
|
2504
2588
|
match el {
|
|
2505
2589
|
tishlang_ast::DestructElement::Ident(name) => {
|
|
2506
2590
|
let val = arr.get(i).cloned().unwrap_or(Value::Null);
|
|
2507
|
-
|
|
2591
|
+
scope.borrow_mut().set(Arc::clone(name), val, mutable);
|
|
2508
2592
|
}
|
|
2509
2593
|
tishlang_ast::DestructElement::Pattern(nested) => {
|
|
2510
2594
|
let val = arr.get(i).cloned().unwrap_or(Value::Null);
|
|
2511
|
-
|
|
2595
|
+
Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
|
|
2512
2596
|
}
|
|
2513
2597
|
tishlang_ast::DestructElement::Rest(name) => {
|
|
2514
2598
|
let rest: Vec<Value> = arr.iter().skip(i).cloned().collect();
|
|
2515
|
-
|
|
2599
|
+
scope.borrow_mut().set(
|
|
2600
|
+
Arc::clone(name),
|
|
2601
|
+
Value::Array(Rc::new(RefCell::new(rest))),
|
|
2602
|
+
mutable,
|
|
2603
|
+
);
|
|
2516
2604
|
break;
|
|
2517
2605
|
}
|
|
2518
2606
|
}
|
|
@@ -2524,18 +2612,20 @@ impl Evaluator {
|
|
|
2524
2612
|
Value::Object(o) => o.borrow().clone(),
|
|
2525
2613
|
_ => return Err(EvalError::Error("Cannot destructure non-object value".to_string())),
|
|
2526
2614
|
};
|
|
2527
|
-
|
|
2615
|
+
|
|
2528
2616
|
for prop in props {
|
|
2529
2617
|
let val = obj.get(&prop.key).cloned().unwrap_or(Value::Null);
|
|
2530
2618
|
match &prop.value {
|
|
2531
2619
|
tishlang_ast::DestructElement::Ident(name) => {
|
|
2532
|
-
|
|
2620
|
+
scope.borrow_mut().set(Arc::clone(name), val, mutable);
|
|
2533
2621
|
}
|
|
2534
2622
|
tishlang_ast::DestructElement::Pattern(nested) => {
|
|
2535
|
-
|
|
2623
|
+
Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
|
|
2536
2624
|
}
|
|
2537
2625
|
tishlang_ast::DestructElement::Rest(_) => {
|
|
2538
|
-
return Err(EvalError::Error(
|
|
2626
|
+
return Err(EvalError::Error(
|
|
2627
|
+
"Rest not supported in object destructuring".to_string(),
|
|
2628
|
+
));
|
|
2539
2629
|
}
|
|
2540
2630
|
}
|
|
2541
2631
|
}
|
|
@@ -2544,6 +2634,10 @@ impl Evaluator {
|
|
|
2544
2634
|
Ok(())
|
|
2545
2635
|
}
|
|
2546
2636
|
|
|
2637
|
+
fn bind_destruct_pattern(&mut self, pattern: &tishlang_ast::DestructPattern, value: &Value, mutable: bool) -> Result<(), EvalError> {
|
|
2638
|
+
Self::bind_destruct_pattern_scoped(&self.scope, pattern, value, mutable)
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2547
2641
|
fn get_prop(&self, obj: &Value, key: &str) -> Result<Value, String> {
|
|
2548
2642
|
match obj {
|
|
2549
2643
|
Value::Object(map) => Ok(map.borrow().get(key).cloned().unwrap_or(Value::Null)),
|
|
@@ -2745,9 +2839,9 @@ impl Evaluator {
|
|
|
2745
2839
|
fn json_parse_object(s: &str) -> Result<Value, ()> {
|
|
2746
2840
|
let s = s[1..].trim_start();
|
|
2747
2841
|
if s.starts_with('}') {
|
|
2748
|
-
return Ok(Value::Object(Rc::new(RefCell::new(
|
|
2842
|
+
return Ok(Value::Object(Rc::new(RefCell::new(PropMap::default()))));
|
|
2749
2843
|
}
|
|
2750
|
-
let mut map =
|
|
2844
|
+
let mut map = PropMap::default();
|
|
2751
2845
|
let mut rest = s;
|
|
2752
2846
|
loop {
|
|
2753
2847
|
if !rest.starts_with('"') {
|
|
@@ -2873,7 +2967,6 @@ impl Evaluator {
|
|
|
2873
2967
|
Value::Function { .. } | Value::Native(_) => "null".to_string(),
|
|
2874
2968
|
#[cfg(feature = "http")]
|
|
2875
2969
|
Value::CorePromise(_) => "null".to_string(),
|
|
2876
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
2877
2970
|
Value::CoreFn(_) => "null".to_string(),
|
|
2878
2971
|
#[cfg(feature = "http")]
|
|
2879
2972
|
Value::Serve
|
|
@@ -2949,7 +3042,7 @@ impl Evaluator {
|
|
|
2949
3042
|
|
|
2950
3043
|
fn object_from_entries(args: &[Value]) -> Result<Value, String> {
|
|
2951
3044
|
if let Some(Value::Array(arr)) = args.first() {
|
|
2952
|
-
let mut map =
|
|
3045
|
+
let mut map = PropMap::default();
|
|
2953
3046
|
for entry in arr.borrow().iter() {
|
|
2954
3047
|
if let Value::Array(pair) = entry {
|
|
2955
3048
|
let pair = pair.borrow();
|
|
@@ -2961,7 +3054,7 @@ impl Evaluator {
|
|
|
2961
3054
|
}
|
|
2962
3055
|
Ok(Value::Object(Rc::new(RefCell::new(map))))
|
|
2963
3056
|
} else {
|
|
2964
|
-
Ok(Value::Object(Rc::new(RefCell::new(
|
|
3057
|
+
Ok(Value::Object(Rc::new(RefCell::new(PropMap::default()))))
|
|
2965
3058
|
}
|
|
2966
3059
|
}
|
|
2967
3060
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
//! HTTP server for the Tish interpreter. Client `fetch` uses `tishlang_runtime` from eval.
|
|
2
2
|
|
|
3
|
-
use crate::value::Value;
|
|
4
|
-
use std::collections::HashMap;
|
|
3
|
+
use crate::value::{PropMap, Value};
|
|
5
4
|
use std::sync::Arc;
|
|
6
5
|
|
|
7
6
|
use tokio::runtime::Runtime;
|
|
@@ -22,7 +21,7 @@ pub fn create_server(port: u16) -> Result<tiny_http::Server, String> {
|
|
|
22
21
|
|
|
23
22
|
/// Convert a tiny_http::Request into a Tish Value object.
|
|
24
23
|
pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
|
|
25
|
-
let mut obj:
|
|
24
|
+
let mut obj: PropMap = PropMap::with_capacity(6);
|
|
26
25
|
|
|
27
26
|
obj.insert(
|
|
28
27
|
Arc::from("method"),
|
|
@@ -39,7 +38,7 @@ pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
|
|
|
39
38
|
let query_string = request.url().split('?').nth(1).unwrap_or("");
|
|
40
39
|
obj.insert(Arc::from("query"), Value::String(query_string.into()));
|
|
41
40
|
|
|
42
|
-
let mut headers_obj:
|
|
41
|
+
let mut headers_obj: PropMap = PropMap::with_capacity(request.headers().len());
|
|
43
42
|
for header in request.headers() {
|
|
44
43
|
headers_obj.insert(
|
|
45
44
|
Arc::from(header.field.as_str().as_str()),
|
|
@@ -14,6 +14,7 @@ pub mod regex;
|
|
|
14
14
|
mod value;
|
|
15
15
|
|
|
16
16
|
pub use eval::Evaluator;
|
|
17
|
+
pub use value::PropMap;
|
|
17
18
|
pub use value::Value;
|
|
18
19
|
|
|
19
20
|
/// Trait for pluggable native modules (e.g. Polars). Implement to register
|
|
@@ -21,6 +22,12 @@ pub use value::Value;
|
|
|
21
22
|
pub trait TishNativeModule: Send + Sync {
|
|
22
23
|
fn name(&self) -> &'static str;
|
|
23
24
|
fn register(&self) -> std::collections::HashMap<std::sync::Arc<str>, Value>;
|
|
25
|
+
|
|
26
|
+
/// Virtual `tish:*` modules for `import { x } from 'tish:…'` (e.g. `tish:polars`).
|
|
27
|
+
/// Return `(specifier, exports_object)` pairs. Default: none.
|
|
28
|
+
fn virtual_builtin_modules(&self) -> Vec<(&'static str, Value)> {
|
|
29
|
+
vec![]
|
|
30
|
+
}
|
|
24
31
|
}
|
|
25
32
|
#[cfg(feature = "regex")]
|
|
26
33
|
pub use regex::TishRegExp;
|