@tishlang/tish 1.0.29 → 1.0.33
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 +233 -71
- package/crates/tish_compile/src/lib.rs +35 -0
- 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 +10 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +149 -72
- package/crates/tish_eval/src/http.rs +3 -4
- 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_lsp/README.md +1 -1
- package/crates/tish_native/src/build.rs +16 -2
- 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 +161 -50
- package/crates/tish_runtime/src/http.rs +4 -5
- package/crates/tish_runtime/src/http_fetch.rs +9 -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
|
}))
|
|
@@ -85,7 +85,7 @@ impl Evaluator {
|
|
|
85
85
|
let scope = Scope::new();
|
|
86
86
|
{
|
|
87
87
|
let mut s = scope.borrow_mut();
|
|
88
|
-
let mut console =
|
|
88
|
+
let mut console = PropMap::with_capacity(5);
|
|
89
89
|
console.insert("debug".into(), Value::Native(natives::console_debug));
|
|
90
90
|
console.insert("info".into(), Value::Native(natives::console_info));
|
|
91
91
|
console.insert("log".into(), Value::Native(natives::console_log));
|
|
@@ -101,7 +101,7 @@ impl Evaluator {
|
|
|
101
101
|
s.set("isNaN".into(), Value::Native(natives::is_nan), true);
|
|
102
102
|
s.set("Infinity".into(), Value::Number(f64::INFINITY), true);
|
|
103
103
|
s.set("NaN".into(), Value::Number(f64::NAN), true);
|
|
104
|
-
let mut math =
|
|
104
|
+
let mut math = PropMap::with_capacity(18);
|
|
105
105
|
math.insert("abs".into(), Value::Native(natives::math_abs));
|
|
106
106
|
math.insert("sqrt".into(), Value::Native(natives::math_sqrt));
|
|
107
107
|
math.insert("min".into(), Value::Native(natives::math_min));
|
|
@@ -122,12 +122,12 @@ impl Evaluator {
|
|
|
122
122
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
123
123
|
s.set("Math".into(), Value::Object(Rc::new(RefCell::new(math))), true);
|
|
124
124
|
|
|
125
|
-
let mut json =
|
|
125
|
+
let mut json = PropMap::with_capacity(2);
|
|
126
126
|
json.insert("parse".into(), Value::Native(Self::json_parse_native));
|
|
127
127
|
json.insert("stringify".into(), Value::Native(Self::json_stringify_native));
|
|
128
128
|
s.set("JSON".into(), Value::Object(Rc::new(RefCell::new(json))), true);
|
|
129
129
|
|
|
130
|
-
let mut object =
|
|
130
|
+
let mut object = PropMap::with_capacity(5);
|
|
131
131
|
object.insert("keys".into(), Value::Native(Self::object_keys));
|
|
132
132
|
object.insert("values".into(), Value::Native(Self::object_values));
|
|
133
133
|
object.insert("entries".into(), Value::Native(Self::object_entries));
|
|
@@ -135,18 +135,33 @@ impl Evaluator {
|
|
|
135
135
|
object.insert("fromEntries".into(), Value::Native(Self::object_from_entries));
|
|
136
136
|
s.set("Object".into(), Value::Object(Rc::new(RefCell::new(object))), true);
|
|
137
137
|
|
|
138
|
-
let mut array_obj =
|
|
138
|
+
let mut array_obj = PropMap::with_capacity(1);
|
|
139
139
|
array_obj.insert("isArray".into(), Value::Native(natives::array_is_array));
|
|
140
140
|
s.set("Array".into(), Value::Object(Rc::new(RefCell::new(array_obj))), true);
|
|
141
141
|
|
|
142
|
-
let mut string_obj =
|
|
142
|
+
let mut string_obj = PropMap::with_capacity(1);
|
|
143
143
|
string_obj.insert("fromCharCode".into(), Value::Native(natives::string_from_char_code));
|
|
144
144
|
s.set("String".into(), Value::Object(Rc::new(RefCell::new(string_obj))), true);
|
|
145
145
|
|
|
146
|
-
let mut date =
|
|
146
|
+
let mut date = PropMap::with_capacity(1);
|
|
147
147
|
date.insert("now".into(), Value::Native(natives::date_now));
|
|
148
148
|
s.set("Date".into(), Value::Object(Rc::new(RefCell::new(date))), true);
|
|
149
149
|
|
|
150
|
+
s.set(
|
|
151
|
+
"Uint8Array".into(),
|
|
152
|
+
crate::value_convert::core_to_eval(
|
|
153
|
+
tishlang_builtins::construct::uint8_array_constructor_value(),
|
|
154
|
+
),
|
|
155
|
+
true,
|
|
156
|
+
);
|
|
157
|
+
s.set(
|
|
158
|
+
"AudioContext".into(),
|
|
159
|
+
crate::value_convert::core_to_eval(
|
|
160
|
+
tishlang_builtins::construct::audio_context_constructor_value(),
|
|
161
|
+
),
|
|
162
|
+
true,
|
|
163
|
+
);
|
|
164
|
+
|
|
150
165
|
#[cfg(feature = "regex")]
|
|
151
166
|
{
|
|
152
167
|
s.set("RegExp".into(), Value::Native(Self::regexp_constructor_native), true);
|
|
@@ -323,14 +338,11 @@ impl Evaluator {
|
|
|
323
338
|
body,
|
|
324
339
|
..
|
|
325
340
|
} => {
|
|
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();
|
|
341
|
+
let formals: Arc<[FunParam]> = Arc::from(params.clone());
|
|
329
342
|
let rest_param_name = rest_param.as_ref().map(|p| Arc::clone(&p.name));
|
|
330
343
|
let body = Arc::new(body.as_ref().clone());
|
|
331
344
|
let func = Value::Function {
|
|
332
|
-
|
|
333
|
-
defaults,
|
|
345
|
+
formals,
|
|
334
346
|
rest_param: rest_param_name,
|
|
335
347
|
body,
|
|
336
348
|
};
|
|
@@ -536,7 +548,7 @@ impl Evaluator {
|
|
|
536
548
|
let _ = self.eval_statement(stmt);
|
|
537
549
|
}
|
|
538
550
|
}
|
|
539
|
-
let mut exports:
|
|
551
|
+
let mut exports: PropMap = PropMap::default();
|
|
540
552
|
for name in export_names {
|
|
541
553
|
if let Some(v) = module_scope.borrow().get(&name) {
|
|
542
554
|
exports.insert(Arc::from(name.as_str()), v);
|
|
@@ -576,7 +588,7 @@ impl Evaluator {
|
|
|
576
588
|
"tish:fs" => {
|
|
577
589
|
#[cfg(feature = "fs")]
|
|
578
590
|
{
|
|
579
|
-
let mut exports:
|
|
591
|
+
let mut exports: PropMap = PropMap::default();
|
|
580
592
|
exports.insert("readFile".into(), Value::Native(natives::read_file));
|
|
581
593
|
exports.insert("writeFile".into(), Value::Native(natives::write_file));
|
|
582
594
|
exports.insert("fileExists".into(), Value::Native(natives::file_exists));
|
|
@@ -595,7 +607,7 @@ impl Evaluator {
|
|
|
595
607
|
"tish:http" => {
|
|
596
608
|
#[cfg(feature = "http")]
|
|
597
609
|
{
|
|
598
|
-
let mut exports:
|
|
610
|
+
let mut exports: PropMap = PropMap::default();
|
|
599
611
|
exports.insert("fetch".into(), Value::Native(Self::fetch_native));
|
|
600
612
|
exports.insert("fetchAll".into(), Value::Native(Self::fetch_all_native));
|
|
601
613
|
exports.insert("serve".into(), Value::Serve);
|
|
@@ -616,7 +628,7 @@ impl Evaluator {
|
|
|
616
628
|
"tish:ws" => {
|
|
617
629
|
#[cfg(feature = "ws")]
|
|
618
630
|
{
|
|
619
|
-
let mut exports:
|
|
631
|
+
let mut exports: PropMap = PropMap::default();
|
|
620
632
|
exports.insert("WebSocket".into(), Value::Native(Self::ws_web_socket_native));
|
|
621
633
|
exports.insert("Server".into(), Value::Native(Self::ws_server_native));
|
|
622
634
|
exports.insert("wsSend".into(), Value::Native(Self::ws_send_native));
|
|
@@ -633,7 +645,7 @@ impl Evaluator {
|
|
|
633
645
|
"tish:process" => {
|
|
634
646
|
#[cfg(feature = "process")]
|
|
635
647
|
{
|
|
636
|
-
let mut exports:
|
|
648
|
+
let mut exports: PropMap = PropMap::default();
|
|
637
649
|
exports.insert("exit".into(), Value::Native(natives::process_exit));
|
|
638
650
|
exports.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
639
651
|
exports.insert("exec".into(), Value::Native(natives::process_exec));
|
|
@@ -641,11 +653,11 @@ impl Evaluator {
|
|
|
641
653
|
.map(|s| Value::String(s.into()))
|
|
642
654
|
.collect();
|
|
643
655
|
exports.insert("argv".into(), Value::Array(Rc::new(RefCell::new(argv.clone()))));
|
|
644
|
-
let env_obj:
|
|
656
|
+
let env_obj: PropMap = std::env::vars()
|
|
645
657
|
.map(|(key, value)| (Arc::from(key.as_str()), Value::String(value.into())))
|
|
646
658
|
.collect();
|
|
647
659
|
exports.insert("env".into(), Value::Object(Rc::new(RefCell::new(env_obj.clone()))));
|
|
648
|
-
let mut process_obj =
|
|
660
|
+
let mut process_obj = PropMap::default();
|
|
649
661
|
process_obj.insert("exit".into(), Value::Native(natives::process_exit));
|
|
650
662
|
process_obj.insert("cwd".into(), Value::Native(natives::process_cwd));
|
|
651
663
|
process_obj.insert("exec".into(), Value::Native(natives::process_exec));
|
|
@@ -1534,7 +1546,7 @@ impl Evaluator {
|
|
|
1534
1546
|
Ok(Value::Array(Rc::new(RefCell::new(vals))))
|
|
1535
1547
|
}
|
|
1536
1548
|
Expr::Object { props, .. } => {
|
|
1537
|
-
let mut map =
|
|
1549
|
+
let mut map = PropMap::default();
|
|
1538
1550
|
for prop in props {
|
|
1539
1551
|
match prop {
|
|
1540
1552
|
tishlang_ast::ObjectProp::KeyValue(k, v) => {
|
|
@@ -1561,6 +1573,11 @@ impl Evaluator {
|
|
|
1561
1573
|
}
|
|
1562
1574
|
}
|
|
1563
1575
|
Expr::Await { operand, .. } => self.eval_await(operand),
|
|
1576
|
+
Expr::New { callee, args, .. } => {
|
|
1577
|
+
let c = self.eval_expr(callee)?;
|
|
1578
|
+
let arg_vals = self.eval_call_args(args)?;
|
|
1579
|
+
self.construct_value(&c, &arg_vals)
|
|
1580
|
+
}
|
|
1564
1581
|
Expr::JsxElement { .. } | Expr::JsxFragment { .. } => Err(EvalError::Error(
|
|
1565
1582
|
"JSX is not supported in the interpreter. Use 'tish compile --target js' to compile to JavaScript.".to_string(),
|
|
1566
1583
|
)),
|
|
@@ -1577,7 +1594,6 @@ impl Evaluator {
|
|
|
1577
1594
|
Value::Array(_) => "object".into(),
|
|
1578
1595
|
Value::Object(_) => "object".into(),
|
|
1579
1596
|
Value::Function { .. } | Value::Native(_) => "function".into(),
|
|
1580
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
1581
1597
|
Value::CoreFn(_) => "function".into(),
|
|
1582
1598
|
#[cfg(feature = "http")]
|
|
1583
1599
|
Value::CorePromise(_) => "object".into(),
|
|
@@ -1755,9 +1771,7 @@ impl Evaluator {
|
|
|
1755
1771
|
}
|
|
1756
1772
|
Expr::ArrowFunction { params, body, .. } => {
|
|
1757
1773
|
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();
|
|
1774
|
+
let formals: Arc<[FunParam]> = Arc::from(params.clone());
|
|
1761
1775
|
let body_stmt = match body {
|
|
1762
1776
|
ArrowBody::Expr(expr) => {
|
|
1763
1777
|
// Expression body: wrap in implicit return
|
|
@@ -1769,8 +1783,7 @@ impl Evaluator {
|
|
|
1769
1783
|
ArrowBody::Block(stmt) => stmt.as_ref().clone(),
|
|
1770
1784
|
};
|
|
1771
1785
|
Ok(Value::Function {
|
|
1772
|
-
|
|
1773
|
-
defaults,
|
|
1786
|
+
formals,
|
|
1774
1787
|
rest_param: None,
|
|
1775
1788
|
body: Arc::new(body_stmt),
|
|
1776
1789
|
})
|
|
@@ -1862,18 +1875,21 @@ impl Evaluator {
|
|
|
1862
1875
|
/// descending = false: checks for `(a, b) => a - b`
|
|
1863
1876
|
/// descending = true: checks for `(a, b) => b - a`
|
|
1864
1877
|
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()) {
|
|
1878
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
1879
|
+
// Must have exactly 2 simple params, no defaults, no rest
|
|
1880
|
+
if formals.len() != 2 || rest_param.is_some() {
|
|
1871
1881
|
return false;
|
|
1872
1882
|
}
|
|
1883
|
+
let (param_a, param_b) = match (&formals[0], &formals[1]) {
|
|
1884
|
+
(FunParam::Simple(a), FunParam::Simple(b))
|
|
1885
|
+
if a.default.is_none() && b.default.is_none() =>
|
|
1886
|
+
{
|
|
1887
|
+
(&a.name, &b.name)
|
|
1888
|
+
}
|
|
1889
|
+
_ => return false,
|
|
1890
|
+
};
|
|
1873
1891
|
|
|
1874
1892
|
// 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
1893
|
|
|
1878
1894
|
// Check for both Statement::Return and Statement::ExprStmt (arrow implicit return)
|
|
1879
1895
|
let expr = match body.as_ref() {
|
|
@@ -1947,20 +1963,32 @@ impl Evaluator {
|
|
|
1947
1963
|
/// Optimized callback invocation for array methods.
|
|
1948
1964
|
/// Creates a reusable scope that can be updated for each iteration.
|
|
1949
1965
|
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()) {
|
|
1966
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
1967
|
+
if rest_param.is_some() {
|
|
1953
1968
|
return None;
|
|
1954
1969
|
}
|
|
1970
|
+
for fp in formals.iter() {
|
|
1971
|
+
match fp {
|
|
1972
|
+
FunParam::Simple(tp) if tp.default.is_none() => {}
|
|
1973
|
+
_ => return None,
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1955
1976
|
let scope = Scope::child(Rc::clone(&self.scope));
|
|
1956
|
-
// Pre-initialize parameters to Null
|
|
1957
1977
|
{
|
|
1958
1978
|
let mut s = scope.borrow_mut();
|
|
1959
|
-
for
|
|
1960
|
-
|
|
1979
|
+
for fp in formals.iter() {
|
|
1980
|
+
for n in fp.bound_names() {
|
|
1981
|
+
s.set(n, Value::Null, true);
|
|
1982
|
+
}
|
|
1961
1983
|
}
|
|
1962
1984
|
}
|
|
1963
|
-
|
|
1985
|
+
let flat_names: Arc<[Arc<str>]> = Arc::from(
|
|
1986
|
+
formals
|
|
1987
|
+
.iter()
|
|
1988
|
+
.flat_map(|fp| fp.bound_names())
|
|
1989
|
+
.collect::<Vec<_>>(),
|
|
1990
|
+
);
|
|
1991
|
+
return Some((scope, flat_names, Arc::clone(body)));
|
|
1964
1992
|
}
|
|
1965
1993
|
None
|
|
1966
1994
|
}
|
|
@@ -2002,12 +2030,14 @@ impl Evaluator {
|
|
|
2002
2030
|
f: &Value,
|
|
2003
2031
|
args: &[Value],
|
|
2004
2032
|
) -> 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()) {
|
|
2033
|
+
if let Value::Function { formals, body, rest_param, .. } = f {
|
|
2034
|
+
if formals.len() != 1 || rest_param.is_some() {
|
|
2008
2035
|
return None;
|
|
2009
2036
|
}
|
|
2010
|
-
let param_name = &
|
|
2037
|
+
let param_name = match &formals[0] {
|
|
2038
|
+
FunParam::Simple(tp) if tp.default.is_none() => &tp.name,
|
|
2039
|
+
_ => return None,
|
|
2040
|
+
};
|
|
2011
2041
|
let arg = args.first().cloned().unwrap_or(Value::Null);
|
|
2012
2042
|
|
|
2013
2043
|
// Get the expression from the body
|
|
@@ -2078,6 +2108,27 @@ impl Evaluator {
|
|
|
2078
2108
|
}
|
|
2079
2109
|
}
|
|
2080
2110
|
|
|
2111
|
+
/// Host `new`: `__construct` on objects; otherwise same callables as `call_func`, else null.
|
|
2112
|
+
fn construct_value(&self, callee: &Value, args: &[Value]) -> Result<Value, EvalError> {
|
|
2113
|
+
if let Value::Object(o) = callee {
|
|
2114
|
+
if let Some(ctor) = o.borrow().get(&Arc::from("__construct")) {
|
|
2115
|
+
return self.call_func(ctor, args);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
match callee {
|
|
2119
|
+
Value::Native(_) | Value::Function { .. } | Value::CoreFn(_) => {
|
|
2120
|
+
self.call_func(callee, args)
|
|
2121
|
+
}
|
|
2122
|
+
#[cfg(feature = "http")]
|
|
2123
|
+
Value::PromiseConstructor
|
|
2124
|
+
| Value::Serve
|
|
2125
|
+
| Value::BoundPromiseMethod(_, _)
|
|
2126
|
+
| Value::TimerBuiltin(_) => self.call_func(callee, args),
|
|
2127
|
+
Value::OpaqueMethod(_, _) => self.call_func(callee, args),
|
|
2128
|
+
_ => Ok(Value::Null),
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2081
2132
|
fn call_func(&self, f: &Value, args: &[Value]) -> Result<Value, EvalError> {
|
|
2082
2133
|
match f {
|
|
2083
2134
|
Value::Native(native_fn) => {
|
|
@@ -2142,7 +2193,6 @@ impl Evaluator {
|
|
|
2142
2193
|
}
|
|
2143
2194
|
#[cfg(feature = "http")]
|
|
2144
2195
|
Value::Serve => self.run_http_server(args),
|
|
2145
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
2146
2196
|
Value::CoreFn(f) => {
|
|
2147
2197
|
let ca: Result<Vec<tishlang_core::Value>, String> =
|
|
2148
2198
|
args.iter().map(crate::value_convert::eval_to_core).collect();
|
|
@@ -2167,15 +2217,19 @@ impl Evaluator {
|
|
|
2167
2217
|
let result = method(&core_args);
|
|
2168
2218
|
Ok(crate::value_convert::core_to_eval(result))
|
|
2169
2219
|
}
|
|
2170
|
-
Value::Function {
|
|
2220
|
+
Value::Function { formals, rest_param, body } => {
|
|
2171
2221
|
let scope = Scope::child(Rc::clone(&self.scope));
|
|
2172
2222
|
{
|
|
2173
2223
|
let mut s = scope.borrow_mut();
|
|
2174
|
-
for (i,
|
|
2224
|
+
for (i, formal) in formals.iter().enumerate() {
|
|
2175
2225
|
let val = match args.get(i) {
|
|
2176
2226
|
Some(v) => v.clone(),
|
|
2177
2227
|
None => {
|
|
2178
|
-
|
|
2228
|
+
let def = match formal {
|
|
2229
|
+
FunParam::Simple(tp) => tp.default.as_ref(),
|
|
2230
|
+
FunParam::Destructure { default, .. } => default.as_ref(),
|
|
2231
|
+
};
|
|
2232
|
+
if let Some(default_expr) = def {
|
|
2179
2233
|
drop(s);
|
|
2180
2234
|
let default_val = self.eval_expr(default_expr)?;
|
|
2181
2235
|
s = scope.borrow_mut();
|
|
@@ -2185,10 +2239,19 @@ impl Evaluator {
|
|
|
2185
2239
|
}
|
|
2186
2240
|
}
|
|
2187
2241
|
};
|
|
2188
|
-
|
|
2242
|
+
match formal {
|
|
2243
|
+
FunParam::Simple(tp) => {
|
|
2244
|
+
s.set(Arc::clone(&tp.name), val, true);
|
|
2245
|
+
}
|
|
2246
|
+
FunParam::Destructure { pattern, .. } => {
|
|
2247
|
+
drop(s);
|
|
2248
|
+
Self::bind_destruct_pattern_scoped(&scope, pattern, &val, true)?;
|
|
2249
|
+
s = scope.borrow_mut();
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2189
2252
|
}
|
|
2190
2253
|
if let Some(ref rest_name) = rest_param {
|
|
2191
|
-
let rest_vals: Vec<Value> = args.iter().skip(
|
|
2254
|
+
let rest_vals: Vec<Value> = args.iter().skip(formals.len()).cloned().collect();
|
|
2192
2255
|
s.set(Arc::clone(rest_name), Value::Array(Rc::new(RefCell::new(rest_vals))), true);
|
|
2193
2256
|
}
|
|
2194
2257
|
}
|
|
@@ -2449,13 +2512,13 @@ impl Evaluator {
|
|
|
2449
2512
|
let response_value = match self.call_func(&handler, &[req_value]) {
|
|
2450
2513
|
Ok(v) => v,
|
|
2451
2514
|
Err(EvalError::Throw(v)) => {
|
|
2452
|
-
let mut err_obj:
|
|
2515
|
+
let mut err_obj: PropMap = PropMap::with_capacity(2);
|
|
2453
2516
|
err_obj.insert(Arc::from("status"), Value::Number(500.0));
|
|
2454
2517
|
err_obj.insert(Arc::from("body"), Value::String(v.to_string().into()));
|
|
2455
2518
|
Value::Object(Rc::new(RefCell::new(err_obj)))
|
|
2456
2519
|
}
|
|
2457
2520
|
Err(e) => {
|
|
2458
|
-
let mut err_obj:
|
|
2521
|
+
let mut err_obj: PropMap = PropMap::with_capacity(2);
|
|
2459
2522
|
err_obj.insert(Arc::from("status"), Value::Number(500.0));
|
|
2460
2523
|
err_obj.insert(Arc::from("body"), Value::String(e.to_string().into()));
|
|
2461
2524
|
Value::Object(Rc::new(RefCell::new(err_obj)))
|
|
@@ -2491,28 +2554,37 @@ impl Evaluator {
|
|
|
2491
2554
|
Ok(result)
|
|
2492
2555
|
}
|
|
2493
2556
|
|
|
2494
|
-
fn
|
|
2557
|
+
fn bind_destruct_pattern_scoped(
|
|
2558
|
+
scope: &Rc<RefCell<Scope>>,
|
|
2559
|
+
pattern: &tishlang_ast::DestructPattern,
|
|
2560
|
+
value: &Value,
|
|
2561
|
+
mutable: bool,
|
|
2562
|
+
) -> Result<(), EvalError> {
|
|
2495
2563
|
match pattern {
|
|
2496
2564
|
tishlang_ast::DestructPattern::Array(elements) => {
|
|
2497
2565
|
let arr = match value {
|
|
2498
2566
|
Value::Array(a) => a.borrow().clone(),
|
|
2499
2567
|
_ => return Err(EvalError::Error("Cannot destructure non-array value".to_string())),
|
|
2500
2568
|
};
|
|
2501
|
-
|
|
2569
|
+
|
|
2502
2570
|
for (i, elem) in elements.iter().enumerate() {
|
|
2503
2571
|
if let Some(el) = elem {
|
|
2504
2572
|
match el {
|
|
2505
2573
|
tishlang_ast::DestructElement::Ident(name) => {
|
|
2506
2574
|
let val = arr.get(i).cloned().unwrap_or(Value::Null);
|
|
2507
|
-
|
|
2575
|
+
scope.borrow_mut().set(Arc::clone(name), val, mutable);
|
|
2508
2576
|
}
|
|
2509
2577
|
tishlang_ast::DestructElement::Pattern(nested) => {
|
|
2510
2578
|
let val = arr.get(i).cloned().unwrap_or(Value::Null);
|
|
2511
|
-
|
|
2579
|
+
Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
|
|
2512
2580
|
}
|
|
2513
2581
|
tishlang_ast::DestructElement::Rest(name) => {
|
|
2514
2582
|
let rest: Vec<Value> = arr.iter().skip(i).cloned().collect();
|
|
2515
|
-
|
|
2583
|
+
scope.borrow_mut().set(
|
|
2584
|
+
Arc::clone(name),
|
|
2585
|
+
Value::Array(Rc::new(RefCell::new(rest))),
|
|
2586
|
+
mutable,
|
|
2587
|
+
);
|
|
2516
2588
|
break;
|
|
2517
2589
|
}
|
|
2518
2590
|
}
|
|
@@ -2524,18 +2596,20 @@ impl Evaluator {
|
|
|
2524
2596
|
Value::Object(o) => o.borrow().clone(),
|
|
2525
2597
|
_ => return Err(EvalError::Error("Cannot destructure non-object value".to_string())),
|
|
2526
2598
|
};
|
|
2527
|
-
|
|
2599
|
+
|
|
2528
2600
|
for prop in props {
|
|
2529
2601
|
let val = obj.get(&prop.key).cloned().unwrap_or(Value::Null);
|
|
2530
2602
|
match &prop.value {
|
|
2531
2603
|
tishlang_ast::DestructElement::Ident(name) => {
|
|
2532
|
-
|
|
2604
|
+
scope.borrow_mut().set(Arc::clone(name), val, mutable);
|
|
2533
2605
|
}
|
|
2534
2606
|
tishlang_ast::DestructElement::Pattern(nested) => {
|
|
2535
|
-
|
|
2607
|
+
Self::bind_destruct_pattern_scoped(scope, nested, &val, mutable)?;
|
|
2536
2608
|
}
|
|
2537
2609
|
tishlang_ast::DestructElement::Rest(_) => {
|
|
2538
|
-
return Err(EvalError::Error(
|
|
2610
|
+
return Err(EvalError::Error(
|
|
2611
|
+
"Rest not supported in object destructuring".to_string(),
|
|
2612
|
+
));
|
|
2539
2613
|
}
|
|
2540
2614
|
}
|
|
2541
2615
|
}
|
|
@@ -2544,6 +2618,10 @@ impl Evaluator {
|
|
|
2544
2618
|
Ok(())
|
|
2545
2619
|
}
|
|
2546
2620
|
|
|
2621
|
+
fn bind_destruct_pattern(&mut self, pattern: &tishlang_ast::DestructPattern, value: &Value, mutable: bool) -> Result<(), EvalError> {
|
|
2622
|
+
Self::bind_destruct_pattern_scoped(&self.scope, pattern, value, mutable)
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2547
2625
|
fn get_prop(&self, obj: &Value, key: &str) -> Result<Value, String> {
|
|
2548
2626
|
match obj {
|
|
2549
2627
|
Value::Object(map) => Ok(map.borrow().get(key).cloned().unwrap_or(Value::Null)),
|
|
@@ -2745,9 +2823,9 @@ impl Evaluator {
|
|
|
2745
2823
|
fn json_parse_object(s: &str) -> Result<Value, ()> {
|
|
2746
2824
|
let s = s[1..].trim_start();
|
|
2747
2825
|
if s.starts_with('}') {
|
|
2748
|
-
return Ok(Value::Object(Rc::new(RefCell::new(
|
|
2826
|
+
return Ok(Value::Object(Rc::new(RefCell::new(PropMap::default()))));
|
|
2749
2827
|
}
|
|
2750
|
-
let mut map =
|
|
2828
|
+
let mut map = PropMap::default();
|
|
2751
2829
|
let mut rest = s;
|
|
2752
2830
|
loop {
|
|
2753
2831
|
if !rest.starts_with('"') {
|
|
@@ -2873,7 +2951,6 @@ impl Evaluator {
|
|
|
2873
2951
|
Value::Function { .. } | Value::Native(_) => "null".to_string(),
|
|
2874
2952
|
#[cfg(feature = "http")]
|
|
2875
2953
|
Value::CorePromise(_) => "null".to_string(),
|
|
2876
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
2877
2954
|
Value::CoreFn(_) => "null".to_string(),
|
|
2878
2955
|
#[cfg(feature = "http")]
|
|
2879
2956
|
Value::Serve
|
|
@@ -2949,7 +3026,7 @@ impl Evaluator {
|
|
|
2949
3026
|
|
|
2950
3027
|
fn object_from_entries(args: &[Value]) -> Result<Value, String> {
|
|
2951
3028
|
if let Some(Value::Array(arr)) = args.first() {
|
|
2952
|
-
let mut map =
|
|
3029
|
+
let mut map = PropMap::default();
|
|
2953
3030
|
for entry in arr.borrow().iter() {
|
|
2954
3031
|
if let Value::Array(pair) = entry {
|
|
2955
3032
|
let pair = pair.borrow();
|
|
@@ -2961,7 +3038,7 @@ impl Evaluator {
|
|
|
2961
3038
|
}
|
|
2962
3039
|
Ok(Value::Object(Rc::new(RefCell::new(map))))
|
|
2963
3040
|
} else {
|
|
2964
|
-
Ok(Value::Object(Rc::new(RefCell::new(
|
|
3041
|
+
Ok(Value::Object(Rc::new(RefCell::new(PropMap::default()))))
|
|
2965
3042
|
}
|
|
2966
3043
|
}
|
|
2967
3044
|
|
|
@@ -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()),
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
//! Re-exports core types from tishlang_core and provides interpreter-specific functionality.
|
|
4
4
|
|
|
5
5
|
use std::cell::RefCell;
|
|
6
|
-
use std::collections::HashMap;
|
|
7
6
|
use std::rc::Rc;
|
|
8
7
|
use std::sync::Arc;
|
|
9
8
|
|
|
10
9
|
pub use tishlang_core::{RegExpFlags, TishRegExp};
|
|
11
10
|
|
|
11
|
+
use crate::value::PropMap;
|
|
12
|
+
|
|
12
13
|
use crate::value::Value;
|
|
13
14
|
|
|
14
15
|
/// RegExp.prototype.exec(string) - returns match object (array-like with index) or null
|
|
@@ -42,7 +43,7 @@ pub fn regexp_exec(re: &mut TishRegExp, input: &str) -> Value {
|
|
|
42
43
|
let match_byte_start = byte_start + full_match.start();
|
|
43
44
|
let match_char_index = input[..match_byte_start].chars().count();
|
|
44
45
|
|
|
45
|
-
let mut obj:
|
|
46
|
+
let mut obj: PropMap = PropMap::default();
|
|
46
47
|
obj.insert(Arc::from("0"), Value::String(full_match.as_str().into()));
|
|
47
48
|
for i in 1..caps.len() {
|
|
48
49
|
let val = match caps.get(i) {
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
//! different shape (no AST-carrying variants). The split is intentional.
|
|
7
7
|
|
|
8
8
|
use std::cell::RefCell;
|
|
9
|
-
use std::collections::HashMap;
|
|
10
9
|
use std::rc::Rc;
|
|
11
10
|
use std::sync::Arc;
|
|
12
11
|
|
|
13
|
-
use
|
|
14
|
-
|
|
12
|
+
use ahash::AHashMap;
|
|
13
|
+
use tishlang_ast::{FunParam, Statement};
|
|
15
14
|
use tishlang_core::NativeFn as CoreNativeFn;
|
|
15
|
+
|
|
16
|
+
/// Property map for interpreter `Value::Object` (uses `eval::Value`, not `tishlang_core::Value`).
|
|
17
|
+
pub type PropMap = AHashMap<Arc<str>, Value>;
|
|
16
18
|
#[cfg(feature = "http")]
|
|
17
19
|
use tishlang_core::TishPromise;
|
|
18
20
|
use tishlang_core::TishOpaque;
|
|
@@ -32,11 +34,10 @@ pub enum Value {
|
|
|
32
34
|
Bool(bool),
|
|
33
35
|
Null,
|
|
34
36
|
Array(Rc<RefCell<Vec<Value>>>),
|
|
35
|
-
Object(Rc<RefCell<
|
|
37
|
+
Object(Rc<RefCell<PropMap>>),
|
|
36
38
|
/// User-defined function with AST body
|
|
37
39
|
Function {
|
|
38
|
-
|
|
39
|
-
defaults: Arc<[Option<Expr>]>,
|
|
40
|
+
formals: Arc<[FunParam]>,
|
|
40
41
|
rest_param: Option<Arc<str>>,
|
|
41
42
|
body: Arc<Statement>,
|
|
42
43
|
},
|
|
@@ -65,8 +66,7 @@ pub enum Value {
|
|
|
65
66
|
/// Native `tishlang_core` Promise (fetch / reader.read / response.text).
|
|
66
67
|
#[cfg(feature = "http")]
|
|
67
68
|
CorePromise(Arc<dyn TishPromise>),
|
|
68
|
-
/// `tishlang_core::Value::Function` (
|
|
69
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
69
|
+
/// `tishlang_core::Value::Function` (native callbacks, `new` constructors, fetch/ws when enabled).
|
|
70
70
|
CoreFn(CoreNativeFn),
|
|
71
71
|
/// Opaque handle to a native Rust type (e.g. Polars DataFrame).
|
|
72
72
|
Opaque(Arc<dyn TishOpaque>),
|
|
@@ -99,7 +99,6 @@ impl std::fmt::Debug for Value {
|
|
|
99
99
|
Value::BoundPromiseMethod(_, _) | Value::TimerBuiltin(_) => write!(f, "[Function]"),
|
|
100
100
|
#[cfg(feature = "http")]
|
|
101
101
|
Value::CorePromise(_) => write!(f, "Promise"),
|
|
102
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
103
102
|
Value::CoreFn(_) => write!(f, "CoreFn"),
|
|
104
103
|
Value::Opaque(o) => write!(f, "{}(opaque)", o.type_name()),
|
|
105
104
|
Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
|
|
@@ -155,7 +154,6 @@ impl std::fmt::Display for Value {
|
|
|
155
154
|
Value::BoundPromiseMethod(_, _) | Value::TimerBuiltin(_) => write!(f, "[Function]"),
|
|
156
155
|
#[cfg(feature = "http")]
|
|
157
156
|
Value::CorePromise(_) => write!(f, "[Promise]"),
|
|
158
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
159
157
|
Value::CoreFn(_) => write!(f, "[Function]"),
|
|
160
158
|
Value::Opaque(o) => write!(f, "[object {}]", o.type_name()),
|
|
161
159
|
Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
|
|
@@ -199,8 +197,8 @@ impl Value {
|
|
|
199
197
|
Value::Array(Rc::new(RefCell::new(items)))
|
|
200
198
|
}
|
|
201
199
|
|
|
202
|
-
/// Create a new object Value from a
|
|
203
|
-
pub fn object(map:
|
|
200
|
+
/// Create a new object Value from a property map.
|
|
201
|
+
pub fn object(map: PropMap) -> Self {
|
|
204
202
|
Value::Object(Rc::new(RefCell::new(map)))
|
|
205
203
|
}
|
|
206
204
|
|
|
@@ -211,7 +209,7 @@ impl Value {
|
|
|
211
209
|
|
|
212
210
|
/// Create an empty object Value.
|
|
213
211
|
pub fn empty_object() -> Self {
|
|
214
|
-
Value::Object(Rc::new(RefCell::new(
|
|
212
|
+
Value::Object(Rc::new(RefCell::new(PropMap::default())))
|
|
215
213
|
}
|
|
216
214
|
|
|
217
215
|
/// Extract the number value, if this is a Number.
|