@tishlang/tish 1.6.0 → 1.7.0
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/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +3 -1
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +31 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/src/codegen.rs +921 -299
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +15 -5
- package/crates/tish_compile/src/resolve.rs +112 -69
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +389 -142
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +1 -6
- package/crates/tish_native/src/lib.rs +48 -19
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +309 -112
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- 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
|
@@ -16,8 +16,8 @@ use std::time::{Duration, Instant};
|
|
|
16
16
|
use futures_util::{SinkExt, StreamExt};
|
|
17
17
|
use lazy_static::lazy_static;
|
|
18
18
|
use tishlang_core::{ObjectMap, Value};
|
|
19
|
-
use tokio::sync::mpsc as tokio_mpsc;
|
|
20
19
|
use tokio::runtime::Runtime;
|
|
20
|
+
use tokio::sync::mpsc as tokio_mpsc;
|
|
21
21
|
|
|
22
22
|
thread_local! {
|
|
23
23
|
/// Multi-thread runtime so `tokio::spawn` I/O tasks keep running after `block_on` returns.
|
|
@@ -200,7 +200,10 @@ fn conn_object(id: u32) -> Value {
|
|
|
200
200
|
obj.insert(
|
|
201
201
|
Arc::from("send"),
|
|
202
202
|
Value::Function(Rc::new(move |args: &[Value]| {
|
|
203
|
-
let data = args
|
|
203
|
+
let data = args
|
|
204
|
+
.first()
|
|
205
|
+
.map(|v| v.to_display_string())
|
|
206
|
+
.unwrap_or_default();
|
|
204
207
|
Value::Bool(conn_send(id, data))
|
|
205
208
|
})),
|
|
206
209
|
);
|
|
@@ -213,15 +216,13 @@ fn conn_object(id: u32) -> Value {
|
|
|
213
216
|
);
|
|
214
217
|
obj.insert(
|
|
215
218
|
Arc::from("receive"),
|
|
216
|
-
Value::Function(Rc::new(move |_args: &[Value]| {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Value::Object(Rc::new(RefCell::new(ev)))
|
|
222
|
-
}
|
|
223
|
-
None => Value::Null,
|
|
219
|
+
Value::Function(Rc::new(move |_args: &[Value]| match conn_receive(id) {
|
|
220
|
+
Some(s) => {
|
|
221
|
+
let mut ev: ObjectMap = ObjectMap::default();
|
|
222
|
+
ev.insert(Arc::from("data"), Value::String(s.into()));
|
|
223
|
+
Value::Object(Rc::new(RefCell::new(ev)))
|
|
224
224
|
}
|
|
225
|
+
None => Value::Null,
|
|
225
226
|
})),
|
|
226
227
|
);
|
|
227
228
|
let id_timeout = id;
|
|
@@ -231,7 +232,9 @@ fn conn_object(id: u32) -> Value {
|
|
|
231
232
|
let timeout_ms = args
|
|
232
233
|
.first()
|
|
233
234
|
.and_then(|v| match v {
|
|
234
|
-
Value::Number(n) if n.is_finite() && *n >= 0.0 =>
|
|
235
|
+
Value::Number(n) if n.is_finite() && *n >= 0.0 => {
|
|
236
|
+
Some((*n as u64).min(3600_000))
|
|
237
|
+
}
|
|
235
238
|
_ => None,
|
|
236
239
|
})
|
|
237
240
|
.unwrap_or(1000);
|
|
@@ -366,11 +369,17 @@ pub fn web_socket_server_listen(args: &[Value]) -> Value {
|
|
|
366
369
|
};
|
|
367
370
|
let ws_stream = match tokio_tungstenite::accept_async(stream).await {
|
|
368
371
|
Ok(ws) => {
|
|
369
|
-
eprintln!(
|
|
372
|
+
eprintln!(
|
|
373
|
+
"[tish ws] server accepted connection (handshake OK): port {}",
|
|
374
|
+
port
|
|
375
|
+
);
|
|
370
376
|
ws
|
|
371
377
|
}
|
|
372
378
|
Err(e) => {
|
|
373
|
-
eprintln!(
|
|
379
|
+
eprintln!(
|
|
380
|
+
"[tish ws] server accept_async failed: {} (port {})",
|
|
381
|
+
e, port
|
|
382
|
+
);
|
|
374
383
|
continue;
|
|
375
384
|
}
|
|
376
385
|
};
|
|
@@ -475,8 +484,7 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
475
484
|
.unwrap_or_default();
|
|
476
485
|
let cb = args.get(2).cloned().unwrap_or(Value::Null);
|
|
477
486
|
if event == "connection" {
|
|
478
|
-
so.borrow_mut()
|
|
479
|
-
.insert(Arc::from("_onConnection"), cb);
|
|
487
|
+
so.borrow_mut().insert(Arc::from("_onConnection"), cb);
|
|
480
488
|
}
|
|
481
489
|
Value::Null
|
|
482
490
|
});
|
|
@@ -535,7 +543,10 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
535
543
|
m.insert(Arc::from("clients"), Value::Array(clients));
|
|
536
544
|
m.insert(Arc::from("on"), Value::Function(on_fn));
|
|
537
545
|
m.insert(Arc::from("listen"), Value::Function(listen_fn));
|
|
538
|
-
m.insert(
|
|
546
|
+
m.insert(
|
|
547
|
+
Arc::from("acceptTimeout"),
|
|
548
|
+
Value::Function(accept_timeout_fn),
|
|
549
|
+
);
|
|
539
550
|
Value::Object(Rc::new(RefCell::new(m)))
|
|
540
551
|
}
|
|
541
552
|
|
|
@@ -578,7 +589,9 @@ mod tests {
|
|
|
578
589
|
.unwrap_or_default(),
|
|
579
590
|
_ => String::new(),
|
|
580
591
|
};
|
|
581
|
-
if let Some(Value::Function(sf)) =
|
|
592
|
+
if let Some(Value::Function(sf)) =
|
|
593
|
+
wso.borrow().get(&Arc::from("send")).cloned()
|
|
594
|
+
{
|
|
582
595
|
let _ = sf(&[Value::String(data.into())]);
|
|
583
596
|
}
|
|
584
597
|
break;
|
|
@@ -692,7 +705,11 @@ mod tests {
|
|
|
692
705
|
let _ = send_f(&[Value::String(join_msg.into())]);
|
|
693
706
|
|
|
694
707
|
// Client uses receiveTimeout like the agent
|
|
695
|
-
let recv_timeout = co
|
|
708
|
+
let recv_timeout = co
|
|
709
|
+
.borrow()
|
|
710
|
+
.get(&Arc::from("receiveTimeout"))
|
|
711
|
+
.cloned()
|
|
712
|
+
.unwrap();
|
|
696
713
|
let Value::Function(recv_timeout_f) = recv_timeout else {
|
|
697
714
|
panic!("no receiveTimeout");
|
|
698
715
|
};
|
|
@@ -707,7 +724,11 @@ mod tests {
|
|
|
707
724
|
.get(&Arc::from("data"))
|
|
708
725
|
.map(|v| v.to_display_string())
|
|
709
726
|
.unwrap_or_default();
|
|
710
|
-
assert!(
|
|
727
|
+
assert!(
|
|
728
|
+
data1.contains("\"type\":\"joined\""),
|
|
729
|
+
"expected joined, got {}",
|
|
730
|
+
data1
|
|
731
|
+
);
|
|
711
732
|
|
|
712
733
|
let got2 = recv_timeout_f(&[timeout_arg]);
|
|
713
734
|
let Value::Object(ev2) = got2 else {
|
|
@@ -718,7 +739,11 @@ mod tests {
|
|
|
718
739
|
.get(&Arc::from("data"))
|
|
719
740
|
.map(|v| v.to_display_string())
|
|
720
741
|
.unwrap_or_default();
|
|
721
|
-
assert!(
|
|
742
|
+
assert!(
|
|
743
|
+
data2.contains("\"type\":\"presence\""),
|
|
744
|
+
"expected presence, got {}",
|
|
745
|
+
data2
|
|
746
|
+
);
|
|
722
747
|
|
|
723
748
|
let _ = server.join();
|
|
724
749
|
}
|
|
@@ -51,16 +51,17 @@ fn fetch_readable_stream_read_chunks() {
|
|
|
51
51
|
Value::Object(o) => o.borrow(),
|
|
52
52
|
_ => panic!("expected object response, got {:?}", resp),
|
|
53
53
|
};
|
|
54
|
-
assert!(obj
|
|
54
|
+
assert!(obj
|
|
55
|
+
.get(&std::sync::Arc::from("ok"))
|
|
56
|
+
.map(|v| matches!(v, Value::Bool(true)))
|
|
57
|
+
.unwrap_or(false));
|
|
55
58
|
|
|
56
59
|
let body = obj.get(&std::sync::Arc::from("body")).expect("body");
|
|
57
60
|
let stream = match body {
|
|
58
61
|
Value::Opaque(s) => s.as_ref(),
|
|
59
62
|
_ => panic!("expected ReadableStream opaque"),
|
|
60
63
|
};
|
|
61
|
-
let reader_val = stream
|
|
62
|
-
.get_method("getReader")
|
|
63
|
-
.expect("getReader")(&[]);
|
|
64
|
+
let reader_val = stream.get_method("getReader").expect("getReader")(&[]);
|
|
64
65
|
let reader = match reader_val {
|
|
65
66
|
Value::Opaque(r) => r,
|
|
66
67
|
_ => panic!("expected reader opaque, got {:?}", reader_val),
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
//! Shared JSX lowering: emit `h(tag, props, children)` as JavaScript or Rust (`Value`) source.
|
|
2
2
|
|
|
3
|
-
use tishlang_ast::{
|
|
4
|
-
ArrayElement, Expr, JsxAttrValue, JsxChild, JsxProp, Literal, ObjectProp,
|
|
5
|
-
};
|
|
3
|
+
use tishlang_ast::{ArrayElement, Expr, JsxAttrValue, JsxChild, JsxProp, Literal, ObjectProp};
|
|
6
4
|
|
|
7
5
|
/// Escape a Tish identifier for Rust output (matches `tishlang_compile` conventions).
|
|
8
6
|
pub fn escape_ident_rust(s: &str) -> String {
|
|
@@ -26,20 +24,29 @@ where
|
|
|
26
24
|
children,
|
|
27
25
|
..
|
|
28
26
|
} => {
|
|
29
|
-
let tag_str = if tag
|
|
27
|
+
let tag_str = if tag
|
|
28
|
+
.chars()
|
|
29
|
+
.next()
|
|
30
|
+
.map(|c| c.is_uppercase())
|
|
31
|
+
.unwrap_or(false)
|
|
32
|
+
{
|
|
30
33
|
tag.as_ref().to_string()
|
|
31
34
|
} else {
|
|
32
35
|
format!("{:?}", tag.as_ref())
|
|
33
36
|
};
|
|
34
37
|
let props_str = emit_jsx_props_js(props, emit_expr)?;
|
|
35
|
-
let children_strs: Result<Vec<_>, _> =
|
|
36
|
-
|
|
38
|
+
let children_strs: Result<Vec<_>, _> = children
|
|
39
|
+
.iter()
|
|
40
|
+
.map(|c| emit_jsx_child_js(c, emit_expr))
|
|
41
|
+
.collect();
|
|
37
42
|
let children_str = children_strs?.join(", ");
|
|
38
43
|
Ok(format!("h({}, {}, [{}])", tag_str, props_str, children_str))
|
|
39
44
|
}
|
|
40
45
|
Expr::JsxFragment { children, .. } => {
|
|
41
|
-
let children_strs: Result<Vec<_>, _> =
|
|
42
|
-
|
|
46
|
+
let children_strs: Result<Vec<_>, _> = children
|
|
47
|
+
.iter()
|
|
48
|
+
.map(|c| emit_jsx_child_js(c, emit_expr))
|
|
49
|
+
.collect();
|
|
43
50
|
let children_str = children_strs?.join(", ");
|
|
44
51
|
Ok(format!("h(Fragment, null, [{}])", children_str))
|
|
45
52
|
}
|
|
@@ -120,7 +127,11 @@ where
|
|
|
120
127
|
children,
|
|
121
128
|
..
|
|
122
129
|
} => {
|
|
123
|
-
let is_component = tag
|
|
130
|
+
let is_component = tag
|
|
131
|
+
.chars()
|
|
132
|
+
.next()
|
|
133
|
+
.map(|c| c.is_uppercase())
|
|
134
|
+
.unwrap_or(false);
|
|
124
135
|
let tag_rust = if is_component {
|
|
125
136
|
escape_ident_rust(tag.as_ref())
|
|
126
137
|
} else {
|
|
@@ -146,11 +157,7 @@ where
|
|
|
146
157
|
"Value::Array(Rc::new(RefCell::new(vec![{}])))",
|
|
147
158
|
child_parts?.join(", ")
|
|
148
159
|
);
|
|
149
|
-
Ok(wrap_h_call_rust(
|
|
150
|
-
"Fragment",
|
|
151
|
-
"Value::Null",
|
|
152
|
-
&children_rust,
|
|
153
|
-
))
|
|
160
|
+
Ok(wrap_h_call_rust("Fragment", "Value::Null", &children_rust))
|
|
154
161
|
}
|
|
155
162
|
_ => Err(E::from("emit_jsx_rust: not a JSX expression".to_string())),
|
|
156
163
|
}
|
|
@@ -279,7 +286,13 @@ fn stmt_contains_jsx(stmt: &tishlang_ast::Statement) -> bool {
|
|
|
279
286
|
Statement::While { cond, body, .. } | Statement::DoWhile { body, cond, .. } => {
|
|
280
287
|
expr_contains_jsx(cond) || stmt_contains_jsx(body)
|
|
281
288
|
}
|
|
282
|
-
Statement::For {
|
|
289
|
+
Statement::For {
|
|
290
|
+
init,
|
|
291
|
+
cond,
|
|
292
|
+
update,
|
|
293
|
+
body,
|
|
294
|
+
..
|
|
295
|
+
} => {
|
|
283
296
|
init.as_ref().is_some_and(|s| stmt_contains_jsx(s))
|
|
284
297
|
|| cond.as_ref().is_some_and(expr_contains_jsx)
|
|
285
298
|
|| update.as_ref().is_some_and(expr_contains_jsx)
|
|
@@ -288,7 +301,12 @@ fn stmt_contains_jsx(stmt: &tishlang_ast::Statement) -> bool {
|
|
|
288
301
|
Statement::ForOf { iterable, body, .. } => {
|
|
289
302
|
expr_contains_jsx(iterable) || stmt_contains_jsx(body)
|
|
290
303
|
}
|
|
291
|
-
Statement::Switch {
|
|
304
|
+
Statement::Switch {
|
|
305
|
+
expr,
|
|
306
|
+
cases,
|
|
307
|
+
default_body,
|
|
308
|
+
..
|
|
309
|
+
} => {
|
|
292
310
|
expr_contains_jsx(expr)
|
|
293
311
|
|| cases.iter().any(|(e, ss)| {
|
|
294
312
|
e.as_ref().is_some_and(expr_contains_jsx) || ss.iter().any(stmt_contains_jsx)
|
|
@@ -372,9 +390,12 @@ fn expr_contains_jsx(expr: &Expr) -> bool {
|
|
|
372
390
|
Expr::MemberAssign { object, value, .. } => {
|
|
373
391
|
expr_contains_jsx(object) || expr_contains_jsx(value)
|
|
374
392
|
}
|
|
375
|
-
Expr::IndexAssign {
|
|
376
|
-
|
|
377
|
-
|
|
393
|
+
Expr::IndexAssign {
|
|
394
|
+
object,
|
|
395
|
+
index,
|
|
396
|
+
value,
|
|
397
|
+
..
|
|
398
|
+
} => expr_contains_jsx(object) || expr_contains_jsx(index) || expr_contains_jsx(value),
|
|
378
399
|
Expr::New { callee, args, .. } => {
|
|
379
400
|
expr_contains_jsx(callee)
|
|
380
401
|
|| args.iter().any(|a| match a {
|
|
@@ -383,8 +404,6 @@ fn expr_contains_jsx(expr: &Expr) -> bool {
|
|
|
383
404
|
}
|
|
384
405
|
})
|
|
385
406
|
}
|
|
386
|
-
Expr::Literal { .. }
|
|
387
|
-
| Expr::Ident { .. }
|
|
388
|
-
| Expr::NativeModuleLoad { .. } => false,
|
|
407
|
+
Expr::Literal { .. } | Expr::Ident { .. } | Expr::NativeModuleLoad { .. } => false,
|
|
389
408
|
}
|
|
390
409
|
}
|
|
@@ -11,6 +11,6 @@ pub mod runtime;
|
|
|
11
11
|
|
|
12
12
|
#[cfg(feature = "runtime")]
|
|
13
13
|
pub use runtime::{
|
|
14
|
-
fragment_value, install_thread_local_host, native_create_root, native_use_state, ui_h,
|
|
15
|
-
|
|
14
|
+
fragment_value, install_thread_local_host, native_create_root, native_use_state, ui_h, ui_text,
|
|
15
|
+
with_thread_local_host, HeadlessHost, Host, FRAGMENT_SENTINEL,
|
|
16
16
|
};
|