@tishlang/tish 1.4.2 → 1.5.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/bin/tish +0 -0
- package/crates/tish/Cargo.toml +2 -2
- package/crates/tish/src/cli_help.rs +504 -0
- package/crates/tish/src/main.rs +76 -90
- package/crates/tish/src/repl_completion.rs +1 -1
- package/crates/tish/tests/integration_test.rs +48 -0
- package/crates/tish_build_utils/src/lib.rs +171 -1
- package/crates/tish_builtins/src/string.rs +248 -0
- package/crates/tish_bytecode/Cargo.toml +1 -0
- package/crates/tish_bytecode/src/compiler.rs +289 -66
- package/crates/tish_bytecode/src/opcode.rs +13 -3
- package/crates/tish_bytecode/src/peephole.rs +21 -16
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_compile/src/codegen.rs +214 -79
- package/crates/tish_compile/src/lib.rs +1 -1
- package/crates/tish_core/src/value.rs +1 -0
- package/crates/tish_eval/src/eval.rs +39 -1
- package/crates/tish_eval/src/lib.rs +1 -1
- package/crates/tish_native/src/build.rs +48 -7
- package/crates/tish_native/src/lib.rs +8 -3
- package/crates/tish_runtime/src/lib.rs +4 -0
- package/crates/tish_vm/src/lib.rs +1 -1
- package/crates/tish_vm/src/vm.rs +155 -16
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -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
package/bin/tish
CHANGED
|
Binary file
|
package/crates/tish/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tishlang"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Tish CLI - run, REPL, compile to native"
|
|
6
6
|
license-file = { workspace = true }
|
|
@@ -39,7 +39,7 @@ tishlang_wasm = { path = "../tish_wasm", version = ">=0.1" }
|
|
|
39
39
|
tishlang_runtime = { path = "../tish_runtime", version = ">=0.1" }
|
|
40
40
|
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
41
41
|
tishlang_js_to_tish = { path = "../js_to_tish", version = ">=0.1" }
|
|
42
|
-
clap = { version = "4.6.0", features = ["derive"] }
|
|
42
|
+
clap = { version = "4.6.0", features = ["derive", "color"] }
|
|
43
43
|
|
|
44
44
|
[dev-dependencies]
|
|
45
45
|
rayon = "1.11"
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
//! Long help text, terminal styling, and ASCII banner for the `tish` CLI.
|
|
2
|
+
|
|
3
|
+
use std::io::{self, IsTerminal, Write};
|
|
4
|
+
use std::thread;
|
|
5
|
+
use std::time::Duration;
|
|
6
|
+
|
|
7
|
+
use clap::builder::styling::{Color, Effects, RgbColor, Style, Styles};
|
|
8
|
+
use clap::{CommandFactory, Parser, Subcommand};
|
|
9
|
+
|
|
10
|
+
/// FIGlet-style block letters (UTF-8). On a TTY, a short expand + palette-color animation runs.
|
|
11
|
+
const TISH_BANNER_LINES: &[&str] = &[
|
|
12
|
+
"",
|
|
13
|
+
"████████╗██╗███████╗██╗ ██╗",
|
|
14
|
+
"╚══██╔══╝██║██╔════╝██║ ██║",
|
|
15
|
+
" ██║ ██║███████╗███████║",
|
|
16
|
+
" ██║ ██║╚════██║██╔══██║",
|
|
17
|
+
" ██║ ██║███████║██║ ██║",
|
|
18
|
+
" ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/// Frames used for the left-to-right expand reveal.
|
|
22
|
+
const BANNER_REVEAL_FRAMES: usize = 14;
|
|
23
|
+
/// Extra frames of rainbow cycling after the logo is fully visible.
|
|
24
|
+
const BANNER_CYCLE_FRAMES: usize = 4;
|
|
25
|
+
const BANNER_FRAME_MS: u64 = 20;
|
|
26
|
+
|
|
27
|
+
/// Orange → Yellow → Green → Teal → Blue → Purple → Pink (matching the brand palette).
|
|
28
|
+
const PALETTE: &[(u8, u8, u8)] = &[
|
|
29
|
+
(255, 159, 64), // Orange
|
|
30
|
+
(255, 213, 64), // Yellow
|
|
31
|
+
( 52, 199, 89), // Green
|
|
32
|
+
( 48, 209, 188), // Teal
|
|
33
|
+
( 10, 132, 255), // Blue
|
|
34
|
+
(175, 82, 222), // Purple
|
|
35
|
+
(255, 55, 148), // Pink
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
fn ease_out_cubic(t: f32) -> f32 {
|
|
39
|
+
let u = 1.0 - t.clamp(0.0, 1.0);
|
|
40
|
+
1.0 - u * u * u
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Linearly interpolate between two palette colors.
|
|
44
|
+
fn lerp_color(a: (u8, u8, u8), b: (u8, u8, u8), t: f32) -> (u8, u8, u8) {
|
|
45
|
+
let t = t.clamp(0.0, 1.0);
|
|
46
|
+
(
|
|
47
|
+
(a.0 as f32 + (b.0 as f32 - a.0 as f32) * t).round() as u8,
|
|
48
|
+
(a.1 as f32 + (b.1 as f32 - a.1 as f32) * t).round() as u8,
|
|
49
|
+
(a.2 as f32 + (b.2 as f32 - a.2 as f32) * t).round() as u8,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Smooth palette sample for a given (row, col) cell and scrolling color frame.
|
|
54
|
+
/// Uses column as the primary gradient axis so every row has a continuous sweep.
|
|
55
|
+
/// A small per-row offset adds a gentle diagonal tilt rather than flat stripes.
|
|
56
|
+
fn palette_color(row: usize, col: usize, color_frame: usize) -> (u8, u8, u8) {
|
|
57
|
+
let n = PALETTE.len();
|
|
58
|
+
// one full palette cycle every ~5 columns; row adds a slight diagonal
|
|
59
|
+
let scroll = color_frame as f32 * 0.22;
|
|
60
|
+
let pos = ((col as f32 / 5.0) + (row as f32 * 0.25) + scroll).rem_euclid(n as f32);
|
|
61
|
+
let lo = pos.floor() as usize % n;
|
|
62
|
+
let hi = (lo + 1) % n;
|
|
63
|
+
lerp_color(PALETTE[lo], PALETTE[hi], pos.fract())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Render one frame. `reveal_t` is 0..=1 (how much of each line is visible).
|
|
67
|
+
/// `color_frame` is the ever-incrementing counter that drives the rainbow scroll.
|
|
68
|
+
fn write_tish_banner_frame(out: &mut impl Write, reveal_t: f32, color_frame: usize) {
|
|
69
|
+
for (row, line) in TISH_BANNER_LINES.iter().enumerate() {
|
|
70
|
+
let chars: Vec<char> = line.chars().collect();
|
|
71
|
+
let len = chars.len();
|
|
72
|
+
let visible = ((len as f32) * reveal_t).round() as usize;
|
|
73
|
+
let visible = visible.min(len);
|
|
74
|
+
|
|
75
|
+
for col in 0..len {
|
|
76
|
+
let ch = chars[col];
|
|
77
|
+
if col >= visible || ch == ' ' {
|
|
78
|
+
let _ = write!(out, " ");
|
|
79
|
+
} else {
|
|
80
|
+
let (r, g, b) = palette_color(row, col, color_frame);
|
|
81
|
+
let _ = write!(out, "\x1b[1;38;2;{r};{g};{b}m{ch}\x1b[0m");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let _ = writeln!(out);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn print_tish_banner_plain(out: &mut impl Write) {
|
|
89
|
+
for line in TISH_BANNER_LINES {
|
|
90
|
+
let _ = writeln!(out, "{line}");
|
|
91
|
+
}
|
|
92
|
+
let _ = writeln!(out);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fn print_tish_banner_animated(out: &mut impl Write) {
|
|
96
|
+
let n = TISH_BANNER_LINES.len();
|
|
97
|
+
let total = BANNER_REVEAL_FRAMES + BANNER_CYCLE_FRAMES;
|
|
98
|
+
|
|
99
|
+
for f in 0..total {
|
|
100
|
+
if f > 0 {
|
|
101
|
+
let _ = write!(out, "\x1b[{n}A");
|
|
102
|
+
}
|
|
103
|
+
// Phase 1: ease-out expand. Phase 2: fully visible, rainbow keeps scrolling.
|
|
104
|
+
let reveal_t = if f < BANNER_REVEAL_FRAMES {
|
|
105
|
+
ease_out_cubic((f + 1) as f32 / BANNER_REVEAL_FRAMES as f32)
|
|
106
|
+
} else {
|
|
107
|
+
1.0
|
|
108
|
+
};
|
|
109
|
+
write_tish_banner_frame(out, reveal_t, f);
|
|
110
|
+
let _ = out.flush();
|
|
111
|
+
thread::sleep(Duration::from_millis(BANNER_FRAME_MS));
|
|
112
|
+
}
|
|
113
|
+
let _ = writeln!(out);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// Print the `TISH` tile banner to stdout (animated palette on a TTY; plain text otherwise).
|
|
117
|
+
pub fn print_tish_banner() {
|
|
118
|
+
let mut out = io::stdout().lock();
|
|
119
|
+
if io::stdout().is_terminal() {
|
|
120
|
+
print_tish_banner_animated(&mut out);
|
|
121
|
+
} else {
|
|
122
|
+
print_tish_banner_plain(&mut out);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Build the `Command` with all colored after_help text attached.
|
|
127
|
+
/// Use this instead of `Cli::command()` everywhere so the help text is consistent.
|
|
128
|
+
pub fn build_command() -> clap::Command {
|
|
129
|
+
Cli::command()
|
|
130
|
+
.after_help(cli_after_help())
|
|
131
|
+
.mut_subcommand("run", |sub| sub.after_help(run_after_help()))
|
|
132
|
+
.mut_subcommand("repl", |sub| sub.after_help(repl_after_help()))
|
|
133
|
+
.mut_subcommand("build", |sub| sub.after_long_help(build_after_help()))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Write help text to `w` (plain bytes, used for line-counting only).
|
|
137
|
+
fn count_help_lines(cmd: &mut clap::Command, sub_name: Option<&str>) -> usize {
|
|
138
|
+
let mut buf = Vec::<u8>::new();
|
|
139
|
+
if let Some(name) = sub_name {
|
|
140
|
+
if cmd.find_subcommand(name).is_some() {
|
|
141
|
+
let _ = cmd.find_subcommand_mut(name).unwrap().write_long_help(&mut buf);
|
|
142
|
+
} else {
|
|
143
|
+
let _ = cmd.write_long_help(&mut buf);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
let _ = cmd.write_long_help(&mut buf);
|
|
147
|
+
}
|
|
148
|
+
buf.iter().filter(|&&b| b == b'\n').count()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// Print help text directly to stdout via clap's own stdout path (guaranteed colors).
|
|
152
|
+
fn print_help_to_stdout(cmd: &mut clap::Command, sub_name: Option<&str>) {
|
|
153
|
+
if let Some(name) = sub_name {
|
|
154
|
+
if cmd.find_subcommand(name).is_some() {
|
|
155
|
+
let _ = cmd.find_subcommand_mut(name).unwrap().print_long_help();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
let _ = cmd.print_long_help();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Detect which subcommand (if any) is being asked about from raw argv.
|
|
163
|
+
fn sub_name_from_argv(argv: &[String]) -> Option<String> {
|
|
164
|
+
match argv.get(1).map(String::as_str) {
|
|
165
|
+
Some("help") => argv.get(2).map(String::to_string), // tish help run
|
|
166
|
+
Some(s) if !s.starts_with('-') => Some(s.to_string()), // tish run --help
|
|
167
|
+
_ => None,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
172
|
+
/// ANSI: bold purple (175, 82, 222)
|
|
173
|
+
const H_PURPLE: &str = "\x1b[1;38;2;175;82;222m";
|
|
174
|
+
/// ANSI: medium grey — clearly less-than-white on dark backgrounds
|
|
175
|
+
const H_GREY: &str = "\x1b[38;2;150;150;150m";
|
|
176
|
+
/// ANSI: pink (255, 55, 148) — used for the website URL
|
|
177
|
+
const H_PINK: &str = "\x1b[38;2;255;55;148m";
|
|
178
|
+
const H_RESET: &str = "\x1b[0m";
|
|
179
|
+
|
|
180
|
+
/// Branded header used for subcommand help pages.
|
|
181
|
+
/// Prints `[purple]Tish[reset] [grey](version x)[reset]`
|
|
182
|
+
/// `[pink]https://tishlang.com[reset]`
|
|
183
|
+
/// followed by a blank line.
|
|
184
|
+
fn print_small_header() {
|
|
185
|
+
if io::stdout().is_terminal() {
|
|
186
|
+
println!("{H_PURPLE}Tish{H_RESET} {H_GREY}(version {VERSION}){H_RESET}");
|
|
187
|
+
println!("{H_PINK}https://tishlang.com{H_RESET}\n");
|
|
188
|
+
} else {
|
|
189
|
+
println!("Tish (version {VERSION})");
|
|
190
|
+
println!("https://tishlang.com\n");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// Number of lines the main-help manual prefix takes (printed before clap output).
|
|
195
|
+
/// Layout: title, description, url, blank = 4 lines.
|
|
196
|
+
const MAIN_PREFIX_LINES: usize = 4;
|
|
197
|
+
|
|
198
|
+
/// Print help, prefixed with the right header and (for top-level only) the
|
|
199
|
+
/// animated banner. Help is written via clap's own stdout path for full colors.
|
|
200
|
+
pub fn print_banner_with_help(argv: &[String]) {
|
|
201
|
+
let sub_name = sub_name_from_argv(argv);
|
|
202
|
+
let sub = sub_name.as_deref();
|
|
203
|
+
|
|
204
|
+
// ── Subcommand help: compact static header, no animation ─────────────
|
|
205
|
+
if sub.is_some() {
|
|
206
|
+
print_small_header();
|
|
207
|
+
let mut cmd = build_command();
|
|
208
|
+
cmd.build();
|
|
209
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── Top-level help ────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
if !io::stdout().is_terminal() {
|
|
216
|
+
let mut out = io::stdout().lock();
|
|
217
|
+
print_tish_banner_plain(&mut out);
|
|
218
|
+
drop(out);
|
|
219
|
+
let mut cmd = build_command().color(clap::ColorChoice::Never);
|
|
220
|
+
cmd.build();
|
|
221
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Line-count pass (ANSI codes never add \n, so Never == Always count).
|
|
226
|
+
let h: usize = {
|
|
227
|
+
let mut cmd = build_command().color(clap::ColorChoice::Never);
|
|
228
|
+
cmd.build();
|
|
229
|
+
count_help_lines(&mut cmd, sub)
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
let n = TISH_BANNER_LINES.len();
|
|
233
|
+
|
|
234
|
+
// 1. First banner frame + manual prefix + full help – all visible immediately.
|
|
235
|
+
{
|
|
236
|
+
let mut out = io::stdout().lock();
|
|
237
|
+
write_tish_banner_frame(&mut out, 1.0, 0);
|
|
238
|
+
let _ = writeln!(out); // blank separator (row n+1)
|
|
239
|
+
// ── Manual prefix (MAIN_PREFIX_LINES = 4 lines) ──────────────────
|
|
240
|
+
let _ = writeln!(out, "{H_PURPLE}Tish{H_RESET} {H_GREY}(version {VERSION}){H_RESET}");
|
|
241
|
+
let _ = writeln!(out, "Minimal TS/JS-ish language");
|
|
242
|
+
let _ = writeln!(out, "{H_PINK}https://tishlang.com{H_RESET}");
|
|
243
|
+
let _ = writeln!(out); // blank before Usage
|
|
244
|
+
let _ = out.flush();
|
|
245
|
+
}
|
|
246
|
+
{
|
|
247
|
+
let mut cmd = build_command();
|
|
248
|
+
cmd.build();
|
|
249
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
250
|
+
let _ = io::stdout().flush();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 2. Jump cursor back to banner top and cycle colors.
|
|
254
|
+
// Total rows above cursor: n + 1 (sep) + MAIN_PREFIX_LINES + h (clap)
|
|
255
|
+
{
|
|
256
|
+
let mut out = io::stdout().lock();
|
|
257
|
+
let _ = write!(out, "\x1b[{}A", n + 1 + MAIN_PREFIX_LINES + h);
|
|
258
|
+
let _ = out.flush();
|
|
259
|
+
|
|
260
|
+
let frames = BANNER_CYCLE_FRAMES;
|
|
261
|
+
for f in 0..frames {
|
|
262
|
+
write_tish_banner_frame(&mut out, 1.0, f);
|
|
263
|
+
if f < frames - 1 {
|
|
264
|
+
let _ = write!(out, "\x1b[{}A", n);
|
|
265
|
+
}
|
|
266
|
+
let _ = out.flush();
|
|
267
|
+
thread::sleep(Duration::from_millis(BANNER_FRAME_MS));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// After last frame cursor is at row n; skip sep + prefix + clap rows.
|
|
271
|
+
let _ = write!(out, "\x1b[{}B", 1 + MAIN_PREFIX_LINES + h);
|
|
272
|
+
let _ = writeln!(out);
|
|
273
|
+
let _ = out.flush();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// Whether argv will cause clap to print help (top-level or subcommand).
|
|
278
|
+
pub fn argv_requests_help(argv: &[String]) -> bool {
|
|
279
|
+
argv.iter().any(|a| a == "--help" || a == "-h")
|
|
280
|
+
|| matches!(argv.get(1).map(String::as_str), Some("help"))
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/// Build a bold true-color `Style` from the brand palette.
|
|
284
|
+
fn rgb_bold(r: u8, g: u8, b: u8) -> Style {
|
|
285
|
+
Style::new().fg_color(Some(Color::Rgb(RgbColor(r, g, b)))) | Effects::BOLD
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// Help colors using the brand palette.
|
|
289
|
+
/// Orange → section headers / usage. Teal → literals (commands, flags). Yellow → placeholders.
|
|
290
|
+
pub fn cargo_help_styles() -> Styles {
|
|
291
|
+
Styles::styled()
|
|
292
|
+
.header(rgb_bold(255, 159, 64)) // Orange – "Commands:", "Options:", "Usage:"
|
|
293
|
+
.usage(rgb_bold(255, 159, 64)) // Orange
|
|
294
|
+
.literal(rgb_bold( 48, 209, 188)) // Teal – run, repl, --help, -V …
|
|
295
|
+
.placeholder(rgb_bold(255, 213, 64)) // Yellow – <FILE>, <NAME>, …
|
|
296
|
+
.error(rgb_bold(255, 55, 148)) // Pink – error messages
|
|
297
|
+
.valid(rgb_bold( 52, 199, 89)) // Green – valid values
|
|
298
|
+
.invalid(rgb_bold(255, 55, 148)) // Pink – invalid values
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// Returns the colored `after_help` text for the top-level `tish --help`.
|
|
302
|
+
/// Colors are emitted only when stdout is a TTY.
|
|
303
|
+
pub fn cli_after_help() -> String {
|
|
304
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
305
|
+
("\x1b[1;38;2;255;159;64m", "\x1b[1;38;2;48;209;188m", "\x1b[0m")
|
|
306
|
+
} else {
|
|
307
|
+
("", "", "")
|
|
308
|
+
};
|
|
309
|
+
format!(
|
|
310
|
+
"\
|
|
311
|
+
{oh}Environment variables:{r}
|
|
312
|
+
{t}TISH_NO_OPTIMIZE=1{r}
|
|
313
|
+
Disable AST and bytecode optimizations for run/build
|
|
314
|
+
|
|
315
|
+
See {t}tish run --help{r} and {t}tish build --help{r} for backend and feature options."
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
fn capabilities_section(oh: &str, t: &str, r: &str) -> String {
|
|
320
|
+
format!(
|
|
321
|
+
"\
|
|
322
|
+
{oh}Backends{r} (--backend):
|
|
323
|
+
{t}vm{r}
|
|
324
|
+
Bytecode VM (default)
|
|
325
|
+
{t}interp{r}
|
|
326
|
+
Tree-walking interpreter
|
|
327
|
+
|
|
328
|
+
{oh}Capabilities{r} (--feature, repeatable; comma-separated values are split):
|
|
329
|
+
{t}http{r}
|
|
330
|
+
Network: fetch, serve, Promise / timers (native async)
|
|
331
|
+
{t}fs{r}
|
|
332
|
+
Filesystem: readFile, writeFile, fileExists, isDir, readDir, mkdir
|
|
333
|
+
{t}process{r}
|
|
334
|
+
process.exit, cwd, exec, argv, env
|
|
335
|
+
{t}regex{r}
|
|
336
|
+
RegExp
|
|
337
|
+
{t}ws{r}
|
|
338
|
+
WebSocket client / server
|
|
339
|
+
{t}full{r}
|
|
340
|
+
All of the above (http, fs, process, regex, ws)
|
|
341
|
+
|
|
342
|
+
Omit --feature to use every capability linked into this binary."
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/// Returns the colored `after_help` for `tish run --help`.
|
|
347
|
+
pub fn run_after_help() -> String {
|
|
348
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
349
|
+
("\x1b[1;38;2;255;159;64m", "\x1b[1;38;2;48;209;188m", "\x1b[0m")
|
|
350
|
+
} else {
|
|
351
|
+
("", "", "")
|
|
352
|
+
};
|
|
353
|
+
capabilities_section(oh, t, r)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// Returns the colored `after_help` for `tish repl --help`.
|
|
357
|
+
pub fn repl_after_help() -> String {
|
|
358
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
359
|
+
("\x1b[1;38;2;255;159;64m", "\x1b[1;38;2;48;209;188m", "\x1b[0m")
|
|
360
|
+
} else {
|
|
361
|
+
("", "", "")
|
|
362
|
+
};
|
|
363
|
+
capabilities_section(oh, t, r)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/// Returns the colored `after_long_help` for `tish build --help`.
|
|
367
|
+
pub fn build_after_help() -> String {
|
|
368
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
369
|
+
("\x1b[1;38;2;255;159;64m", "\x1b[1;38;2;48;209;188m", "\x1b[0m")
|
|
370
|
+
} else {
|
|
371
|
+
("", "", "")
|
|
372
|
+
};
|
|
373
|
+
format!(
|
|
374
|
+
"\
|
|
375
|
+
{oh}Build targets{r} (--target, default: native):
|
|
376
|
+
{t}native{r}
|
|
377
|
+
Native executable (see --native-backend)
|
|
378
|
+
{t}js{r}
|
|
379
|
+
JavaScript bundle
|
|
380
|
+
{t}wasm{r}
|
|
381
|
+
WebAssembly (.tish project; .js source supported on some paths)
|
|
382
|
+
{t}wasi{r}
|
|
383
|
+
WASI WebAssembly
|
|
384
|
+
|
|
385
|
+
{oh}Native backends{r} (--native-backend, only with --target native, default: rust):
|
|
386
|
+
{t}rust{r}
|
|
387
|
+
Emit Rust + link tishlang_runtime via cargo
|
|
388
|
+
{t}cranelift{r}
|
|
389
|
+
Embedded bytecode + Cranelift/VM runtime binary
|
|
390
|
+
{t}llvm{r}
|
|
391
|
+
Embedded bytecode + LLVM/clang link path
|
|
392
|
+
|
|
393
|
+
{oh}Capabilities{r} (--feature, repeatable; comma-separated values are split):
|
|
394
|
+
{t}http{r}
|
|
395
|
+
Network: fetch, serve, Promise / timers (native async)
|
|
396
|
+
{t}fs{r}
|
|
397
|
+
Filesystem: readFile, writeFile, fileExists, isDir, readDir, mkdir
|
|
398
|
+
{t}process{r}
|
|
399
|
+
process.exit, cwd, exec, argv, env
|
|
400
|
+
{t}regex{r}
|
|
401
|
+
RegExp
|
|
402
|
+
{t}ws{r}
|
|
403
|
+
WebSocket client / server
|
|
404
|
+
{t}full{r}
|
|
405
|
+
All of the above (http, fs, process, regex, ws)
|
|
406
|
+
|
|
407
|
+
Omit --feature to use every capability linked into this binary.
|
|
408
|
+
Build `tish` with matching Cargo features (e.g. cargo build -p tishlang --features full)."
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#[derive(Parser)]
|
|
413
|
+
#[command(name = "tish")]
|
|
414
|
+
#[command(version = env!("CARGO_PKG_VERSION"))]
|
|
415
|
+
#[command(styles = cargo_help_styles())]
|
|
416
|
+
pub(crate) struct Cli {
|
|
417
|
+
#[command(subcommand)]
|
|
418
|
+
pub command: Option<Commands>,
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#[derive(Parser)]
|
|
422
|
+
pub(crate) struct RunArgs {
|
|
423
|
+
/// Path to a `.tish` file, or `-` to read the program from stdin (like `node -`).
|
|
424
|
+
#[arg(required = true, allow_hyphen_values = true, value_name = "FILE", help_heading = "Arguments")]
|
|
425
|
+
pub file: String,
|
|
426
|
+
/// `vm` or `interp` (see `tish --help` for capabilities / `--feature`).
|
|
427
|
+
#[arg(long, default_value = "vm", value_name = "NAME", help_heading = "Options")]
|
|
428
|
+
pub backend: String,
|
|
429
|
+
/// Subset of capabilities (see `tish --help` for the full list).
|
|
430
|
+
#[arg(
|
|
431
|
+
long = "feature",
|
|
432
|
+
value_name = "NAME",
|
|
433
|
+
action = clap::ArgAction::Append,
|
|
434
|
+
help_heading = "Options"
|
|
435
|
+
)]
|
|
436
|
+
pub features: Vec<String>,
|
|
437
|
+
/// Disable AST and bytecode optimizations (for debugging).
|
|
438
|
+
#[arg(long, help_heading = "Options")]
|
|
439
|
+
pub no_optimize: bool,
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
#[derive(Parser)]
|
|
443
|
+
pub(crate) struct ReplArgs {
|
|
444
|
+
/// `vm` or `interp` (see `tish --help`).
|
|
445
|
+
#[arg(long, default_value = "vm", value_name = "NAME", help_heading = "Options")]
|
|
446
|
+
pub backend: String,
|
|
447
|
+
/// Subset of capabilities (see `tish --help` for the full list).
|
|
448
|
+
#[arg(
|
|
449
|
+
long = "feature",
|
|
450
|
+
value_name = "NAME",
|
|
451
|
+
action = clap::ArgAction::Append,
|
|
452
|
+
help_heading = "Options"
|
|
453
|
+
)]
|
|
454
|
+
pub features: Vec<String>,
|
|
455
|
+
#[arg(long, help_heading = "Options")]
|
|
456
|
+
pub no_optimize: bool,
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
#[derive(Parser)]
|
|
460
|
+
pub(crate) struct BuildArgs {
|
|
461
|
+
#[arg(
|
|
462
|
+
short,
|
|
463
|
+
long,
|
|
464
|
+
default_value = "tish_out",
|
|
465
|
+
value_name = "PATH",
|
|
466
|
+
help_heading = "Options"
|
|
467
|
+
)]
|
|
468
|
+
pub output: String,
|
|
469
|
+
/// `native`, `js`, `wasm`, or `wasi` (see long help below).
|
|
470
|
+
#[arg(long, default_value = "native", value_name = "TARGET", help_heading = "Options")]
|
|
471
|
+
pub target: String,
|
|
472
|
+
/// `rust`, `cranelift`, or `llvm` (only for `--target native`).
|
|
473
|
+
#[arg(long, default_value = "rust", value_name = "BACKEND", help_heading = "Options")]
|
|
474
|
+
pub native_backend: String,
|
|
475
|
+
/// Capability subset for native output (see long help below).
|
|
476
|
+
#[arg(
|
|
477
|
+
long = "feature",
|
|
478
|
+
value_name = "NAME",
|
|
479
|
+
action = clap::ArgAction::Append,
|
|
480
|
+
help_heading = "Options"
|
|
481
|
+
)]
|
|
482
|
+
pub features: Vec<String>,
|
|
483
|
+
#[arg(long, help_heading = "Options")]
|
|
484
|
+
pub no_optimize: bool,
|
|
485
|
+
/// Entry `.tish` file (or `.js` where supported).
|
|
486
|
+
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
487
|
+
pub file: String,
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
#[derive(Subcommand)]
|
|
491
|
+
pub(crate) enum Commands {
|
|
492
|
+
/// Run a Tish file (interpret)
|
|
493
|
+
Run(RunArgs),
|
|
494
|
+
/// Interactive REPL
|
|
495
|
+
Repl(ReplArgs),
|
|
496
|
+
/// Build native binary, wasm, wasi, or JavaScript output
|
|
497
|
+
Build(BuildArgs),
|
|
498
|
+
/// Parse and dump AST
|
|
499
|
+
#[command(name = "dump-ast")]
|
|
500
|
+
DumpAst {
|
|
501
|
+
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
502
|
+
file: String,
|
|
503
|
+
},
|
|
504
|
+
}
|