@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.
Files changed (79) hide show
  1. package/Cargo.toml +1 -0
  2. package/bin/tish +0 -0
  3. package/crates/js_to_tish/src/error.rs +2 -8
  4. package/crates/js_to_tish/src/transform/expr.rs +101 -130
  5. package/crates/js_to_tish/src/transform/stmt.rs +25 -22
  6. package/crates/tish/Cargo.toml +1 -1
  7. package/crates/tish/src/cli_help.rs +76 -29
  8. package/crates/tish/src/main.rs +85 -54
  9. package/crates/tish/tests/cargo_example_compile.rs +3 -1
  10. package/crates/tish/tests/integration_test.rs +197 -47
  11. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  12. package/crates/tish/tests/shortcircuit.rs +19 -4
  13. package/crates/tish_ast/src/ast.rs +12 -14
  14. package/crates/tish_build_utils/src/lib.rs +31 -6
  15. package/crates/tish_builtins/src/array.rs +52 -21
  16. package/crates/tish_builtins/src/construct.rs +2 -8
  17. package/crates/tish_builtins/src/globals.rs +30 -15
  18. package/crates/tish_builtins/src/lib.rs +5 -5
  19. package/crates/tish_builtins/src/math.rs +5 -3
  20. package/crates/tish_builtins/src/string.rs +71 -19
  21. package/crates/tish_bytecode/src/chunk.rs +0 -1
  22. package/crates/tish_bytecode/src/compiler.rs +164 -60
  23. package/crates/tish_bytecode/src/opcode.rs +13 -4
  24. package/crates/tish_bytecode/src/peephole.rs +2 -2
  25. package/crates/tish_compile/src/codegen.rs +921 -299
  26. package/crates/tish_compile/src/infer.rs +69 -19
  27. package/crates/tish_compile/src/lib.rs +15 -5
  28. package/crates/tish_compile/src/resolve.rs +112 -69
  29. package/crates/tish_compile/src/types.rs +10 -14
  30. package/crates/tish_compile_js/src/codegen.rs +34 -13
  31. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  32. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  33. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
  34. package/crates/tish_core/src/json.rs +5 -3
  35. package/crates/tish_core/src/lib.rs +1 -1
  36. package/crates/tish_core/src/uri.rs +9 -6
  37. package/crates/tish_core/src/value.rs +92 -28
  38. package/crates/tish_cranelift/src/link.rs +6 -9
  39. package/crates/tish_cranelift/src/lower.rs +14 -8
  40. package/crates/tish_eval/src/eval.rs +389 -142
  41. package/crates/tish_eval/src/lib.rs +10 -6
  42. package/crates/tish_eval/src/natives.rs +95 -38
  43. package/crates/tish_eval/src/promise.rs +14 -8
  44. package/crates/tish_eval/src/timers.rs +28 -19
  45. package/crates/tish_eval/src/value.rs +10 -3
  46. package/crates/tish_fmt/src/lib.rs +29 -13
  47. package/crates/tish_lexer/src/lib.rs +217 -63
  48. package/crates/tish_lexer/src/token.rs +6 -6
  49. package/crates/tish_llvm/src/lib.rs +15 -8
  50. package/crates/tish_lsp/src/main.rs +41 -43
  51. package/crates/tish_native/src/build.rs +1 -6
  52. package/crates/tish_native/src/lib.rs +48 -19
  53. package/crates/tish_opt/src/lib.rs +67 -50
  54. package/crates/tish_parser/src/lib.rs +36 -11
  55. package/crates/tish_parser/src/parser.rs +172 -87
  56. package/crates/tish_runtime/src/http.rs +15 -6
  57. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  58. package/crates/tish_runtime/src/lib.rs +224 -168
  59. package/crates/tish_runtime/src/promise.rs +1 -5
  60. package/crates/tish_runtime/src/ws.rs +45 -20
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  62. package/crates/tish_ui/src/jsx.rs +41 -22
  63. package/crates/tish_ui/src/lib.rs +2 -2
  64. package/crates/tish_vm/src/vm.rs +309 -112
  65. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  66. package/crates/tish_wasm/src/lib.rs +38 -28
  67. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  68. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  69. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  70. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  71. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  72. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  73. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  74. package/package.json +1 -1
  75. package/platform/darwin-arm64/tish +0 -0
  76. package/platform/darwin-x64/tish +0 -0
  77. package/platform/linux-arm64/tish +0 -0
  78. package/platform/linux-x64/tish +0 -0
  79. 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.first().map(|v| v.to_display_string()).unwrap_or_default();
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
- match conn_receive(id) {
218
- Some(s) => {
219
- let mut ev: ObjectMap = ObjectMap::default();
220
- ev.insert(Arc::from("data"), Value::String(s.into()));
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 => Some((*n as u64).min(3600_000)),
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!("[tish ws] server accepted connection (handshake OK): port {}", port);
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!("[tish ws] server accept_async failed: {} (port {})", e, port);
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(Arc::from("acceptTimeout"), Value::Function(accept_timeout_fn));
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)) = wso.borrow().get(&Arc::from("send")).cloned() {
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.borrow().get(&Arc::from("receiveTimeout")).cloned().unwrap();
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!(data1.contains("\"type\":\"joined\""), "expected joined, got {}", data1);
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!(data2.contains("\"type\":\"presence\""), "expected presence, got {}", data2);
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.get(&std::sync::Arc::from("ok")).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false));
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.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) {
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
- children.iter().map(|c| emit_jsx_child_js(c, emit_expr)).collect();
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
- children.iter().map(|c| emit_jsx_child_js(c, emit_expr)).collect();
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.chars().next().map(|c| c.is_uppercase()).unwrap_or(false);
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 { init, cond, update, body, .. } => {
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 { expr, cases, default_body, .. } => {
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 { object, index, value, .. } => {
376
- expr_contains_jsx(object) || expr_contains_jsx(index) || expr_contains_jsx(value)
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
- ui_text, with_thread_local_host, Host, HeadlessHost, FRAGMENT_SENTINEL,
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
  };