@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
package/Cargo.toml
CHANGED
|
@@ -6,7 +6,7 @@ use oxc::ast::ast::Expression as OxcExpr;
|
|
|
6
6
|
use oxc::semantic::Semantic;
|
|
7
7
|
use tishlang_ast::{
|
|
8
8
|
ArrayElement, ArrowBody, BinOp, CompoundOp, DestructPattern, Expr, Literal, LogicalAssignOp,
|
|
9
|
-
MemberProp, ObjectProp, TypedParam,
|
|
9
|
+
FunParam, MemberProp, ObjectProp, TypedParam,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
use crate::error::{ConvertError, ConvertErrorKind};
|
|
@@ -73,6 +73,15 @@ pub fn convert_expr(expr: &OxcExpr<'_>, ctx: &Ctx<'_>) -> Result<Expr, ConvertEr
|
|
|
73
73
|
.collect::<Result<Vec<_>, _>>()?;
|
|
74
74
|
Ok(Expr::Call { callee, args, span })
|
|
75
75
|
}
|
|
76
|
+
OxcExpr::NewExpression(n) => {
|
|
77
|
+
let callee = Box::new(convert_expr(&n.callee, ctx)?);
|
|
78
|
+
let args = n
|
|
79
|
+
.arguments
|
|
80
|
+
.iter()
|
|
81
|
+
.map(|a| convert_call_arg(a, ctx))
|
|
82
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
83
|
+
Ok(Expr::New { callee, args, span })
|
|
84
|
+
}
|
|
76
85
|
OxcExpr::StaticMemberExpression(s) => {
|
|
77
86
|
let object = Box::new(convert_expr(&s.object, ctx)?);
|
|
78
87
|
Ok(Expr::Member {
|
|
@@ -518,7 +527,7 @@ fn convert_unary_op(
|
|
|
518
527
|
pub fn convert_params(
|
|
519
528
|
params: &oxc::ast::ast::FormalParameters<'_>,
|
|
520
529
|
ctx: &Ctx<'_>,
|
|
521
|
-
) -> Result<(Vec<
|
|
530
|
+
) -> Result<(Vec<FunParam>, Option<TypedParam>), ConvertError> {
|
|
522
531
|
let mut typed_params = Vec::new();
|
|
523
532
|
let mut rest_param = None;
|
|
524
533
|
for (i, p) in params.items.iter().enumerate() {
|
|
@@ -558,11 +567,11 @@ pub fn convert_params(
|
|
|
558
567
|
.as_ref()
|
|
559
568
|
.map(|e| convert_expr(e, ctx))
|
|
560
569
|
.transpose()?;
|
|
561
|
-
typed_params.push(TypedParam {
|
|
570
|
+
typed_params.push(FunParam::Simple(TypedParam {
|
|
562
571
|
name: Arc::from(name),
|
|
563
572
|
type_ann: None,
|
|
564
573
|
default,
|
|
565
|
-
});
|
|
574
|
+
}));
|
|
566
575
|
}
|
|
567
576
|
}
|
|
568
577
|
if rest_param.is_none() {
|
|
@@ -589,10 +598,10 @@ pub fn convert_params(
|
|
|
589
598
|
fn convert_arrow_params(
|
|
590
599
|
params: &oxc::ast::ast::FormalParameters<'_>,
|
|
591
600
|
ctx: &Ctx<'_>,
|
|
592
|
-
) -> Result<Vec<
|
|
601
|
+
) -> Result<Vec<FunParam>, ConvertError> {
|
|
593
602
|
let (mut ps, rest) = convert_params(params, ctx)?;
|
|
594
603
|
if let Some(r) = rest {
|
|
595
|
-
ps.push(r);
|
|
604
|
+
ps.push(FunParam::Simple(r));
|
|
596
605
|
}
|
|
597
606
|
Ok(ps)
|
|
598
607
|
}
|
package/crates/tish/Cargo.toml
CHANGED
package/crates/tish/src/main.rs
CHANGED
|
@@ -28,7 +28,7 @@ struct RunArgs {
|
|
|
28
28
|
#[arg(long, default_value = "vm")]
|
|
29
29
|
backend: String,
|
|
30
30
|
/// Enable capabilities (http, fs, process, regex, ws). Must match how tish was built.
|
|
31
|
-
/// E.g. cargo run -p
|
|
31
|
+
/// E.g. cargo run -p tishlang--features http,fs -- run script.tish --feature http,fs
|
|
32
32
|
#[arg(long = "feature", action = clap::ArgAction::Append)]
|
|
33
33
|
features: Vec<String>,
|
|
34
34
|
/// Disable AST and bytecode optimizations (for debugging)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//! Full-stack integration tests: run .tish files with interpreter or each backend and compare
|
|
2
2
|
//! stdout to static expected files (e.g. `fn_any.tish.expected`).
|
|
3
3
|
//!
|
|
4
|
-
//! - Run: `cargo test -p
|
|
5
|
-
//! - Generate/update expected files: `REGENERATE_EXPECTED=1 cargo test -p
|
|
4
|
+
//! - Run: `cargo test -p tishlang` (or `cargo nextest run -p tishlang`).
|
|
5
|
+
//! - Generate/update expected files: `REGENERATE_EXPECTED=1 cargo test -p tishlangtest_mvp_programs_interpreter`
|
|
6
6
|
//! then commit the new/updated `tests/core/*.tish.expected` files.
|
|
7
7
|
//! - Compiled outputs are cached under `target/integration_compile_cache/` per backend.
|
|
8
8
|
|
|
@@ -232,7 +232,7 @@ fn test_async_await_compile_via_binary() {
|
|
|
232
232
|
/// Uses httpbin.org/delay/1 (1s each). 3 parallel ≈ 1s, 3 sequential ≈ 3s.
|
|
233
233
|
#[test]
|
|
234
234
|
#[cfg(feature = "http")]
|
|
235
|
-
#[ignore = "timing and network sensitive; run manually: cargo test test_async_parallel_vs_sequential_timing -p
|
|
235
|
+
#[ignore = "timing and network sensitive; run manually: cargo test test_async_parallel_vs_sequential_timing -p tishlang--features http -- --ignored"]
|
|
236
236
|
fn test_async_parallel_vs_sequential_timing() {
|
|
237
237
|
let bin = tish_bin();
|
|
238
238
|
let parallel_src = workspace_root().join("examples").join("async-await").join("src").join("parallel.tish");
|
|
@@ -474,6 +474,7 @@ const MVP_TEST_FILES: &[&str] = &[
|
|
|
474
474
|
"types.tish",
|
|
475
475
|
"logical_assign.tish",
|
|
476
476
|
"spread.tish",
|
|
477
|
+
"fn_param_destructuring.tish",
|
|
477
478
|
];
|
|
478
479
|
|
|
479
480
|
/// Run each .tish file with interpreter and compare stdout to static expected.
|
|
@@ -34,6 +34,62 @@ pub struct TypedParam {
|
|
|
34
34
|
pub default: Option<Expr>,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
|
|
38
|
+
/// Single formal parameter: simple identifier or destructuring pattern.
|
|
39
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
40
|
+
pub enum FunParam {
|
|
41
|
+
Simple(TypedParam),
|
|
42
|
+
Destructure {
|
|
43
|
+
pattern: DestructPattern,
|
|
44
|
+
type_ann: Option<TypeAnnotation>,
|
|
45
|
+
default: Option<Expr>,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
impl FunParam {
|
|
51
|
+
/// Variable names introduced by this formal parameter.
|
|
52
|
+
pub fn bound_names(&self) -> Vec<Arc<str>> {
|
|
53
|
+
let mut out = Vec::new();
|
|
54
|
+
match self {
|
|
55
|
+
FunParam::Simple(tp) => out.push(Arc::clone(&tp.name)),
|
|
56
|
+
FunParam::Destructure { pattern, .. } => {
|
|
57
|
+
Self::collect_pattern_binding_names(pattern, &mut out);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
out
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fn collect_pattern_binding_names(pattern: &DestructPattern, out: &mut Vec<Arc<str>>) {
|
|
64
|
+
match pattern {
|
|
65
|
+
DestructPattern::Array(elements) => {
|
|
66
|
+
for el in elements {
|
|
67
|
+
if let Some(el) = el {
|
|
68
|
+
match el {
|
|
69
|
+
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
70
|
+
DestructElement::Pattern(p) => {
|
|
71
|
+
Self::collect_pattern_binding_names(p, out);
|
|
72
|
+
}
|
|
73
|
+
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
DestructPattern::Object(props) => {
|
|
79
|
+
for prop in props {
|
|
80
|
+
match &prop.value {
|
|
81
|
+
DestructElement::Ident(n) => out.push(Arc::clone(n)),
|
|
82
|
+
DestructElement::Pattern(p) => {
|
|
83
|
+
Self::collect_pattern_binding_names(p, out);
|
|
84
|
+
}
|
|
85
|
+
DestructElement::Rest(n) => out.push(Arc::clone(n)),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
37
93
|
/// Destructuring pattern for array or object destructuring
|
|
38
94
|
#[derive(Debug, Clone, PartialEq)]
|
|
39
95
|
pub enum DestructPattern {
|
|
@@ -149,7 +205,7 @@ pub enum Statement {
|
|
|
149
205
|
FunDecl {
|
|
150
206
|
async_: bool,
|
|
151
207
|
name: Arc<str>,
|
|
152
|
-
params: Vec<
|
|
208
|
+
params: Vec<FunParam>,
|
|
153
209
|
rest_param: Option<TypedParam>,
|
|
154
210
|
return_type: Option<TypeAnnotation>,
|
|
155
211
|
body: Box<Statement>,
|
|
@@ -214,6 +270,12 @@ pub enum Expr {
|
|
|
214
270
|
args: Vec<CallArg>,
|
|
215
271
|
span: Span,
|
|
216
272
|
},
|
|
273
|
+
/// `new` expression (JavaScript target). `callee` is the constructor reference; `args` may be empty.
|
|
274
|
+
New {
|
|
275
|
+
callee: Box<Expr>,
|
|
276
|
+
args: Vec<CallArg>,
|
|
277
|
+
span: Span,
|
|
278
|
+
},
|
|
217
279
|
Member {
|
|
218
280
|
object: Box<Expr>,
|
|
219
281
|
prop: MemberProp,
|
|
@@ -298,7 +360,7 @@ pub enum Expr {
|
|
|
298
360
|
},
|
|
299
361
|
/// Arrow function: (params) => body
|
|
300
362
|
ArrowFunction {
|
|
301
|
-
params: Vec<
|
|
363
|
+
params: Vec<FunParam>,
|
|
302
364
|
body: ArrowBody,
|
|
303
365
|
span: Span,
|
|
304
366
|
},
|
|
@@ -374,6 +436,7 @@ impl Expr {
|
|
|
374
436
|
Expr::Binary { span, .. } => *span,
|
|
375
437
|
Expr::Unary { span, .. } => *span,
|
|
376
438
|
Expr::Call { span, .. } => *span,
|
|
439
|
+
Expr::New { span, .. } => *span,
|
|
377
440
|
Expr::Member { span, .. } => *span,
|
|
378
441
|
Expr::Index { span, .. } => *span,
|
|
379
442
|
Expr::Conditional { span, .. } => *span,
|
|
@@ -7,6 +7,14 @@ use std::fs;
|
|
|
7
7
|
use std::path::{Path, PathBuf};
|
|
8
8
|
use std::process::Command;
|
|
9
9
|
|
|
10
|
+
/// True if `root` looks like the Tish language repo (has `crates/tish_runtime`).
|
|
11
|
+
///
|
|
12
|
+
/// Used so we do not treat unrelated workspaces (e.g. a parent `zectre-platform` repo) as Tish
|
|
13
|
+
/// when `CARGO_MANIFEST_DIR` or cwd points at another Rust workspace.
|
|
14
|
+
fn is_tish_workspace_root(root: &Path) -> bool {
|
|
15
|
+
root.join("crates").join("tish_runtime").is_dir()
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
/// Find the Tish workspace root using multiple strategies.
|
|
11
19
|
///
|
|
12
20
|
/// Returns the directory containing the workspace Cargo.toml (with [workspace]).
|
|
@@ -18,7 +26,7 @@ pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
|
18
26
|
// For crates/tish_*, workspace root is parent.parent()
|
|
19
27
|
if let Some(root) = path.parent().and_then(|p| p.parent()) {
|
|
20
28
|
let root_buf = root.to_path_buf();
|
|
21
|
-
if root_buf.join("Cargo.toml").exists() {
|
|
29
|
+
if root_buf.join("Cargo.toml").exists() && is_tish_workspace_root(&root_buf) {
|
|
22
30
|
return Ok(root_buf);
|
|
23
31
|
}
|
|
24
32
|
}
|
|
@@ -47,7 +55,7 @@ pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
|
47
55
|
let cargo_toml = current.join("Cargo.toml");
|
|
48
56
|
if cargo_toml.exists() {
|
|
49
57
|
if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
|
|
50
|
-
if content.contains("[workspace]") {
|
|
58
|
+
if content.contains("[workspace]") && is_tish_workspace_root(¤t) {
|
|
51
59
|
return Ok(current);
|
|
52
60
|
}
|
|
53
61
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
//! `new` lowering for non-JS targets: `construct(callee, args)` approximates JS `[[Construct]]`.
|
|
2
|
+
//! Browser-exact behavior remains on `tish compile --target js`.
|
|
3
|
+
|
|
4
|
+
use std::cell::RefCell;
|
|
5
|
+
use std::rc::Rc;
|
|
6
|
+
use std::sync::Arc;
|
|
7
|
+
|
|
8
|
+
use tishlang_core::{ObjectMap, Value};
|
|
9
|
+
|
|
10
|
+
const CONSTRUCT: &str = "__construct";
|
|
11
|
+
|
|
12
|
+
/// Host `new`: `Object` with `__construct`, `Function` as plain call, else `Null`.
|
|
13
|
+
pub fn construct(callee: &Value, args: &[Value]) -> Value {
|
|
14
|
+
match callee {
|
|
15
|
+
Value::Function(f) => f(args),
|
|
16
|
+
Value::Object(o) => {
|
|
17
|
+
let b = o.borrow();
|
|
18
|
+
if let Some(Value::Function(ctor)) = b.get(&Arc::from(CONSTRUCT)) {
|
|
19
|
+
let c = Rc::clone(ctor);
|
|
20
|
+
drop(b);
|
|
21
|
+
return c(args);
|
|
22
|
+
}
|
|
23
|
+
Value::Null
|
|
24
|
+
}
|
|
25
|
+
_ => Value::Null,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fn param(initial: f64) -> Value {
|
|
30
|
+
let mut m = ObjectMap::default();
|
|
31
|
+
m.insert(Arc::from("value"), Value::Number(initial));
|
|
32
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn connect_fn() -> Value {
|
|
36
|
+
Value::Function(Rc::new(|_| Value::Null))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Shared audio-node shape: connect, gain, optional filter fields.
|
|
40
|
+
fn audio_node_stub() -> Value {
|
|
41
|
+
let mut m = ObjectMap::default();
|
|
42
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
43
|
+
m.insert(Arc::from("gain"), param(0.0));
|
|
44
|
+
m.insert(Arc::from("frequency"), param(440.0));
|
|
45
|
+
m.insert(Arc::from("Q"), param(1.0));
|
|
46
|
+
m.insert(Arc::from("type"), Value::String("peaking".into()));
|
|
47
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn analyser_stub() -> Value {
|
|
51
|
+
let mut m = ObjectMap::default();
|
|
52
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
53
|
+
m.insert(Arc::from("fftSize"), Value::Number(2048.0));
|
|
54
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn stereo_panner_stub() -> Value {
|
|
58
|
+
let mut m = ObjectMap::default();
|
|
59
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
60
|
+
m.insert(Arc::from("pan"), param(0.0));
|
|
61
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn audio_buffer_stub(len: usize) -> Value {
|
|
65
|
+
let n = len.max(1);
|
|
66
|
+
let data = Rc::new(RefCell::new(vec![Value::Number(0.0); n]));
|
|
67
|
+
let data2 = Rc::clone(&data);
|
|
68
|
+
let mut m = ObjectMap::default();
|
|
69
|
+
m.insert(
|
|
70
|
+
Arc::from("getChannelData"),
|
|
71
|
+
Value::Function(Rc::new(move |_args| Value::Array(Rc::clone(&data2)))),
|
|
72
|
+
);
|
|
73
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn buffer_source_stub() -> Value {
|
|
77
|
+
let mut m = ObjectMap::default();
|
|
78
|
+
m.insert(Arc::from("buffer"), Value::Null);
|
|
79
|
+
m.insert(Arc::from("loop"), Value::Bool(false));
|
|
80
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
81
|
+
m.insert(
|
|
82
|
+
Arc::from("start"),
|
|
83
|
+
Value::Function(Rc::new(|_| Value::Null)),
|
|
84
|
+
);
|
|
85
|
+
m.insert(
|
|
86
|
+
Arc::from("stop"),
|
|
87
|
+
Value::Function(Rc::new(|_| Value::Null)),
|
|
88
|
+
);
|
|
89
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn oscillator_stub() -> Value {
|
|
93
|
+
let mut m = ObjectMap::default();
|
|
94
|
+
m.insert(Arc::from("frequency"), param(440.0));
|
|
95
|
+
m.insert(Arc::from("type"), Value::String("sine".into()));
|
|
96
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
97
|
+
m.insert(
|
|
98
|
+
Arc::from("start"),
|
|
99
|
+
Value::Function(Rc::new(|_| Value::Null)),
|
|
100
|
+
);
|
|
101
|
+
m.insert(
|
|
102
|
+
Arc::from("stop"),
|
|
103
|
+
Value::Function(Rc::new(|_| Value::Null)),
|
|
104
|
+
);
|
|
105
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fn audio_context_instance() -> Value {
|
|
109
|
+
let mut ctx = ObjectMap::default();
|
|
110
|
+
ctx.insert(Arc::from("sampleRate"), Value::Number(48_000.0));
|
|
111
|
+
ctx.insert(Arc::from("destination"), audio_node_stub());
|
|
112
|
+
|
|
113
|
+
ctx.insert(
|
|
114
|
+
Arc::from("createGain"),
|
|
115
|
+
Value::Function(Rc::new(|_| audio_node_stub())),
|
|
116
|
+
);
|
|
117
|
+
ctx.insert(
|
|
118
|
+
Arc::from("createBiquadFilter"),
|
|
119
|
+
Value::Function(Rc::new(|_| audio_node_stub())),
|
|
120
|
+
);
|
|
121
|
+
ctx.insert(
|
|
122
|
+
Arc::from("createStereoPanner"),
|
|
123
|
+
Value::Function(Rc::new(|_| stereo_panner_stub())),
|
|
124
|
+
);
|
|
125
|
+
ctx.insert(
|
|
126
|
+
Arc::from("createAnalyser"),
|
|
127
|
+
Value::Function(Rc::new(|_| analyser_stub())),
|
|
128
|
+
);
|
|
129
|
+
ctx.insert(
|
|
130
|
+
Arc::from("createBuffer"),
|
|
131
|
+
Value::Function(Rc::new(|args: &[Value]| {
|
|
132
|
+
let len = args
|
|
133
|
+
.get(1)
|
|
134
|
+
.and_then(Value::as_number)
|
|
135
|
+
.unwrap_or(0.0)
|
|
136
|
+
.clamp(0.0, 1_000_000_000.0) as usize;
|
|
137
|
+
audio_buffer_stub(len)
|
|
138
|
+
})),
|
|
139
|
+
);
|
|
140
|
+
ctx.insert(
|
|
141
|
+
Arc::from("createBufferSource"),
|
|
142
|
+
Value::Function(Rc::new(|_| buffer_source_stub())),
|
|
143
|
+
);
|
|
144
|
+
ctx.insert(
|
|
145
|
+
Arc::from("createOscillator"),
|
|
146
|
+
Value::Function(Rc::new(|_| oscillator_stub())),
|
|
147
|
+
);
|
|
148
|
+
ctx.insert(
|
|
149
|
+
Arc::from("decodeAudioData"),
|
|
150
|
+
Value::Function(Rc::new(|_| Value::Null)),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
Value::Object(Rc::new(RefCell::new(ctx)))
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// Global `Uint8Array` for native/VM: `new Uint8Array(n)` → numeric array of zeros (not real bytes).
|
|
157
|
+
pub fn uint8_array_constructor_value() -> Value {
|
|
158
|
+
let ctor = Rc::new(|args: &[Value]| {
|
|
159
|
+
let len = args
|
|
160
|
+
.first()
|
|
161
|
+
.and_then(Value::as_number)
|
|
162
|
+
.unwrap_or(0.0)
|
|
163
|
+
.clamp(0.0, 1_000_000_000.0) as usize;
|
|
164
|
+
Value::Array(Rc::new(RefCell::new(vec![Value::Number(0.0); len])))
|
|
165
|
+
});
|
|
166
|
+
let mut m = ObjectMap::default();
|
|
167
|
+
m.insert(Arc::from(CONSTRUCT), Value::Function(ctor));
|
|
168
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Global `AudioContext` for native/VM: stub graph (no real audio).
|
|
172
|
+
pub fn audio_context_constructor_value() -> Value {
|
|
173
|
+
let ctor = Rc::new(|_args: &[Value]| audio_context_instance());
|
|
174
|
+
let mut m = ObjectMap::default();
|
|
175
|
+
m.insert(Arc::from(CONSTRUCT), Value::Function(ctor));
|
|
176
|
+
Value::Object(Rc::new(RefCell::new(m)))
|
|
177
|
+
}
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
//! independent of tishlang_runtime.
|
|
5
5
|
|
|
6
6
|
use std::cell::RefCell;
|
|
7
|
-
use std::collections::HashMap;
|
|
8
7
|
use std::rc::Rc;
|
|
9
8
|
use std::sync::Arc;
|
|
10
|
-
use tishlang_core::{percent_decode, percent_encode, Value};
|
|
9
|
+
use tishlang_core::{percent_decode, percent_encode, ObjectMap, Value};
|
|
11
10
|
|
|
12
11
|
/// Boolean(value) - coerce to bool
|
|
13
12
|
pub fn boolean(args: &[Value]) -> Value {
|
|
@@ -174,8 +173,7 @@ pub fn parse_float(args: &[Value]) -> Value {
|
|
|
174
173
|
pub fn object_from_entries(args: &[Value]) -> Value {
|
|
175
174
|
if let Some(Value::Array(entries)) = args.first() {
|
|
176
175
|
let entries_borrow = entries.borrow();
|
|
177
|
-
let mut obj:
|
|
178
|
-
HashMap::with_capacity(entries_borrow.len());
|
|
176
|
+
let mut obj: ObjectMap = ObjectMap::with_capacity(entries_borrow.len());
|
|
179
177
|
|
|
180
178
|
for entry in entries_borrow.iter() {
|
|
181
179
|
if let Value::Array(pair) = entry {
|
|
@@ -192,6 +190,6 @@ pub fn object_from_entries(args: &[Value]) -> Value {
|
|
|
192
190
|
|
|
193
191
|
Value::Object(Rc::new(RefCell::new(obj)))
|
|
194
192
|
} else {
|
|
195
|
-
Value::Object(Rc::new(RefCell::new(
|
|
193
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::default())))
|
|
196
194
|
}
|
|
197
195
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
//! Common helper functions used across builtin implementations.
|
|
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
|
-
use tishlang_core::Value;
|
|
6
|
+
use tishlang_core::{ObjectMap, Value};
|
|
8
7
|
|
|
9
8
|
/// Normalize an array index, handling negative indices.
|
|
10
9
|
/// Returns a valid index within bounds or the default value.
|
|
@@ -24,7 +23,7 @@ pub fn normalize_index(idx: &Value, len: i64, default: usize) -> usize {
|
|
|
24
23
|
|
|
25
24
|
/// Create an error object with a single "error" field.
|
|
26
25
|
pub fn make_error_value(e: impl std::fmt::Display) -> Value {
|
|
27
|
-
let mut obj =
|
|
26
|
+
let mut obj = ObjectMap::with_capacity(1);
|
|
28
27
|
obj.insert(Arc::from("error"), Value::String(e.to_string().into()));
|
|
29
28
|
Value::Object(Rc::new(RefCell::new(obj)))
|
|
30
29
|
}
|
|
@@ -4,19 +4,18 @@
|
|
|
4
4
|
//! Functions will be migrated here from tishlang_runtime and tishlang_eval.
|
|
5
5
|
|
|
6
6
|
use std::cell::RefCell;
|
|
7
|
-
use std::collections::HashMap;
|
|
8
7
|
use std::rc::Rc;
|
|
9
8
|
use std::sync::Arc;
|
|
10
|
-
use tishlang_core::Value;
|
|
9
|
+
use tishlang_core::{ObjectMap, Value};
|
|
11
10
|
|
|
12
11
|
/// Create a new empty object Value.
|
|
13
12
|
pub fn new() -> Value {
|
|
14
|
-
Value::Object(Rc::new(RefCell::new(
|
|
13
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::default())))
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
/// Create a new object Value with a given capacity.
|
|
18
17
|
pub fn with_capacity(capacity: usize) -> Value {
|
|
19
|
-
Value::Object(Rc::new(RefCell::new(
|
|
18
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::with_capacity(capacity))))
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/// Get the keys of an object.
|
|
@@ -5,7 +5,8 @@ use std::sync::Arc;
|
|
|
5
5
|
|
|
6
6
|
use tishlang_ast::{
|
|
7
7
|
ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern, Expr,
|
|
8
|
-
JsxAttrValue, JsxChild, JsxProp, Literal, MemberProp, ObjectProp, Program, Span,
|
|
8
|
+
FunParam, JsxAttrValue, JsxChild, JsxProp, Literal, MemberProp, ObjectProp, Program, Span,
|
|
9
|
+
Statement,
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
use crate::chunk::{Chunk, Constant};
|
|
@@ -137,8 +138,14 @@ impl<'a> Compiler<'a> {
|
|
|
137
138
|
if params.len() != 2 {
|
|
138
139
|
return None;
|
|
139
140
|
}
|
|
140
|
-
let param_a = params[0]
|
|
141
|
-
|
|
141
|
+
let (param_a, param_b) = match (¶ms[0], ¶ms[1]) {
|
|
142
|
+
(FunParam::Simple(a), FunParam::Simple(b))
|
|
143
|
+
if a.default.is_none() && b.default.is_none() =>
|
|
144
|
+
{
|
|
145
|
+
(a.name.as_ref(), b.name.as_ref())
|
|
146
|
+
}
|
|
147
|
+
_ => return None,
|
|
148
|
+
};
|
|
142
149
|
let body_expr = match body {
|
|
143
150
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
144
151
|
ArrowBody::Block(stmt) => {
|
|
@@ -184,8 +191,14 @@ impl<'a> Compiler<'a> {
|
|
|
184
191
|
if params.len() != 2 {
|
|
185
192
|
return None;
|
|
186
193
|
}
|
|
187
|
-
let param_a = params[0]
|
|
188
|
-
|
|
194
|
+
let (param_a, param_b) = match (¶ms[0], ¶ms[1]) {
|
|
195
|
+
(FunParam::Simple(a), FunParam::Simple(b))
|
|
196
|
+
if a.default.is_none() && b.default.is_none() =>
|
|
197
|
+
{
|
|
198
|
+
(a.name.as_ref(), b.name.as_ref())
|
|
199
|
+
}
|
|
200
|
+
_ => return None,
|
|
201
|
+
};
|
|
189
202
|
let body_expr = match body {
|
|
190
203
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
191
204
|
ArrowBody::Block(stmt) => {
|
|
@@ -228,7 +241,10 @@ impl<'a> Compiler<'a> {
|
|
|
228
241
|
if params.len() != 1 {
|
|
229
242
|
return None;
|
|
230
243
|
}
|
|
231
|
-
let param_name = params[0]
|
|
244
|
+
let param_name = match ¶ms[0] {
|
|
245
|
+
FunParam::Simple(tp) if tp.default.is_none() => tp.name.as_ref(),
|
|
246
|
+
_ => return None,
|
|
247
|
+
};
|
|
232
248
|
let expr_ref: &Expr = match body {
|
|
233
249
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
234
250
|
ArrowBody::Block(stmt) => {
|
|
@@ -277,7 +293,10 @@ impl<'a> Compiler<'a> {
|
|
|
277
293
|
if params.len() != 1 {
|
|
278
294
|
return None;
|
|
279
295
|
}
|
|
280
|
-
let param_name = params[0]
|
|
296
|
+
let param_name = match ¶ms[0] {
|
|
297
|
+
FunParam::Simple(tp) if tp.default.is_none() => tp.name.as_ref(),
|
|
298
|
+
_ => return None,
|
|
299
|
+
};
|
|
281
300
|
let expr_ref: &Expr = match body {
|
|
282
301
|
ArrowBody::Expr(e) => e.as_ref(),
|
|
283
302
|
ArrowBody::Block(stmt) => {
|
|
@@ -550,9 +569,22 @@ impl<'a> Compiler<'a> {
|
|
|
550
569
|
async_: _,
|
|
551
570
|
..
|
|
552
571
|
} => {
|
|
572
|
+
for p in params {
|
|
573
|
+
if matches!(p, FunParam::Destructure { .. }) {
|
|
574
|
+
return Err(CompileError {
|
|
575
|
+
message: "Destructuring parameters are not supported in bytecode"
|
|
576
|
+
.to_string(),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
553
580
|
let mut inner = Chunk::new();
|
|
554
|
-
let mut param_names: Vec<Arc<str>> =
|
|
555
|
-
|
|
581
|
+
let mut param_names: Vec<Arc<str>> = params
|
|
582
|
+
.iter()
|
|
583
|
+
.map(|p| match p {
|
|
584
|
+
FunParam::Simple(tp) => Arc::clone(&tp.name),
|
|
585
|
+
_ => unreachable!(),
|
|
586
|
+
})
|
|
587
|
+
.collect();
|
|
556
588
|
if let Some(rp) = rest_param {
|
|
557
589
|
param_names.push(rp.name.clone());
|
|
558
590
|
inner.rest_param_index = (param_names.len() as u16).saturating_sub(1);
|
|
@@ -1061,9 +1093,22 @@ impl<'a> Compiler<'a> {
|
|
|
1061
1093
|
self.emit_u16(Opcode::Call, 1);
|
|
1062
1094
|
}
|
|
1063
1095
|
Expr::ArrowFunction { params, body, .. } => {
|
|
1096
|
+
for p in params {
|
|
1097
|
+
if matches!(p, FunParam::Destructure { .. }) {
|
|
1098
|
+
return Err(CompileError {
|
|
1099
|
+
message: "Destructuring parameters are not supported in bytecode"
|
|
1100
|
+
.to_string(),
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1064
1104
|
let mut inner = Chunk::new();
|
|
1065
|
-
let param_names: Vec<Arc<str>> =
|
|
1066
|
-
|
|
1105
|
+
let param_names: Vec<Arc<str>> = params
|
|
1106
|
+
.iter()
|
|
1107
|
+
.map(|p| match p {
|
|
1108
|
+
FunParam::Simple(tp) => Arc::clone(&tp.name),
|
|
1109
|
+
_ => unreachable!(),
|
|
1110
|
+
})
|
|
1111
|
+
.collect();
|
|
1067
1112
|
for p in ¶m_names {
|
|
1068
1113
|
inner.add_name(Arc::clone(p));
|
|
1069
1114
|
}
|
|
@@ -1205,6 +1250,35 @@ impl<'a> Compiler<'a> {
|
|
|
1205
1250
|
message: "Logical assignment (&&=, ||=, ??=) not yet supported in bytecode".to_string(),
|
|
1206
1251
|
});
|
|
1207
1252
|
}
|
|
1253
|
+
Expr::New { callee, args, .. } => {
|
|
1254
|
+
let has_spread = args.iter().any(|a| matches!(a, CallArg::Spread(_)));
|
|
1255
|
+
if has_spread {
|
|
1256
|
+
self.emit_u16(Opcode::NewArray, 0);
|
|
1257
|
+
for arg in args {
|
|
1258
|
+
match arg {
|
|
1259
|
+
CallArg::Expr(e) => {
|
|
1260
|
+
self.compile_expr(e)?;
|
|
1261
|
+
self.emit_u16(Opcode::NewArray, 1);
|
|
1262
|
+
self.emit(Opcode::ConcatArray);
|
|
1263
|
+
}
|
|
1264
|
+
CallArg::Spread(expr) => {
|
|
1265
|
+
self.compile_expr(expr)?;
|
|
1266
|
+
self.emit(Opcode::ConcatArray);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
self.compile_expr(callee)?;
|
|
1271
|
+
self.emit(Opcode::ConstructSpread);
|
|
1272
|
+
} else {
|
|
1273
|
+
self.compile_expr(callee)?;
|
|
1274
|
+
for arg in args {
|
|
1275
|
+
if let CallArg::Expr(e) = arg {
|
|
1276
|
+
self.compile_expr(e)?;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
self.emit_u16(Opcode::Construct, args.len() as u16);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1208
1282
|
}
|
|
1209
1283
|
Ok(())
|
|
1210
1284
|
}
|