@tishlang/tish-format 1.0.13 → 2.0.1

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 (108) hide show
  1. package/Cargo.toml +2 -0
  2. package/bin/tish-format +0 -0
  3. package/crates/js_to_tish/src/transform/expr.rs +1 -0
  4. package/crates/tish/Cargo.toml +10 -2
  5. package/crates/tish/build.rs +21 -0
  6. package/crates/tish/src/cli_help.rs +15 -4
  7. package/crates/tish/src/main.rs +93 -21
  8. package/crates/tish/src/repl_completion.rs +0 -1
  9. package/crates/tish/tests/error_source_location.rs +36 -0
  10. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  11. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  12. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  13. package/crates/tish/tests/integration_test.rs +402 -91
  14. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  15. package/crates/tish/tests/tty_capability.rs +43 -0
  16. package/crates/tish_ast/src/ast.rs +37 -8
  17. package/crates/tish_builtins/Cargo.toml +2 -0
  18. package/crates/tish_builtins/src/array.rs +375 -13
  19. package/crates/tish_builtins/src/collections.rs +481 -0
  20. package/crates/tish_builtins/src/construct.rs +59 -19
  21. package/crates/tish_builtins/src/date.rs +538 -0
  22. package/crates/tish_builtins/src/globals.rs +86 -6
  23. package/crates/tish_builtins/src/iterator.rs +129 -0
  24. package/crates/tish_builtins/src/lib.rs +5 -0
  25. package/crates/tish_builtins/src/number.rs +96 -0
  26. package/crates/tish_builtins/src/object.rs +2 -2
  27. package/crates/tish_builtins/src/string.rs +19 -20
  28. package/crates/tish_builtins/src/symbol.rs +1 -1
  29. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  30. package/crates/tish_bytecode/src/chunk.rs +69 -1
  31. package/crates/tish_bytecode/src/compiler.rs +933 -89
  32. package/crates/tish_bytecode/src/encoding.rs +2 -0
  33. package/crates/tish_bytecode/src/lib.rs +2 -1
  34. package/crates/tish_bytecode/src/opcode.rs +47 -4
  35. package/crates/tish_bytecode/src/serialize.rs +31 -1
  36. package/crates/tish_compile/Cargo.toml +1 -0
  37. package/crates/tish_compile/src/check.rs +774 -0
  38. package/crates/tish_compile/src/codegen.rs +2334 -349
  39. package/crates/tish_compile/src/infer.rs +1395 -6
  40. package/crates/tish_compile/src/lib.rs +50 -8
  41. package/crates/tish_compile/src/resolve.rs +584 -21
  42. package/crates/tish_compile/src/types.rs +106 -2
  43. package/crates/tish_compile_js/src/codegen.rs +67 -0
  44. package/crates/tish_compile_js/src/tests_jsx.rs +64 -0
  45. package/crates/tish_core/Cargo.toml +7 -1
  46. package/crates/tish_core/src/console_style.rs +11 -1
  47. package/crates/tish_core/src/json.rs +81 -38
  48. package/crates/tish_core/src/lib.rs +3 -0
  49. package/crates/tish_core/src/shape.rs +85 -0
  50. package/crates/tish_core/src/value.rs +679 -25
  51. package/crates/tish_core/src/vmref.rs +13 -8
  52. package/crates/tish_cranelift/src/link.rs +17 -4
  53. package/crates/tish_cranelift_runtime/Cargo.toml +1 -0
  54. package/crates/tish_eval/Cargo.toml +6 -0
  55. package/crates/tish_eval/src/eval.rs +665 -117
  56. package/crates/tish_eval/src/http.rs +4 -1
  57. package/crates/tish_eval/src/natives.rs +165 -13
  58. package/crates/tish_eval/src/value.rs +31 -13
  59. package/crates/tish_eval/src/value_convert.rs +10 -4
  60. package/crates/tish_ffi/Cargo.toml +26 -0
  61. package/crates/tish_ffi/src/lib.rs +518 -0
  62. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  63. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  64. package/crates/tish_ffi/tests/loader.rs +65 -0
  65. package/crates/tish_fmt/Cargo.toml +1 -1
  66. package/crates/tish_fmt/src/lib.rs +61 -5
  67. package/crates/tish_lexer/src/lib.rs +397 -9
  68. package/crates/tish_lexer/src/token.rs +7 -0
  69. package/crates/tish_lint/src/lib.rs +2 -10
  70. package/crates/tish_lsp/src/import_goto.rs +2 -0
  71. package/crates/tish_lsp/src/main.rs +439 -26
  72. package/crates/tish_native/src/build.rs +55 -1
  73. package/crates/tish_opt/src/lib.rs +126 -23
  74. package/crates/tish_parser/src/lib.rs +55 -1
  75. package/crates/tish_parser/src/parser.rs +456 -34
  76. package/crates/tish_pg/src/lib.rs +3 -3
  77. package/crates/tish_resolve/src/lib.rs +99 -59
  78. package/crates/tish_runtime/Cargo.toml +4 -0
  79. package/crates/tish_runtime/src/http.rs +66 -17
  80. package/crates/tish_runtime/src/http_fetch.rs +29 -8
  81. package/crates/tish_runtime/src/http_hyper.rs +25 -2
  82. package/crates/tish_runtime/src/lib.rs +299 -44
  83. package/crates/tish_runtime/src/promise.rs +328 -18
  84. package/crates/tish_runtime/src/timers.rs +13 -7
  85. package/crates/tish_runtime/src/tty.rs +226 -0
  86. package/crates/tish_runtime/src/ws.rs +35 -18
  87. package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
  88. package/crates/tish_ui/src/jsx.rs +10 -0
  89. package/crates/tish_ui/src/runtime/hooks.rs +19 -15
  90. package/crates/tish_ui/src/runtime/mod.rs +15 -12
  91. package/crates/tish_vm/Cargo.toml +14 -1
  92. package/crates/tish_vm/src/jit.rs +1050 -0
  93. package/crates/tish_vm/src/lib.rs +2 -0
  94. package/crates/tish_vm/src/vm.rs +1546 -202
  95. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  96. package/crates/tish_wasm/src/lib.rs +6 -2
  97. package/crates/tish_wasm_runtime/src/gpu.rs +17 -1
  98. package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
  99. package/crates/tishlang_cargo_bindgen/src/lib.rs +2 -2
  100. package/crates/tishlang_cargo_bindgen/src/metadata.rs +1 -1
  101. package/justfile +8 -0
  102. package/package.json +2 -2
  103. package/platform/darwin-arm64/tish-fmt +0 -0
  104. package/platform/darwin-x64/tish-fmt +0 -0
  105. package/platform/linux-arm64/tish-fmt +0 -0
  106. package/platform/linux-x64/tish-fmt +0 -0
  107. package/platform/win32-x64/tish-fmt.exe +0 -0
  108. package/README.md +0 -138
@@ -0,0 +1,140 @@
1
+ //! Regression: concurrent HTTP handlers that mutate shared module-level state must not deadlock.
2
+ //!
3
+ //! ## What this guards
4
+ //!
5
+ //! Under `send-values` (forced on by the `http` feature), `serve(port, handler)` runs the handler
6
+ //! closure — a `NativeFn` (`Arc<dyn Callable>`, `Send + Sync`) — **directly on each accept thread**
7
+ //! (`tish_runtime::http::worker_loop_direct`). So N concurrent requests execute the SAME handler in
8
+ //! parallel, all sharing the captured module scope through `Arc<Mutex>` (`VmRef`). A handler that
9
+ //! mutates a module-level `let` (a request counter / cache / rate-limiter) therefore has many threads
10
+ //! reading and writing the same scope cell at once.
11
+ //!
12
+ //! This test drives that exact path without a network: it pulls the handler `Value::Function` out of
13
+ //! a freshly-run program and invokes it from many OS threads while they all read-modify-write a shared
14
+ //! module-level `let`. It exists to catch a regression where the VM's variable-write path holds a
15
+ //! scope guard across a re-acquisition of the same lock (which would deadlock concurrent writers and
16
+ //! hang `serve`). The watchdog turns such a hang into a fast, explicit test failure instead of a
17
+ //! stuck CI job.
18
+ //!
19
+ //! Note on coverage: macOS `SO_REUSEPORT` funnels HTTP accepts to a single worker thread, so a
20
+ //! network-level test can't actually run two handlers concurrently on macOS. Calling the handler
21
+ //! closure directly does — and OS thread scheduling + mutex semantics are platform-independent, so
22
+ //! this reproduces the Linux multi-worker dispatch contention the bug was reported against.
23
+ #![cfg(feature = "send-values")]
24
+
25
+ use std::sync::atomic::{AtomicUsize, Ordering};
26
+ use std::sync::Arc;
27
+ use std::time::{Duration, Instant};
28
+ use tishlang_bytecode::compile;
29
+ use tishlang_core::{NativeFn, Value};
30
+ use tishlang_vm::Vm;
31
+
32
+ // `Value::Function` holds a `NativeFn` (= `Arc<dyn Callable>`, `Callable: Send + Sync` under
33
+ // send-values); invoke a handler via the trait-object method `.call(args)`.
34
+ type Handler = NativeFn;
35
+
36
+ /// Compile + run `src`, then pull a function it stored in a global back out.
37
+ fn export(vm: &Vm, name: &str) -> Handler {
38
+ match vm.get_global(name).unwrap_or_else(|| panic!("global `{name}` not found")) {
39
+ Value::Function(f) => f,
40
+ other => panic!("global `{name}` is not a function: {other:?}"),
41
+ }
42
+ }
43
+
44
+ fn read_num(obj: &Value, field: &str) -> f64 {
45
+ match obj {
46
+ Value::Object(o) => match o.borrow().strings.get(field) {
47
+ Some(Value::Number(n)) => *n,
48
+ other => panic!("stats.{field} is not a number: {other:?}"),
49
+ },
50
+ other => panic!("stats is not an object: {other:?}"),
51
+ }
52
+ }
53
+
54
+ #[test]
55
+ fn concurrent_handlers_mutating_shared_module_state_do_not_deadlock() {
56
+ // `handler`/`stats` are bare top-level assignments (undeclared names) -> stored in globals, so
57
+ // the test can pull them out. Both functions close over the same module-level `let`s, exactly as
58
+ // a real `serve` handler closes over module state. `served` is monotonic (only incremented), so
59
+ // it gives a deterministic plausibility bound even though the read-modify-write is racy.
60
+ let src = r#"
61
+ let active = 0
62
+ let maxActive = 0
63
+ let served = 0
64
+ fn handleRequest(req) {
65
+ active = active + 1
66
+ served = served + 1
67
+ if (active > maxActive) { maxActive = active }
68
+ let i = 0
69
+ while (i < 2000) { i = i + 1 } // brief CPU hold so handlers overlap
70
+ active = active - 1
71
+ return { status: 200, body: "ok" }
72
+ }
73
+ fn getStats() {
74
+ return { active: active, maxActive: maxActive, served: served }
75
+ }
76
+ handler = handleRequest
77
+ stats = getStats
78
+ "#;
79
+ let program = tishlang_parser::parse(src).expect("parse");
80
+ let chunk = compile(&program).expect("compile");
81
+ let mut vm = Vm::new();
82
+ vm.run(&chunk).expect("run top-level");
83
+ let handler = export(&vm, "handler");
84
+ let stats = export(&vm, "stats");
85
+
86
+ const THREADS: usize = 12;
87
+ const ITERS: usize = 100;
88
+ let total = THREADS * ITERS;
89
+
90
+ let done = Arc::new(AtomicUsize::new(0));
91
+ let start = Instant::now();
92
+ let mut handles = Vec::with_capacity(THREADS);
93
+ for t in 0..THREADS {
94
+ let h = handler.clone();
95
+ let done = Arc::clone(&done);
96
+ handles.push(std::thread::spawn(move || {
97
+ for i in 0..ITERS {
98
+ let resp = h.call(&[Value::Number((t * ITERS + i) as f64)]);
99
+ assert!(matches!(resp, Value::Object(_)), "handler must return a response object");
100
+ done.fetch_add(1, Ordering::Relaxed);
101
+ }
102
+ }));
103
+ }
104
+
105
+ // Watchdog: if concurrent writers deadlocked on the scope mutex, `done` stops advancing.
106
+ // Fail fast (and loudly) rather than hang the test runner.
107
+ let mut last = 0usize;
108
+ let mut last_change = Instant::now();
109
+ while done.load(Ordering::Relaxed) < total {
110
+ let cur = done.load(Ordering::Relaxed);
111
+ if cur != last {
112
+ last = cur;
113
+ last_change = Instant::now();
114
+ }
115
+ assert!(
116
+ last_change.elapsed() < Duration::from_secs(15),
117
+ "DEADLOCK regression: concurrent handlers stalled at {cur}/{total} (no progress for 15s)"
118
+ );
119
+ std::thread::sleep(Duration::from_millis(20));
120
+ }
121
+ for h in handles {
122
+ h.join().expect("a handler thread panicked");
123
+ }
124
+
125
+ // All calls returned without hanging. Read the shared counters back.
126
+ let s = stats.call(&[]);
127
+ let served = read_num(&s, "served");
128
+ let max_active = read_num(&s, "maxActive");
129
+ let active = read_num(&s, "active");
130
+ eprintln!(
131
+ "completed {total} concurrent calls / {THREADS} threads in {:?}; served={served}, maxActive={max_active}, active(final)={active}",
132
+ start.elapsed()
133
+ );
134
+
135
+ // `served` is monotonic, so it is deterministically in (0, total] regardless of lost updates.
136
+ assert!(served > 0.0 && served <= total as f64, "served={served} out of plausible range (0, {total}]");
137
+ // `maxActive` >= 2 proves at least two handlers were genuinely in-flight simultaneously, i.e. we
138
+ // actually exercised concurrent shared-state mutation (not an accidentally-serialized run).
139
+ assert!(max_active >= 2.0, "handlers never overlapped (maxActive={max_active}); test did not exercise concurrency");
140
+ }
@@ -379,10 +379,14 @@ fn main() {
379
379
  message: format!("Cannot write main.rs: {}", e),
380
380
  })?;
381
381
 
382
- // Build - use explicit target-dir so we know where the artifact is
382
+ // Build into a SHARED target dir (one per host), not per-program. The wasi runtime + embedded
383
+ // VM then compile ONCE and are reused by every wasi build; only each program's tiny main is
384
+ // rebuilt. Without this each program left its own multi-GB `target/` and a full-suite sweep
385
+ // would fill the disk (same issue fixed for cranelift; see full-backend-parity-plan.md A3).
386
+ // cargo's target lock serializes concurrent builds safely.
383
387
  let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
384
388
  let bin_name = format!("tish_wasi_{}", stem);
385
- let target_dir = build_dir.join("target");
389
+ let target_dir = std::env::temp_dir().join("tishlang_wasi_target");
386
390
  let build_status = Command::new(&cargo)
387
391
  .current_dir(&build_dir)
388
392
  .env("CARGO_TARGET_DIR", &target_dir)
@@ -10,7 +10,7 @@
10
10
  //! command API is synchronous, so this covers it)
11
11
  //! - `js_new(ctorNameOrHandle, argsArray)`
12
12
  //! - `js_typeof(handle)` — debugging
13
- //! - `f32a(arr)` / `u16a(arr)` / `u8a(arr)` — tish `number[]` → real typed array
13
+ //! - `f32a(arr)` / `u16a(arr)` / `u8a(arr)` / `u32a(arr)` — tish `number[]` → real typed array
14
14
  //! - `request_animation_frame(cb)` — drive a render loop
15
15
  //!
16
16
  //! GPU/JS objects (device, queue, context, buffers, pipelines, textures,
@@ -284,6 +284,21 @@ fn ffi_u8a() -> Value {
284
284
  })
285
285
  }
286
286
 
287
+ fn ffi_u32a() -> Value {
288
+ Value::native(|args: &[Value]| {
289
+ let arr = match args.first() {
290
+ Some(Value::Array(a)) => a.clone(),
291
+ _ => return Value::Null,
292
+ };
293
+ let b = arr.borrow();
294
+ let ta = js_sys::Uint32Array::new_with_length(b.len() as u32);
295
+ for (i, v) in b.iter().enumerate() {
296
+ ta.set_index(i as u32, v.as_number().unwrap_or(0.0) as u32);
297
+ }
298
+ wrap(ta.into())
299
+ })
300
+ }
301
+
287
302
  // ---------------------------------------------------------------------------
288
303
  // requestAnimationFrame render loop
289
304
  // ---------------------------------------------------------------------------
@@ -362,6 +377,7 @@ fn install_ffi(vm: &mut Vm) {
362
377
  vm.set_global("f32a".into(), ffi_f32a());
363
378
  vm.set_global("u16a".into(), ffi_u16a());
364
379
  vm.set_global("u8a".into(), ffi_u8a());
380
+ vm.set_global("u32a".into(), ffi_u32a());
365
381
  vm.set_global("request_animation_frame".into(), ffi_request_animation_frame());
366
382
  }
367
383
 
@@ -39,9 +39,7 @@ fn classify_tish_abi(item: &ItemFn) -> Option<SignatureClass> {
39
39
  if value_args != 1 || sig.inputs.len() != 1 {
40
40
  return None;
41
41
  }
42
- let Some(ret_ty) = return_type_inner(&sig.output) else {
43
- return None;
44
- };
42
+ let ret_ty = return_type_inner(&sig.output)?;
45
43
  if !is_value_type(ret_ty) {
46
44
  return None;
47
45
  }
@@ -121,7 +121,7 @@ fn generate_from_resolved(
121
121
  impl BindgenConfig {
122
122
  /// Write using [`generate_from_registry_dependency`].
123
123
  pub fn write_files(&self) -> io::Result<()> {
124
- generate_from_registry_dependency(self).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
124
+ generate_from_registry_dependency(self).map_err(io::Error::other)
125
125
  }
126
126
  }
127
127
 
@@ -199,7 +199,7 @@ fn render_generated_lib(
199
199
  name = rust_fn
200
200
  ),
201
201
  SignatureClass::SerializeRefToResultString => format!(
202
- "pub fn {name}(args: &[Value]) -> Value {{\n let Some(v) = args.first() else {{ return Value::Null }};\n match _tish_upstream::{name}(&tish_to_json(v)) {{\n Ok(s) => Value::String(Arc::from(s)),\n Err(_) => Value::Null,\n }}\n}}\n\n",
202
+ "pub fn {name}(args: &[Value]) -> Value {{\n let Some(v) = args.first() else {{ return Value::Null }};\n match _tish_upstream::{name}(&tish_to_json(v)) {{\n Ok(s) => Value::String(arcstr::ArcStr::from(s)),\n Err(_) => Value::Null,\n }}\n}}\n\n",
203
203
  name = rust_fn
204
204
  ),
205
205
  SignatureClass::DeserializeStrToResult => format!(
@@ -79,7 +79,7 @@ edition = "2021"
79
79
  && !pkg
80
80
  .targets
81
81
  .iter()
82
- .any(|t| t.kind.iter().any(|k| *k == TargetKind::Lib))
82
+ .any(|t| t.kind.contains(&TargetKind::Lib))
83
83
  {
84
84
  return Err(format!(
85
85
  "package `{}` has no src/ or lib target at {}",
package/justfile CHANGED
@@ -258,6 +258,14 @@ perf-suite *ARGS:
258
258
  perf-suite-gen:
259
259
  ./scripts/generate_perf_ci_main.sh
260
260
 
261
+ # HTTP throughput: tish vs Node, single vs multi-worker, plaintext + json (needs oha + jq)
262
+ perf-http *ARGS:
263
+ ./scripts/run_http_perf.sh {{ARGS}}
264
+
265
+ # Perf gauntlet: compute benchmarks vs Node, incl. known-fail targets to evolve past (needs node)
266
+ perf-gauntlet *ARGS:
267
+ ./scripts/run_perf_gauntlet.sh {{ARGS}}
268
+
261
269
  # Show binary sizes for different builds
262
270
  sizes:
263
271
  @echo "Building secure binary..."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tishlang/tish-format",
3
- "version": "1.0.13",
3
+ "version": "2.0.1",
4
4
  "description": "Tish code formatter",
5
5
  "license": "PIF",
6
6
  "repository": {
@@ -34,4 +34,4 @@
34
34
  "format",
35
35
  "formatter"
36
36
  ]
37
- }
37
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
package/README.md DELETED
@@ -1,138 +0,0 @@
1
- <picture>
2
- <img alt="Tishlang" src="https://tishlang.com/banner.png">
3
- </picture>
4
-
5
- # Tish
6
-
7
- <p>
8
- <a href="https://npmjs.com/package/@tishlang/tish?activeTab=readme"><img src="https://img.shields.io/npm/v/@tishlang/tish?style=flat-square&colorA=1C1C1C&colorB=B688FF" alt="npm version" /></a>
9
- <a href="https://npmcharts.com/compare/@tishlang/tish"><img src="https://img.shields.io/npm/dm/@tishlang/tish.svg?style=flat-square&colorA=1C1C1C&colorB=B688FF" alt="downloads" /></a>
10
- <a href="https://nodejs.org/en/about/previous-releases"><img src="https://img.shields.io/node/v/@tishlang/tish.svg?style=flat-square&colorA=1C1C1C&colorB=B688FF" alt="node version"></a>
11
- <a href="https://crates.io/crates/tishlang"><img src="https://img.shields.io/crates/v/tishlang?style=flat-square&colorA=1C1C1C&colorB=B688FF" alt="crate version" /></a>
12
- <a href="https://github.com/tishlang/tish/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-PIF-blue.svg?style=flat-square&colorA=1C1C1C&colorB=B688FF" alt="license" /></a>
13
-
14
- </p>
15
-
16
- Tish is a TypeScript- and JavaScript-compatible language implemented in Rust. It is aimed at teams who want a familiar surface syntax, predictable semantics, and the option to ship either interpreted scripts or runtime free **native binaries**.
17
-
18
- The same source can run on a tree-walking interpreter, a bytecode VM, or compiled targets (native, WASM/WASI, and others). Network, filesystem, and process APIs are **feature-gated** so defaults stay safe. For the full syntax and semantics, see the canonical spec in-repo; for tutorials and reference, see [tishlang.com/docs](https://tishlang.com/docs).
19
-
20
- ## 🔥 Features
21
-
22
- - **JS-like surface** — `let` / `const`, `fn`, arrows, template literals, async/await, modules, and a large slice of familiar builtins (`console`, `Math`, `JSON`, arrays, strings, objects).
23
- - **Two ways to run** — interpret for fast iteration (`tish run`, REPL); compile to native or WASM for distribution and performance (`tish build`).
24
- - **Memory-safe implementation** — Rust-hosted runtime and compiler pipeline; no GC in the host language for the toolchain itself.
25
- - **Secure by default** — I/O and platform APIs (`http`, `fs`, `process`, etc.) are opt-in via features.
26
- - **No `undefined`** — `null` only where JS would use undefined; strict equality (`===` / `!==`) without loose coercion.
27
- - **Optional types** — TypeScript-style annotations are parsed for tooling and future checking; see the language reference for status.
28
-
29
- Full specification: [docs/LANGUAGE.md](docs/LANGUAGE.md). Implementation status, gaps, and JS compatibility: [docs/plan-gap-analysis.md](docs/plan-gap-analysis.md).
30
-
31
- ## 📚 Documentation
32
-
33
- User-facing guides and reference:
34
-
35
- | Section | Description |
36
- |---------|-------------|
37
- | [Getting started](https://tishlang.com/docs/getting-started/installation/) | Install Tish, build your first app |
38
- | [First app](https://tishlang.com/docs/getting-started/first-app/) | Run and build workflows, targets |
39
- | [Editor & IDE](https://tishlang.com/docs/getting-started/editor/) | VS Code, LSP, tasks, Neovim |
40
- | [Language server](https://tishlang.com/docs/reference/language-server/) | `tish-lsp` capabilities |
41
- | [Formatting](https://tishlang.com/docs/reference/formatting/) | `tish-fmt` |
42
- | [Linting](https://tishlang.com/docs/reference/linting/) | `tish-lint` |
43
- | [Interactive REPL](https://tishlang.com/docs/getting-started/repl/) | Multi-line input, completion, history |
44
- | [Language overview](https://tishlang.com/docs/language/overview/) | Syntax, keywords, semantics |
45
- | [Tish vs JavaScript](https://tishlang.com/docs/language/vs-javascript/) | Differences and additions from JS |
46
- | [Builtins](https://tishlang.com/docs/builtins/overview/) | Console, Math, JSON, Array, String, Object |
47
- | [Features (APIs)](https://tishlang.com/docs/features/http/) | `http`, `fs`, `process`, `regex` — feature-gated |
48
- | [Native backend](https://tishlang.com/docs/reference/native-backend/) | Rust, Cranelift, LLVM compilation |
49
- | [WASM targets](https://tishlang.com/docs/reference/wasm-targets/) | Web and WASI |
50
- | [Deploy](https://tishlang.com/docs/deploy/overview/) | Platform and hosting |
51
-
52
- ### In-repo docs
53
-
54
- Contributor- and spec-oriented material in [docs/](docs/):
55
-
56
- | File | Purpose |
57
- |------|---------|
58
- | [LANGUAGE.md](docs/LANGUAGE.md) | Canonical language reference (syntax, semantics, builtins) |
59
- | [ecma-alignment.md](docs/ecma-alignment.md) | ECMA-262 / test262 mapping |
60
- | [plan-gap-analysis.md](docs/plan-gap-analysis.md) | Implementation audit, MVP checklist |
61
- | [architecture-next-steps.md](docs/architecture-next-steps.md) | Crate layout, design decisions |
62
- | [builtins-gap-analysis.md](docs/builtins-gap-analysis.md) | Builtins across Rust vs bytecode VM (Cranelift/WASI) |
63
-
64
- ## 🛠️ Toolchain
65
-
66
- | Tool | Purpose |
67
- |------|---------|
68
- | **`tish`** | CLI — `run`, `repl`, `build`, `dump-ast` |
69
- | **`tish-fmt`** | Formatter |
70
- | **`tish-lint`** | Linter (`--format sarif` for code scanning) |
71
- | **`tish-lsp`** | Language server; uses `tish_fmt` / `tish_lint` as libraries |
72
- | **`tish-security/`** (sibling repo) | Rules & CI examples — clone next to `tish/` as `../tish-security` ([OpenGrep](https://github.com/opengrep/opengrep), ast-grep, Comby, Codacy scaffold, fixtures) |
73
- | **`tree-sitter-tish/`** | Tree-sitter grammar (editors, ast-grep, future OpenGrep integration) |
74
- | **VS Code extension** | [tish-vscode](https://github.com/tishlang/tish-vscode) — grammar, snippets, LSP client, tasks |
75
-
76
- Related docs on [tishlang.com](https://tishlang.com/docs): [Editor & IDE](https://tishlang.com/docs/getting-started/editor/), [Language server](https://tishlang.com/docs/reference/language-server/), [Formatting](https://tishlang.com/docs/reference/formatting/), [Linting](https://tishlang.com/docs/reference/linting/).
77
-
78
- ## 📦 Installation
79
-
80
- Install globally:
81
-
82
- ```sh
83
- brew tap tishlang/tish https://github.com/tishlang/tish
84
- brew install tish
85
- ```
86
-
87
- Or locally with npm:
88
-
89
- ```sh
90
- npm install @tishlang/tish
91
- ```
92
-
93
- More options: [Installation](https://tishlang.com/docs/getting-started/installation/).
94
-
95
- ## ⚡ Quick start
96
-
97
- ```sh
98
- npx @tishlang/create-tish-app my-app
99
- cd my-app
100
- npx @tishlang/tish run src/main.tish
101
- ```
102
-
103
- ## ▶️ Run and build
104
-
105
- ```tish
106
- // hello.tish
107
- fn greeting(name) = `Hello, ${name}!`
108
- console.log(greeting("World"))
109
- ```
110
-
111
- ```bash
112
- tish run hello.tish
113
- # Hello, World!
114
-
115
- tish build hello.tish -o hello
116
- ./hello
117
- # Hello, World!
118
- ```
119
-
120
- Native binaries are standalone (no Tish or Rust runtime required on the machine that runs them). Backends, flags, and WASM are covered in [First app](https://tishlang.com/docs/getting-started/first-app/), [Native backend](https://tishlang.com/docs/reference/native-backend/), and [WASM targets](https://tishlang.com/docs/reference/wasm-targets/).
121
-
122
- ## 🤝 Contribution
123
-
124
- Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for building, testing, and code style. Tish is licensed under the [Pay It Forward License (PIF)](https://payitforwardlicense.com/).
125
-
126
- ## 💪 Performance
127
-
128
- JavaScript equivalents live in `tests/core/*.js`. Compare Tish with Node.js or Bun:
129
-
130
- ```bash
131
- ./scripts/run_performance_manual.sh
132
- ```
133
-
134
- Details: [docs/perf.md](docs/perf.md).
135
-
136
- ## 📝 License
137
-
138
- Tish is licensed under the [Pay It Forward License (PIF)](https://payitforwardlicense.com/). See [LICENSE](LICENSE).