@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
|
@@ -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.
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
//! Conversion between tishlang_eval::Value and tishlang_core::Value for opaque method calls.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
-
use std::collections::HashMap;
|
|
5
4
|
use std::rc::Rc;
|
|
6
5
|
use std::sync::Arc;
|
|
7
6
|
|
|
8
|
-
use tishlang_core::Value as CoreValue;
|
|
7
|
+
use tishlang_core::{ObjectMap, Value as CoreValue};
|
|
9
8
|
|
|
10
|
-
use crate::value::Value;
|
|
9
|
+
use crate::value::{PropMap, Value};
|
|
11
10
|
|
|
12
11
|
/// Convert interpreter Value to core Value. Fails for interpreter-only variants.
|
|
13
12
|
pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
@@ -24,7 +23,7 @@ pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
|
24
23
|
Ok(CoreValue::Array(Rc::new(RefCell::new(out))))
|
|
25
24
|
}
|
|
26
25
|
Value::Object(map) => {
|
|
27
|
-
let mut out =
|
|
26
|
+
let mut out = ObjectMap::default();
|
|
28
27
|
for (k, v) in map.borrow().iter() {
|
|
29
28
|
out.insert(Arc::clone(k), eval_to_core(v)?);
|
|
30
29
|
}
|
|
@@ -53,7 +52,7 @@ pub fn core_to_eval(v: CoreValue) -> Value {
|
|
|
53
52
|
Value::Array(Rc::new(RefCell::new(out)))
|
|
54
53
|
}
|
|
55
54
|
CoreValue::Object(map) => {
|
|
56
|
-
let mut out =
|
|
55
|
+
let mut out = PropMap::default();
|
|
57
56
|
for (k, v) in map.borrow().iter() {
|
|
58
57
|
out.insert(Arc::clone(k), core_to_eval(v.clone()));
|
|
59
58
|
}
|
|
@@ -64,10 +63,7 @@ pub fn core_to_eval(v: CoreValue) -> Value {
|
|
|
64
63
|
CoreValue::Promise(p) => Value::CorePromise(Arc::clone(&p)),
|
|
65
64
|
#[cfg(not(feature = "http"))]
|
|
66
65
|
CoreValue::Promise(_) => Value::Null,
|
|
67
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
68
66
|
CoreValue::Function(f) => Value::CoreFn(Rc::clone(&f)),
|
|
69
|
-
#[cfg(not(any(feature = "http", feature = "ws")))]
|
|
70
|
-
CoreValue::Function(_) => Value::Null,
|
|
71
67
|
// tishlang_core gets regex from http or regex features; handle RegExp when it exists
|
|
72
68
|
#[cfg(any(feature = "http", feature = "regex"))]
|
|
73
69
|
CoreValue::RegExp(re) => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
use tishlang_ast::{
|
|
4
4
|
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
5
|
-
ExportDeclaration, Expr, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp,
|
|
5
|
+
ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp,
|
|
6
6
|
Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Statement, TypeAnnotation,
|
|
7
7
|
TypedParam, UnaryOp,
|
|
8
8
|
};
|
|
@@ -385,19 +385,38 @@ impl Printer {
|
|
|
385
385
|
self.buf.push_str(" }");
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
fn param_list(&mut self, params: &[
|
|
388
|
+
fn param_list(&mut self, params: &[FunParam], rest: &Option<TypedParam>) {
|
|
389
389
|
for (i, p) in params.iter().enumerate() {
|
|
390
390
|
if i > 0 {
|
|
391
391
|
self.buf.push_str(", ");
|
|
392
392
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
393
|
+
match p {
|
|
394
|
+
FunParam::Simple(tp) => {
|
|
395
|
+
self.buf.push_str(tp.name.as_ref());
|
|
396
|
+
if let Some(t) = &tp.type_ann {
|
|
397
|
+
self.buf.push_str(": ");
|
|
398
|
+
self.type_ann(t);
|
|
399
|
+
}
|
|
400
|
+
if let Some(e) = &tp.default {
|
|
401
|
+
self.buf.push_str(" = ");
|
|
402
|
+
self.expr(e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
FunParam::Destructure {
|
|
406
|
+
pattern,
|
|
407
|
+
type_ann,
|
|
408
|
+
default,
|
|
409
|
+
} => {
|
|
410
|
+
self.destruct_pat(pattern);
|
|
411
|
+
if let Some(t) = type_ann {
|
|
412
|
+
self.buf.push_str(": ");
|
|
413
|
+
self.type_ann(t);
|
|
414
|
+
}
|
|
415
|
+
if let Some(e) = default {
|
|
416
|
+
self.buf.push_str(" = ");
|
|
417
|
+
self.expr(e);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
401
420
|
}
|
|
402
421
|
}
|
|
403
422
|
if let Some(r) = rest {
|
|
@@ -551,6 +570,26 @@ impl Printer {
|
|
|
551
570
|
}
|
|
552
571
|
self.buf.push(')');
|
|
553
572
|
}
|
|
573
|
+
Expr::New { callee, args, .. } => {
|
|
574
|
+
self.buf.push_str("new ");
|
|
575
|
+
self.expr(callee);
|
|
576
|
+
if !args.is_empty() {
|
|
577
|
+
self.buf.push('(');
|
|
578
|
+
for (i, a) in args.iter().enumerate() {
|
|
579
|
+
if i > 0 {
|
|
580
|
+
self.buf.push_str(", ");
|
|
581
|
+
}
|
|
582
|
+
match a {
|
|
583
|
+
CallArg::Expr(ex) => self.expr(ex),
|
|
584
|
+
CallArg::Spread(ex) => {
|
|
585
|
+
self.buf.push_str("...");
|
|
586
|
+
self.expr(ex);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
self.buf.push(')');
|
|
591
|
+
}
|
|
592
|
+
}
|
|
554
593
|
Expr::Member {
|
|
555
594
|
object,
|
|
556
595
|
prop,
|
|
@@ -54,6 +54,7 @@ pub enum TokenKind {
|
|
|
54
54
|
In,
|
|
55
55
|
Async,
|
|
56
56
|
Await,
|
|
57
|
+
New,
|
|
57
58
|
Import,
|
|
58
59
|
Export,
|
|
59
60
|
|
|
@@ -149,6 +150,7 @@ impl TokenKind {
|
|
|
149
150
|
"in" => TokenKind::In,
|
|
150
151
|
"async" => TokenKind::Async,
|
|
151
152
|
"await" => TokenKind::Await,
|
|
153
|
+
"new" => TokenKind::New,
|
|
152
154
|
"import" => TokenKind::Import,
|
|
153
155
|
"export" => TokenKind::Export,
|
|
154
156
|
_ => TokenKind::Ident,
|
|
@@ -180,6 +180,15 @@ fn lint_expr(e: &Expr, out: &mut Vec<LintDiagnostic>) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
Expr::New { callee, args, .. } => {
|
|
184
|
+
lint_expr(callee, out);
|
|
185
|
+
for a in args {
|
|
186
|
+
match a {
|
|
187
|
+
tishlang_ast::CallArg::Expr(x) => lint_expr(x, out),
|
|
188
|
+
tishlang_ast::CallArg::Spread(x) => lint_expr(x, out),
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
183
192
|
Expr::Member { object, .. } => {
|
|
184
193
|
lint_expr(object, out);
|
|
185
194
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
//! LLVM
|
|
1
|
+
//! LLVM/clang link path for the **embedded-bytecode + VM** native binary.
|
|
2
2
|
//!
|
|
3
|
-
//!
|
|
4
|
-
//!
|
|
5
|
-
//!
|
|
3
|
+
//! Emits a C file that only holds the serialized chunk as bytes; clang produces `chunk.o`,
|
|
4
|
+
//! then links with **`tishlang_cranelift_runtime`** (same `tish_run_chunk` + `tishlang_vm`
|
|
5
|
+
//! entry as `--native-backend cranelift`). This is **not** LLVM IR lowering of Tish opcodes.
|
|
6
6
|
|
|
7
7
|
use std::fs;
|
|
8
8
|
use std::path::Path;
|
|
@@ -45,6 +45,19 @@ pub fn build_via_cargo(
|
|
|
45
45
|
})
|
|
46
46
|
.collect();
|
|
47
47
|
|
|
48
|
+
let tish_ui_path = std::path::Path::new(&runtime_path)
|
|
49
|
+
.parent()
|
|
50
|
+
.ok_or_else(|| "invalid tishlang_runtime path (no parent)".to_string())?
|
|
51
|
+
.join("tish_ui");
|
|
52
|
+
let ui_dep = if rust_code.contains("tishlang_ui") {
|
|
53
|
+
format!(
|
|
54
|
+
"\ntishlang_ui = {{ path = {:?}, default-features = false, features = [\"runtime\"] }}\n",
|
|
55
|
+
tish_ui_path.display().to_string().replace('\\', "/")
|
|
56
|
+
)
|
|
57
|
+
} else {
|
|
58
|
+
String::new()
|
|
59
|
+
};
|
|
60
|
+
|
|
48
61
|
let cargo_toml = format!(
|
|
49
62
|
r#"[package]
|
|
50
63
|
name = "tish_output"
|
|
@@ -63,7 +76,7 @@ codegen-units = 1
|
|
|
63
76
|
lto = "thin"
|
|
64
77
|
|
|
65
78
|
[dependencies]
|
|
66
|
-
tishlang_runtime = {{ path = {:?}{} }}{}{}
|
|
79
|
+
tishlang_runtime = {{ path = {:?}{} }}{}{}{}
|
|
67
80
|
"#,
|
|
68
81
|
out_name,
|
|
69
82
|
runtime_path,
|
|
@@ -73,7 +86,8 @@ tishlang_runtime = {{ path = {:?}{} }}{}{}
|
|
|
73
86
|
String::new()
|
|
74
87
|
} else {
|
|
75
88
|
format!("\n{}", native_deps)
|
|
76
|
-
}
|
|
89
|
+
},
|
|
90
|
+
ui_dep
|
|
77
91
|
);
|
|
78
92
|
|
|
79
93
|
fs::write(build_dir.join("Cargo.toml"), cargo_toml)
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
//! Native code generation backend for Tish.
|
|
2
2
|
//!
|
|
3
|
-
//!
|
|
4
|
-
//!
|
|
5
|
-
//! -
|
|
3
|
+
//! - **`rust`:** `tishlang_compile` emits Rust calling **`tishlang_runtime`** (`Value`, etc.),
|
|
4
|
+
//! then `cargo build --release` links the user binary.
|
|
5
|
+
//! - **`cranelift`:** Embeds serialized bytecode in an object file and links **`tishlang_cranelift_runtime`**
|
|
6
|
+
//! — the executable runs **`tishlang_vm`** on that chunk (same as `tish run --backend vm`), not CLIF lowering.
|
|
7
|
+
//! - **`llvm`:** Same embedded-bytecode + VM link path via `tishlang_llvm` / shared linker.
|
|
6
8
|
//!
|
|
7
|
-
//!
|
|
8
|
-
//!
|
|
9
|
-
//! 2. Lower to Cranelift IR
|
|
10
|
-
//! 3. Emit .o via cranelift-object
|
|
11
|
-
//! 4. Link against prebuilt tishlang_runtime staticlib
|
|
9
|
+
//! **Future:** Lower bytecode (or typed IR) through Cranelift/LLVM to real machine code where semantics allow;
|
|
10
|
+
//! emit Rust using `Vec<f64>` / fixed primitives instead of `Value` on hot paths.
|
|
12
11
|
|
|
13
12
|
mod build;
|
|
14
13
|
|
|
@@ -31,9 +30,9 @@ impl std::error::Error for NativeError {}
|
|
|
31
30
|
|
|
32
31
|
/// Compile a Tish project to a native binary.
|
|
33
32
|
///
|
|
34
|
-
/// - `native_backend == "rust"`:
|
|
35
|
-
/// - `native_backend == "cranelift"`:
|
|
36
|
-
/// - `native_backend == "llvm"`:
|
|
33
|
+
/// - `native_backend == "rust"`: Rust source + `tishlang_runtime` + cargo (native imports).
|
|
34
|
+
/// - `native_backend == "cranelift"`: Embedded bytecode + VM binary (pure Tish only); not opcode AOT yet.
|
|
35
|
+
/// - `native_backend == "llvm"`: Embedded bytecode + VM via LLVM/clang link path.
|
|
37
36
|
pub fn compile_to_native(
|
|
38
37
|
entry_path: &Path,
|
|
39
38
|
project_root: Option<&Path>,
|
|
@@ -339,6 +339,21 @@ fn optimize_expr(expr: &Expr) -> Expr {
|
|
|
339
339
|
.collect(),
|
|
340
340
|
span: *span,
|
|
341
341
|
},
|
|
342
|
+
Expr::New {
|
|
343
|
+
callee,
|
|
344
|
+
args,
|
|
345
|
+
span,
|
|
346
|
+
} => Expr::New {
|
|
347
|
+
callee: Box::new(optimize_expr(callee)),
|
|
348
|
+
args: args
|
|
349
|
+
.iter()
|
|
350
|
+
.map(|a| match a {
|
|
351
|
+
tishlang_ast::CallArg::Expr(e) => tishlang_ast::CallArg::Expr(optimize_expr(e)),
|
|
352
|
+
tishlang_ast::CallArg::Spread(e) => tishlang_ast::CallArg::Spread(optimize_expr(e)),
|
|
353
|
+
})
|
|
354
|
+
.collect(),
|
|
355
|
+
span: *span,
|
|
356
|
+
},
|
|
342
357
|
Expr::Member {
|
|
343
358
|
object,
|
|
344
359
|
prop,
|
|
@@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result<Program, String> {
|
|
|
18
18
|
#[cfg(test)]
|
|
19
19
|
mod tests {
|
|
20
20
|
use super::*;
|
|
21
|
-
use tishlang_ast::{Expr, ObjectProp, Statement};
|
|
21
|
+
use tishlang_ast::{CallArg, Expr, ObjectProp, Statement};
|
|
22
22
|
|
|
23
23
|
#[test]
|
|
24
24
|
fn test_async_fn_parse() {
|
|
@@ -120,4 +120,104 @@ mod tests {
|
|
|
120
120
|
_ => panic!("expected KeyValue prop"),
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
fn unwrap_expr_stmt(program: &tishlang_ast::Program) -> &Expr {
|
|
125
|
+
match program.statements.first() {
|
|
126
|
+
Some(Statement::ExprStmt { expr, .. }) => expr,
|
|
127
|
+
_ => panic!("expected expression statement"),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#[test]
|
|
132
|
+
fn new_expression_simple_call() {
|
|
133
|
+
let program = parse("new Foo()").expect("parse");
|
|
134
|
+
let e = unwrap_expr_stmt(&program);
|
|
135
|
+
match e {
|
|
136
|
+
Expr::New { callee, args, .. } => {
|
|
137
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Foo"));
|
|
138
|
+
assert!(args.is_empty());
|
|
139
|
+
}
|
|
140
|
+
_ => panic!("expected New, got {:?}", e),
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[test]
|
|
145
|
+
fn new_expression_with_args() {
|
|
146
|
+
let program = parse("new Uint8Array(16)").expect("parse");
|
|
147
|
+
let e = unwrap_expr_stmt(&program);
|
|
148
|
+
match e {
|
|
149
|
+
Expr::New { callee, args, .. } => {
|
|
150
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Uint8Array"));
|
|
151
|
+
assert_eq!(args.len(), 1);
|
|
152
|
+
assert!(matches!(&args[0], CallArg::Expr(Expr::Literal { .. })));
|
|
153
|
+
}
|
|
154
|
+
_ => panic!("expected New"),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[test]
|
|
159
|
+
fn new_expression_member_callee() {
|
|
160
|
+
let program = parse("new ns.AudioContext()").expect("parse");
|
|
161
|
+
let e = unwrap_expr_stmt(&program);
|
|
162
|
+
match e {
|
|
163
|
+
Expr::New { callee, args, .. } => {
|
|
164
|
+
assert!(matches!(
|
|
165
|
+
callee.as_ref(),
|
|
166
|
+
Expr::Member { prop: tishlang_ast::MemberProp::Name(p), .. } if p.as_ref() == "AudioContext"
|
|
167
|
+
));
|
|
168
|
+
assert!(args.is_empty());
|
|
169
|
+
}
|
|
170
|
+
_ => panic!("expected New"),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[test]
|
|
175
|
+
fn new_expression_chained_new() {
|
|
176
|
+
let program = parse("new new Date()").expect("parse");
|
|
177
|
+
let e = unwrap_expr_stmt(&program);
|
|
178
|
+
match e {
|
|
179
|
+
Expr::New { callee, args, .. } => {
|
|
180
|
+
assert!(args.is_empty());
|
|
181
|
+
match callee.as_ref() {
|
|
182
|
+
Expr::New { callee: inner, args: inner_args, .. } => {
|
|
183
|
+
assert!(matches!(inner.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Date"));
|
|
184
|
+
assert!(inner_args.is_empty());
|
|
185
|
+
}
|
|
186
|
+
_ => panic!("expected nested New"),
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
_ => panic!("expected New"),
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[test]
|
|
194
|
+
fn new_then_member_access() {
|
|
195
|
+
let program = parse("new Foo().bar").expect("parse");
|
|
196
|
+
let e = unwrap_expr_stmt(&program);
|
|
197
|
+
match e {
|
|
198
|
+
Expr::Member { object, prop: tishlang_ast::MemberProp::Name(p), .. } => {
|
|
199
|
+
assert_eq!(p.as_ref(), "bar");
|
|
200
|
+
match object.as_ref() {
|
|
201
|
+
Expr::New { callee, args, .. } => {
|
|
202
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Foo"));
|
|
203
|
+
assert!(args.is_empty());
|
|
204
|
+
}
|
|
205
|
+
_ => panic!("expected New object"),
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
_ => panic!("expected Member"),
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[test]
|
|
213
|
+
fn new_with_spread_arg() {
|
|
214
|
+
let program = parse("new Foo(...xs)").expect("parse");
|
|
215
|
+
let e = unwrap_expr_stmt(&program);
|
|
216
|
+
match e {
|
|
217
|
+
Expr::New { args, .. } => {
|
|
218
|
+
assert!(matches!(&args[0], CallArg::Spread(Expr::Ident { name, .. }) if name.as_ref() == "xs"));
|
|
219
|
+
}
|
|
220
|
+
_ => panic!("expected New"),
|
|
221
|
+
}
|
|
222
|
+
}
|
|
123
223
|
}
|