@tishlang/tish 1.0.28 → 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 +8 -55
- 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 -1
- package/crates/tish_compile_js/src/codegen.rs +43 -147
- package/crates/tish_compile_js/src/lib.rs +4 -7
- package/crates/tish_compile_js/src/tests_jsx.rs +89 -19
- package/crates/tish_compiler_wasm/src/lib.rs +2 -3
- 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_jsx_web/Cargo.toml +1 -1
- package/crates/tish_jsx_web/README.md +3 -16
- package/crates/tish_jsx_web/src/lib.rs +2 -157
- 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/crates/tish_jsx_web/vendor/Lattish.tish +0 -362
package/crates/tish_vm/src/vm.rs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
//! Stack-based bytecode VM.
|
|
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
7
|
use tishlang_ast::{BinOp, UnaryOp};
|
|
9
8
|
use tishlang_builtins::array as arr_builtins;
|
|
9
|
+
use tishlang_builtins::construct as construct_builtin;
|
|
10
10
|
use tishlang_builtins::string as str_builtins;
|
|
11
11
|
use tishlang_builtins::globals as globals_builtins;
|
|
12
12
|
use tishlang_builtins::math as math_builtins;
|
|
13
13
|
use tishlang_bytecode::{u8_to_binop, u8_to_unaryop, Chunk, Constant, Opcode, NO_REST_PARAM};
|
|
14
|
-
use tishlang_core::Value;
|
|
14
|
+
use tishlang_core::{ObjectMap, Value};
|
|
15
15
|
|
|
16
16
|
type ArrayMethodFn = Rc<dyn Fn(&[Value]) -> Value>;
|
|
17
17
|
|
|
@@ -69,7 +69,7 @@ fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
|
|
|
69
69
|
.collect(),
|
|
70
70
|
)))),
|
|
71
71
|
"process" => {
|
|
72
|
-
let mut m =
|
|
72
|
+
let mut m = ObjectMap::default();
|
|
73
73
|
m.insert("exit".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))));
|
|
74
74
|
m.insert("cwd".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args))));
|
|
75
75
|
m.insert("exec".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exec(args))));
|
|
@@ -145,10 +145,10 @@ fn vm_log_err(s: &str) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/// Initialize default globals (console, Math, JSON, etc.)
|
|
148
|
-
fn init_globals() ->
|
|
149
|
-
let mut g =
|
|
148
|
+
fn init_globals() -> ObjectMap {
|
|
149
|
+
let mut g = ObjectMap::default();
|
|
150
150
|
|
|
151
|
-
let mut console =
|
|
151
|
+
let mut console = ObjectMap::default();
|
|
152
152
|
console.insert(
|
|
153
153
|
"debug".into(),
|
|
154
154
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -191,7 +191,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
191
191
|
);
|
|
192
192
|
g.insert("console".into(), Value::Object(Rc::new(RefCell::new(console))));
|
|
193
193
|
|
|
194
|
-
let mut math =
|
|
194
|
+
let mut math = ObjectMap::default();
|
|
195
195
|
math.insert(
|
|
196
196
|
"abs".into(),
|
|
197
197
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -257,7 +257,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
257
257
|
math.insert("E".into(), Value::Number(std::f64::consts::E));
|
|
258
258
|
g.insert("Math".into(), Value::Object(Rc::new(RefCell::new(math))));
|
|
259
259
|
|
|
260
|
-
let mut json =
|
|
260
|
+
let mut json = ObjectMap::default();
|
|
261
261
|
json.insert(
|
|
262
262
|
"parse".into(),
|
|
263
263
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -292,7 +292,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
292
292
|
);
|
|
293
293
|
|
|
294
294
|
// Date - at minimum Date.now() for timing
|
|
295
|
-
let mut date =
|
|
295
|
+
let mut date = ObjectMap::default();
|
|
296
296
|
date.insert(
|
|
297
297
|
"now".into(),
|
|
298
298
|
Value::Function(Rc::new(|_args: &[Value]| {
|
|
@@ -305,8 +305,17 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
305
305
|
);
|
|
306
306
|
g.insert("Date".into(), Value::Object(Rc::new(RefCell::new(date))));
|
|
307
307
|
|
|
308
|
+
g.insert(
|
|
309
|
+
"Uint8Array".into(),
|
|
310
|
+
construct_builtin::uint8_array_constructor_value(),
|
|
311
|
+
);
|
|
312
|
+
g.insert(
|
|
313
|
+
"AudioContext".into(),
|
|
314
|
+
construct_builtin::audio_context_constructor_value(),
|
|
315
|
+
);
|
|
316
|
+
|
|
308
317
|
// Object methods - delegate to tishlang_builtins::globals
|
|
309
|
-
let mut object_methods =
|
|
318
|
+
let mut object_methods = ObjectMap::default();
|
|
310
319
|
object_methods.insert(
|
|
311
320
|
"assign".into(),
|
|
312
321
|
Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_assign(args))),
|
|
@@ -321,7 +330,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
321
330
|
g.insert("Object".into(), Value::Object(Rc::new(RefCell::new(object_methods))));
|
|
322
331
|
|
|
323
332
|
// Array.isArray
|
|
324
|
-
let mut array_static =
|
|
333
|
+
let mut array_static = ObjectMap::default();
|
|
325
334
|
array_static.insert(
|
|
326
335
|
"isArray".into(),
|
|
327
336
|
Value::Function(Rc::new(|args: &[Value]| globals_builtins::array_is_array(args))),
|
|
@@ -330,7 +339,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
330
339
|
|
|
331
340
|
// String(value) as callable + String.fromCharCode
|
|
332
341
|
let string_convert_fn = Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_convert(args)));
|
|
333
|
-
let mut string_static =
|
|
342
|
+
let mut string_static = ObjectMap::default();
|
|
334
343
|
string_static.insert("fromCharCode".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_from_char_code(args))));
|
|
335
344
|
string_static.insert(Arc::from("__call"), string_convert_fn);
|
|
336
345
|
g.insert("String".into(), Value::Object(Rc::new(RefCell::new(string_static))));
|
|
@@ -342,12 +351,12 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
342
351
|
);
|
|
343
352
|
g.insert(
|
|
344
353
|
"Fragment".into(),
|
|
345
|
-
Value::Object(Rc::new(RefCell::new(
|
|
354
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::default()))),
|
|
346
355
|
);
|
|
347
356
|
g.insert(
|
|
348
357
|
"createRoot".into(),
|
|
349
358
|
Value::Function(Rc::new(|_args: &[Value]| {
|
|
350
|
-
let mut render_obj =
|
|
359
|
+
let mut render_obj = ObjectMap::default();
|
|
351
360
|
render_obj.insert(
|
|
352
361
|
"render".into(),
|
|
353
362
|
Value::Function(Rc::new(|_args: &[Value]| Value::Null)),
|
|
@@ -363,13 +372,13 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
363
372
|
Value::Array(Rc::new(RefCell::new(arr)))
|
|
364
373
|
})),
|
|
365
374
|
);
|
|
366
|
-
let mut document_obj =
|
|
375
|
+
let mut document_obj = ObjectMap::default();
|
|
367
376
|
document_obj.insert("body".into(), Value::Null);
|
|
368
377
|
g.insert("document".into(), Value::Object(Rc::new(RefCell::new(document_obj))));
|
|
369
378
|
|
|
370
379
|
#[cfg(feature = "process")]
|
|
371
380
|
{
|
|
372
|
-
let mut process_obj =
|
|
381
|
+
let mut process_obj = ObjectMap::default();
|
|
373
382
|
process_obj.insert(
|
|
374
383
|
"exit".into(),
|
|
375
384
|
Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))),
|
|
@@ -418,21 +427,21 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
|
|
|
418
427
|
}
|
|
419
428
|
|
|
420
429
|
/// Shared scope for closure capture (parent frame's locals).
|
|
421
|
-
type ScopeMap = Rc<RefCell<
|
|
430
|
+
type ScopeMap = Rc<RefCell<ObjectMap>>;
|
|
422
431
|
|
|
423
432
|
pub struct Vm {
|
|
424
433
|
stack: Vec<Value>,
|
|
425
|
-
scope:
|
|
434
|
+
scope: ObjectMap,
|
|
426
435
|
/// Enclosing scope for closures (captured parent frame locals).
|
|
427
436
|
enclosing: Option<ScopeMap>,
|
|
428
|
-
globals: Rc<RefCell<
|
|
437
|
+
globals: Rc<RefCell<ObjectMap>>,
|
|
429
438
|
}
|
|
430
439
|
|
|
431
440
|
impl Vm {
|
|
432
441
|
pub fn new() -> Self {
|
|
433
442
|
Self {
|
|
434
443
|
stack: Vec::new(),
|
|
435
|
-
scope:
|
|
444
|
+
scope: ObjectMap::default(),
|
|
436
445
|
enclosing: None,
|
|
437
446
|
globals: Rc::new(RefCell::new(init_globals())),
|
|
438
447
|
}
|
|
@@ -477,7 +486,7 @@ impl Vm {
|
|
|
477
486
|
let names = &chunk.names;
|
|
478
487
|
|
|
479
488
|
let mut ip = 0;
|
|
480
|
-
let local_scope: ScopeMap = Rc::new(RefCell::new(
|
|
489
|
+
let local_scope: ScopeMap = Rc::new(RefCell::new(ObjectMap::default()));
|
|
481
490
|
{
|
|
482
491
|
let mut ls = local_scope.borrow_mut();
|
|
483
492
|
let param_count = chunk.param_count as usize;
|
|
@@ -538,7 +547,7 @@ impl Vm {
|
|
|
538
547
|
Value::Function(Rc::new(move |args: &[Value]| {
|
|
539
548
|
let mut vm = Vm {
|
|
540
549
|
stack: Vec::new(),
|
|
541
|
-
scope:
|
|
550
|
+
scope: ObjectMap::default(),
|
|
542
551
|
enclosing: enclosing.clone(),
|
|
543
552
|
globals: Rc::clone(&globals),
|
|
544
553
|
};
|
|
@@ -723,6 +732,45 @@ impl Vm {
|
|
|
723
732
|
let result = f(&args);
|
|
724
733
|
self.stack.push(result);
|
|
725
734
|
}
|
|
735
|
+
Opcode::Construct => {
|
|
736
|
+
let argc = Self::read_u16(code, &mut ip) as usize;
|
|
737
|
+
let mut args = Vec::with_capacity(argc);
|
|
738
|
+
for _ in 0..argc {
|
|
739
|
+
args.push(
|
|
740
|
+
self.stack
|
|
741
|
+
.pop()
|
|
742
|
+
.ok_or_else(|| "Stack underflow in construct".to_string())?,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
args.reverse();
|
|
746
|
+
let callee = self
|
|
747
|
+
.stack
|
|
748
|
+
.pop()
|
|
749
|
+
.ok_or_else(|| "Stack underflow: no callee for construct".to_string())?;
|
|
750
|
+
let result = construct_builtin::construct(&callee, &args);
|
|
751
|
+
self.stack.push(result);
|
|
752
|
+
}
|
|
753
|
+
Opcode::ConstructSpread => {
|
|
754
|
+
let callee = self
|
|
755
|
+
.stack
|
|
756
|
+
.pop()
|
|
757
|
+
.ok_or_else(|| "Stack underflow: callee in ConstructSpread".to_string())?;
|
|
758
|
+
let args_array = self
|
|
759
|
+
.stack
|
|
760
|
+
.pop()
|
|
761
|
+
.ok_or_else(|| "Stack underflow in ConstructSpread".to_string())?;
|
|
762
|
+
let args: Vec<Value> = match &args_array {
|
|
763
|
+
Value::Array(a) => a.borrow().clone(),
|
|
764
|
+
_ => {
|
|
765
|
+
return Err(format!(
|
|
766
|
+
"ConstructSpread: args must be array, got {}",
|
|
767
|
+
args_array.to_display_string()
|
|
768
|
+
));
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
let result = construct_builtin::construct(&callee, &args);
|
|
772
|
+
self.stack.push(result);
|
|
773
|
+
}
|
|
726
774
|
Opcode::Return => {
|
|
727
775
|
let v = self.stack.pop().unwrap_or(Value::Null);
|
|
728
776
|
return Ok(v);
|
|
@@ -861,7 +909,7 @@ impl Vm {
|
|
|
861
909
|
}
|
|
862
910
|
Opcode::NewObject => {
|
|
863
911
|
let n = Self::read_u16(code, &mut ip) as usize;
|
|
864
|
-
let mut map =
|
|
912
|
+
let mut map = ObjectMap::with_capacity(n.max(1));
|
|
865
913
|
for _ in 0..n {
|
|
866
914
|
let val = self
|
|
867
915
|
.stack
|
|
@@ -926,7 +974,11 @@ impl Vm {
|
|
|
926
974
|
.stack
|
|
927
975
|
.pop()
|
|
928
976
|
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
929
|
-
let
|
|
977
|
+
let cap = match (&left, &right) {
|
|
978
|
+
(Value::Object(l), Value::Object(r)) => l.borrow().len() + r.borrow().len(),
|
|
979
|
+
_ => 0,
|
|
980
|
+
};
|
|
981
|
+
let mut merged: ObjectMap = ObjectMap::with_capacity(cap.max(8));
|
|
930
982
|
if let Value::Object(l) = &left {
|
|
931
983
|
merged.extend(l.borrow().iter().map(|(k, v)| (Arc::clone(k), v.clone())));
|
|
932
984
|
} else {
|
|
@@ -1112,6 +1164,40 @@ impl Default for Vm {
|
|
|
1112
1164
|
}
|
|
1113
1165
|
}
|
|
1114
1166
|
|
|
1167
|
+
/// Rough byte capacity for string coercion (matches hot paths like `"x" + n + "ms"`).
|
|
1168
|
+
fn estimate_string_concat_len(v: &Value) -> usize {
|
|
1169
|
+
match v {
|
|
1170
|
+
Value::String(s) => s.len(),
|
|
1171
|
+
Value::Number(_) => 24,
|
|
1172
|
+
Value::Bool(_) => 5,
|
|
1173
|
+
Value::Null => 4,
|
|
1174
|
+
_ => 32,
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/// Append JS-style string conversion without an intermediate `String` per operand (unlike
|
|
1179
|
+
/// `format!("{}{}", a.to_display_string(), b.to_display_string())`, which triple-allocates).
|
|
1180
|
+
fn append_value_for_string_concat(out: &mut String, v: &Value) {
|
|
1181
|
+
use std::fmt::Write;
|
|
1182
|
+
match v {
|
|
1183
|
+
Value::Number(n) => {
|
|
1184
|
+
if n.is_nan() {
|
|
1185
|
+
out.push_str("NaN");
|
|
1186
|
+
} else if *n == f64::INFINITY {
|
|
1187
|
+
out.push_str("Infinity");
|
|
1188
|
+
} else if *n == f64::NEG_INFINITY {
|
|
1189
|
+
out.push_str("-Infinity");
|
|
1190
|
+
} else {
|
|
1191
|
+
let _ = write!(out, "{n}");
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
Value::String(s) => out.push_str(s.as_ref()),
|
|
1195
|
+
Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
|
|
1196
|
+
Value::Null => out.push_str("null"),
|
|
1197
|
+
_ => out.push_str(&v.to_display_string()),
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1115
1201
|
fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
|
|
1116
1202
|
use tishlang_ast::BinOp::*;
|
|
1117
1203
|
use tishlang_core::Value::*;
|
|
@@ -1120,7 +1206,11 @@ fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
|
|
|
1120
1206
|
match op {
|
|
1121
1207
|
Add => {
|
|
1122
1208
|
if matches!(l, Value::String(_)) || matches!(r, Value::String(_)) {
|
|
1123
|
-
|
|
1209
|
+
let cap = estimate_string_concat_len(l) + estimate_string_concat_len(r);
|
|
1210
|
+
let mut buf = std::string::String::with_capacity(cap);
|
|
1211
|
+
append_value_for_string_concat(&mut buf, l);
|
|
1212
|
+
append_value_for_string_concat(&mut buf, r);
|
|
1213
|
+
Ok(String(buf.into()))
|
|
1124
1214
|
} else {
|
|
1125
1215
|
Ok(Number(ln + rn))
|
|
1126
1216
|
}
|
|
@@ -1146,9 +1236,13 @@ fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
|
|
|
1146
1236
|
Shl => Ok(Number(((ln as i32) << (rn as i32)) as f64)),
|
|
1147
1237
|
Shr => Ok(Number(((ln as i32) >> (rn as i32)) as f64)),
|
|
1148
1238
|
In => {
|
|
1149
|
-
let key_s: Arc<str> = l
|
|
1239
|
+
let key_s: Arc<str> = match l {
|
|
1240
|
+
Value::String(s) => Arc::clone(s),
|
|
1241
|
+
Value::Number(n) => n.to_string().into(),
|
|
1242
|
+
_ => l.to_display_string().into(),
|
|
1243
|
+
};
|
|
1150
1244
|
Ok(Bool(match r {
|
|
1151
|
-
Value::Object(m) => m.borrow().contains_key(
|
|
1245
|
+
Value::Object(m) => m.borrow().contains_key(key_s.as_ref()),
|
|
1152
1246
|
Value::Array(a) => {
|
|
1153
1247
|
if key_s.as_ref() == "length" {
|
|
1154
1248
|
true
|
package/justfile
CHANGED
|
@@ -125,15 +125,15 @@ test *ARGS:
|
|
|
125
125
|
|
|
126
126
|
# Run only tish package tests (same as CI: integration tests only)
|
|
127
127
|
test-tish *ARGS:
|
|
128
|
-
cargo nextest run -p
|
|
128
|
+
cargo nextest run -p tishlang--features full -- {{ARGS}}
|
|
129
129
|
|
|
130
130
|
# Skip slow backend tests (native/cranelift/wasi) for fast local iteration
|
|
131
131
|
test-quick:
|
|
132
|
-
cargo nextest run -p
|
|
132
|
+
cargo nextest run -p tishlang--features full -- --skip test_mvp_programs_native --skip test_mvp_programs_cranelift --skip test_mvp_programs_wasi
|
|
133
133
|
|
|
134
134
|
# Run tests with coverage (requires llvm-tools: rustup component add llvm-tools-preview)
|
|
135
135
|
test-coverage:
|
|
136
|
-
cargo llvm-cov nextest -p
|
|
136
|
+
cargo llvm-cov nextest -p tishlang--features full --lcov --output-path lcov.info --html coverage-html
|
|
137
137
|
|
|
138
138
|
# Plain cargo test (whole workspace)
|
|
139
139
|
test-cargo:
|
package/package.json
CHANGED
|
Binary file
|
package/platform/darwin-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|
package/platform/linux-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
//! JS-only call sites that require `new` (Tish has no `new`).
|
|
2
|
-
//! Intrinsic names, validation, and runtime preamble live here — main codegen only dispatches.
|
|
3
|
-
|
|
4
|
-
use tishlang_ast::{CallArg, Expr};
|
|
5
|
-
|
|
6
|
-
use crate::error::CompileError;
|
|
7
|
-
|
|
8
|
-
/// Built-in calls lowered to `new ...` in the JS emit.
|
|
9
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
10
|
-
pub enum JsIntrinsic {
|
|
11
|
-
WebAudioCreateContext,
|
|
12
|
-
Uint8Array,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
#[derive(Debug, Default)]
|
|
16
|
-
pub struct JsIntrinsics {
|
|
17
|
-
pub needs_web_audio: bool,
|
|
18
|
-
pub needs_uint8_array: bool,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
impl JsIntrinsics {
|
|
22
|
-
pub fn new() -> Self {
|
|
23
|
-
Self::default()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/// Recognize `webAudioCreateContext()` / `jsUint8Array(n)` and validate arguments.
|
|
27
|
-
pub fn classify_call(callee: &Expr, args: &[CallArg]) -> Result<Option<JsIntrinsic>, CompileError> {
|
|
28
|
-
let Expr::Ident { name, .. } = callee else {
|
|
29
|
-
return Ok(None);
|
|
30
|
-
};
|
|
31
|
-
match name.as_ref() {
|
|
32
|
-
"webAudioCreateContext" => {
|
|
33
|
-
if !args.is_empty() {
|
|
34
|
-
return Err(CompileError::new(
|
|
35
|
-
"webAudioCreateContext() takes no arguments (JS target only)",
|
|
36
|
-
));
|
|
37
|
-
}
|
|
38
|
-
Ok(Some(JsIntrinsic::WebAudioCreateContext))
|
|
39
|
-
}
|
|
40
|
-
"jsUint8Array" => {
|
|
41
|
-
if args.len() != 1 {
|
|
42
|
-
return Err(CompileError::new(
|
|
43
|
-
"jsUint8Array(length) expects one argument (JS target only)",
|
|
44
|
-
));
|
|
45
|
-
}
|
|
46
|
-
Ok(Some(JsIntrinsic::Uint8Array))
|
|
47
|
-
}
|
|
48
|
-
_ => Ok(None),
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
pub fn mark(&mut self, kind: JsIntrinsic) {
|
|
53
|
-
match kind {
|
|
54
|
-
JsIntrinsic::WebAudioCreateContext => self.needs_web_audio = true,
|
|
55
|
-
JsIntrinsic::Uint8Array => self.needs_uint8_array = true,
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
pub fn emit_expr(kind: JsIntrinsic, uint8_length_js: &str) -> String {
|
|
60
|
-
match kind {
|
|
61
|
-
JsIntrinsic::WebAudioCreateContext => "(__tishWebAudioCreateContext() ?? null)".to_string(),
|
|
62
|
-
JsIntrinsic::Uint8Array => format!("(__tishUint8Array({}) ?? null)", uint8_length_js),
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/// Prepend runtime helpers (Uint8Array above AudioContext when both are used).
|
|
67
|
-
pub fn prepend_runtime_preamble(&self, mut output: String) -> String {
|
|
68
|
-
if self.needs_web_audio {
|
|
69
|
-
output = format!(
|
|
70
|
-
"function __tishWebAudioCreateContext(){{ return new AudioContext(); }}\n{}",
|
|
71
|
-
output
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
if self.needs_uint8_array {
|
|
75
|
-
output = format!(
|
|
76
|
-
"function __tishUint8Array(n){{ return new Uint8Array(n); }}\n{}",
|
|
77
|
-
output
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
output
|
|
81
|
-
}
|
|
82
|
-
}
|