@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.
Files changed (68) hide show
  1. package/Cargo.toml +1 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +15 -6
  3. package/crates/tish/Cargo.toml +1 -1
  4. package/crates/tish/src/main.rs +1 -1
  5. package/crates/tish/tests/integration_test.rs +4 -3
  6. package/crates/tish_ast/src/ast.rs +65 -2
  7. package/crates/tish_build_utils/src/lib.rs +10 -2
  8. package/crates/tish_builtins/src/construct.rs +177 -0
  9. package/crates/tish_builtins/src/globals.rs +3 -5
  10. package/crates/tish_builtins/src/helpers.rs +2 -3
  11. package/crates/tish_builtins/src/lib.rs +1 -0
  12. package/crates/tish_builtins/src/object.rs +3 -4
  13. package/crates/tish_bytecode/src/compiler.rs +85 -11
  14. package/crates/tish_bytecode/src/opcode.rs +7 -3
  15. package/crates/tish_compile/Cargo.toml +1 -0
  16. package/crates/tish_compile/src/codegen.rs +604 -106
  17. package/crates/tish_compile/src/infer.rs +236 -0
  18. package/crates/tish_compile/src/lib.rs +52 -5
  19. package/crates/tish_compile/src/types.rs +42 -5
  20. package/crates/tish_compile_js/Cargo.toml +1 -0
  21. package/crates/tish_compile_js/src/codegen.rs +38 -94
  22. package/crates/tish_compile_js/src/lib.rs +0 -1
  23. package/crates/tish_compile_js/src/tests_jsx.rs +68 -0
  24. package/crates/tish_core/Cargo.toml +4 -0
  25. package/crates/tish_core/src/console_style.rs +7 -1
  26. package/crates/tish_core/src/json.rs +1 -2
  27. package/crates/tish_core/src/macros.rs +2 -3
  28. package/crates/tish_core/src/value.rs +13 -5
  29. package/crates/tish_cranelift/src/lib.rs +6 -4
  30. package/crates/tish_cranelift/src/lower.rs +5 -3
  31. package/crates/tish_cranelift_runtime/src/lib.rs +4 -2
  32. package/crates/tish_eval/Cargo.toml +2 -0
  33. package/crates/tish_eval/src/eval.rs +172 -79
  34. package/crates/tish_eval/src/http.rs +3 -4
  35. package/crates/tish_eval/src/lib.rs +7 -0
  36. package/crates/tish_eval/src/regex.rs +3 -2
  37. package/crates/tish_eval/src/value.rs +11 -13
  38. package/crates/tish_eval/src/value_convert.rs +4 -8
  39. package/crates/tish_fmt/src/lib.rs +49 -10
  40. package/crates/tish_lexer/src/token.rs +2 -0
  41. package/crates/tish_lint/src/lib.rs +9 -0
  42. package/crates/tish_llvm/src/lib.rs +4 -4
  43. package/crates/tish_lsp/README.md +1 -1
  44. package/crates/tish_native/src/build.rs +16 -2
  45. package/crates/tish_native/src/lib.rs +10 -11
  46. package/crates/tish_opt/src/lib.rs +15 -0
  47. package/crates/tish_parser/src/lib.rs +101 -1
  48. package/crates/tish_parser/src/parser.rs +168 -51
  49. package/crates/tish_runtime/src/http.rs +4 -5
  50. package/crates/tish_runtime/src/http_fetch.rs +17 -10
  51. package/crates/tish_runtime/src/lib.rs +9 -2
  52. package/crates/tish_runtime/src/promise.rs +2 -3
  53. package/crates/tish_runtime/src/promise_io.rs +2 -3
  54. package/crates/tish_runtime/src/ws.rs +7 -7
  55. package/crates/tish_ui/Cargo.toml +17 -0
  56. package/crates/tish_ui/src/jsx.rs +390 -0
  57. package/crates/tish_ui/src/lib.rs +16 -0
  58. package/crates/tish_ui/src/runtime/hooks.rs +122 -0
  59. package/crates/tish_ui/src/runtime/mod.rs +173 -0
  60. package/crates/tish_vm/src/vm.rs +121 -27
  61. package/justfile +3 -3
  62. package/package.json +1 -1
  63. package/platform/darwin-arm64/tish +0 -0
  64. package/platform/darwin-x64/tish +0 -0
  65. package/platform/linux-arm64/tish +0 -0
  66. package/platform/linux-x64/tish +0 -0
  67. package/platform/win32-x64/tish.exe +0 -0
  68. package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
@@ -211,4 +211,72 @@ fn FileList() {
211
211
  &js[..900.min(js.len())]
212
212
  );
213
213
  }
214
+
215
+ #[test]
216
+ fn new_date_global_emits_valid_js_with_and_without_optimize() {
217
+ let src = "let epoch = new Date(0)\nconsole.log(epoch.getTime())";
218
+ let program = parse(src).expect("parse");
219
+ for optimize in [false, true] {
220
+ let js = compile_with_jsx(&program, optimize).expect("compile");
221
+ assert!(
222
+ js.contains("new Date(0)"),
223
+ "optimize={optimize}: expected `new Date(0)` in JS output:\n{js}"
224
+ );
225
+ assert!(
226
+ !js.contains("let epoch = new;"),
227
+ "optimize={optimize}: broken `new` emission (missing constructor):\n{js}"
228
+ );
229
+ }
230
+ }
231
+
232
+ #[test]
233
+ fn new_uint8array_emits_direct_new_no_preamble() {
234
+ let src = "fn f(n) { return new Uint8Array(n) }";
235
+ let program = parse(src).expect("parse");
236
+ let js = compile_with_jsx(&program, false).expect("compile");
237
+ assert!(
238
+ js.contains("new Uint8Array("),
239
+ "expected direct new Uint8Array, got: {}",
240
+ &js[..500.min(js.len())]
241
+ );
242
+ assert!(
243
+ !js.contains("__tishUint8Array"),
244
+ "should not emit legacy intrinsic helper"
245
+ );
246
+ }
247
+
248
+ #[test]
249
+ fn new_audio_context_emits_direct_new_no_preamble() {
250
+ let src = "fn f() { return new AudioContext() }";
251
+ let program = parse(src).expect("parse");
252
+ let js = compile_with_jsx(&program, false).expect("compile");
253
+ assert!(
254
+ js.contains("new AudioContext("),
255
+ "expected new AudioContext, got: {}",
256
+ &js[..500.min(js.len())]
257
+ );
258
+ assert!(
259
+ !js.contains("__tishWebAudioCreateContext"),
260
+ "should not emit legacy intrinsic helper"
261
+ );
262
+ }
263
+
264
+ #[test]
265
+ fn new_class_name_emits_direct_new_js() {
266
+ let src = r#"
267
+ fn ClassName(x) {
268
+ return x
269
+ }
270
+ fn factory() {
271
+ return new ClassName(42)
272
+ }
273
+ "#;
274
+ let program = parse(src).expect("parse");
275
+ let js = compile_with_jsx(&program, false).expect("compile");
276
+ assert!(
277
+ js.contains("new ClassName("),
278
+ "expected new ClassName( in JS output, got: {}",
279
+ &js[..800.min(js.len())]
280
+ );
281
+ }
214
282
  }
@@ -11,4 +11,8 @@ default = []
11
11
  regex = ["dep:fancy-regex"]
12
12
 
13
13
  [dependencies]
14
+ ahash = "0.8.11"
14
15
  fancy-regex = { version = "0.17.0", optional = true }
16
+
17
+ [target.wasm32-unknown-unknown.dependencies]
18
+ getrandom = { version = "0.3", features = ["wasm_js"] }
@@ -4,9 +4,12 @@
4
4
  //! booleans, null, and object structure are easier to scan.
5
5
 
6
6
  use std::io::IsTerminal;
7
+ use std::sync::OnceLock;
7
8
 
8
9
  use crate::Value;
9
10
 
11
+ static CONSOLE_USES_COLORS: OnceLock<bool> = OnceLock::new();
12
+
10
13
  /// ANSI escape codes (standard 4-bit + bright black for dim).
11
14
  const RESET: &str = "\x1b[0m";
12
15
  /// Number: yellow (Node-style)
@@ -25,8 +28,11 @@ const PUNCT: &str = "\x1b[90m";
25
28
  const SPECIAL: &str = "\x1b[90m";
26
29
 
27
30
  /// Returns whether console output should use colors (stdout is a TTY).
31
+ ///
32
+ /// Cached for the process lifetime. `is_terminal()` is a syscall; benchmarks and
33
+ /// scripts with many `console.log` calls must not pay it on every line.
28
34
  pub fn use_console_colors() -> bool {
29
- std::io::stdout().is_terminal()
35
+ *CONSOLE_USES_COLORS.get_or_init(|| std::io::stdout().is_terminal())
30
36
  }
31
37
 
32
38
  /// Format a single value for console with optional ANSI colors (Node/Bun-style).
@@ -2,7 +2,6 @@
2
2
 
3
3
  use crate::Value;
4
4
  use std::cell::RefCell;
5
- use std::collections::HashMap;
6
5
  use std::rc::Rc;
7
6
  use std::sync::Arc;
8
7
 
@@ -260,7 +259,7 @@ fn parse_array(input: &str) -> Result<(Value, &str), String> {
260
259
 
261
260
  fn parse_object(input: &str) -> Result<(Value, &str), String> {
262
261
  let mut input = &input[1..]; // skip '{'
263
- let mut map = HashMap::new();
262
+ let mut map = crate::ObjectMap::default();
264
263
 
265
264
  input = input.trim_start();
266
265
  if let Some(rest) = input.strip_prefix('}') {
@@ -24,11 +24,10 @@
24
24
  macro_rules! tish_module {
25
25
  ($($name:expr => $fn:expr),* $(,)?) => {{
26
26
  use std::cell::RefCell;
27
- use std::collections::HashMap;
28
27
  use std::rc::Rc;
29
28
  use std::sync::Arc;
30
- use $crate::Value;
31
- let mut map = HashMap::new();
29
+ use $crate::{ObjectMap, Value};
30
+ let mut map = ObjectMap::default();
32
31
  $(
33
32
  map.insert(Arc::from($name), Value::Function(Rc::new($fn)));
34
33
  )*
@@ -1,10 +1,15 @@
1
1
  //! Unified Value type for Tish runtime values.
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
 
7
+ use ahash::AHashMap;
8
+
9
+ /// Property map for objects and other `Arc<str>` → `Value` tables (VM globals, scopes).
10
+ /// Uses a faster hasher than `std::collections::HashMap` for string-heavy workloads.
11
+ pub type ObjectMap = AHashMap<Arc<str>, Value>;
12
+
8
13
  #[cfg(feature = "regex")]
9
14
  use fancy_regex::Regex;
10
15
 
@@ -20,6 +25,9 @@ pub trait TishOpaque: Send + Sync {
20
25
 
21
26
  /// Get a method by name. Returns a native function if the method exists.
22
27
  fn get_method(&self, name: &str) -> Option<NativeFn>;
28
+
29
+ /// For downcasting `Arc<dyn TishOpaque>` in native crates (e.g. Polars → `DataFrame`).
30
+ fn as_any(&self) -> &dyn std::any::Any;
23
31
  }
24
32
 
25
33
  /// Trait for Promise-like values that can be awaited (block until settled).
@@ -147,7 +155,7 @@ pub enum Value {
147
155
  Bool(bool),
148
156
  Null,
149
157
  Array(Rc<RefCell<Vec<Value>>>),
150
- Object(Rc<RefCell<HashMap<Arc<str>, Value>>>),
158
+ Object(Rc<RefCell<ObjectMap>>),
151
159
  Function(NativeFn),
152
160
  #[cfg(feature = "regex")]
153
161
  RegExp(Rc<RefCell<TishRegExp>>),
@@ -256,8 +264,8 @@ impl Value {
256
264
  Value::Array(Rc::new(RefCell::new(items)))
257
265
  }
258
266
 
259
- /// Create a new object Value from a HashMap.
260
- pub fn object(map: HashMap<Arc<str>, Value>) -> Self {
267
+ /// Create a new object Value from a property map.
268
+ pub fn object(map: ObjectMap) -> Self {
261
269
  Value::Object(Rc::new(RefCell::new(map)))
262
270
  }
263
271
 
@@ -268,7 +276,7 @@ impl Value {
268
276
 
269
277
  /// Create an empty object Value.
270
278
  pub fn empty_object() -> Self {
271
- Value::Object(Rc::new(RefCell::new(HashMap::new())))
279
+ Value::Object(Rc::new(RefCell::new(ObjectMap::default())))
272
280
  }
273
281
 
274
282
  /// Extract the number value, if this is a Number.
@@ -1,6 +1,8 @@
1
- //! Bytecode to native via Cranelift.
1
+ //! Standalone native binary: embedded bytecode + VM (Cranelift used as object builder).
2
2
  //!
3
- //! Compiles Tish bytecode to native object files and links with a minimal runtime.
3
+ //! Produces an executable that runs **`tishlang_vm`** on serialized bytecode embedded in
4
+ //! the binary — not lowering of Tish opcodes to CLIF/machine code (see module docs in
5
+ //! `lower.rs`). For Rust transpile + `tishlang_runtime`, use `--native-backend rust`.
4
6
 
5
7
  mod link;
6
8
  mod lower;
@@ -25,8 +27,8 @@ impl std::fmt::Display for CraneliftError {
25
27
 
26
28
  impl std::error::Error for CraneliftError {}
27
29
 
28
- /// Compile a bytecode chunk to a native binary.
29
- /// `features` are passed to tishlang_cranelift_runtime (e.g. fs, process, http for built-in modules).
30
+ /// Build a native binary that embeds `chunk` and runs it with the bytecode VM.
31
+ /// `features` are passed to `tishlang_cranelift_runtime` (e.g. fs, process, http).
30
32
  pub fn compile_chunk_to_native(
31
33
  chunk: &Chunk,
32
34
  output_path: &Path,
@@ -1,7 +1,9 @@
1
- //! Bytecode to Cranelift IR lowering.
1
+ //! Embed serialized bytecode in an object file for the standalone native binary.
2
2
  //!
3
- //! Emits object file with tish_chunk_data and tish_chunk_len symbols.
4
- //! The link step builds a Rust binary that reads these and runs via tishlang_vm.
3
+ //! **This is not AOT compilation of Tish into Cranelift IR.** The chunk is stored as
4
+ //! read-only data (`tish_chunk_data`, `tish_chunk_len`). The link step produces an
5
+ //! executable that **deserializes the chunk and runs `tishlang_vm`** — same VM as
6
+ //! `tish run --backend vm`. Cranelift is only the object-file emitter for that blob.
5
7
 
6
8
  use std::path::Path;
7
9
 
@@ -1,6 +1,8 @@
1
- //! Runtime for Cranelift-compiled Tish programs.
1
+ //! Runtime linked into the `tish compile --native-backend cranelift` executable.
2
2
  //!
3
- //! Provides tish_run_chunk(ptr, len) which deserializes and runs bytecode.
3
+ //! **`tish_run_chunk`** deserializes embedded bytecode and runs **`tishlang_vm`** — the same
4
+ //! execution engine as `tish run --backend vm`. The crate name is historical; this is not
5
+ //! running CLIF-emitted machine code for each Tish opcode.
4
6
 
5
7
  use tishlang_bytecode::deserialize;
6
8
  use tishlang_vm::Vm;
@@ -16,8 +16,10 @@ tokio = ["dep:tokio"]
16
16
  ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
17
17
 
18
18
  [dependencies]
19
+ ahash = "0.8.12"
19
20
  rand = "0.10.0"
20
21
  tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
22
+ tishlang_builtins = { path = "../tish_builtins", version = ">=0.1" }
21
23
  tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
22
24
  tishlang_core = { path = "../tish_core", version = ">=0.1" }
23
25
  reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }