@tishlang/tish 1.6.0 → 1.8.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 +2 -0
- package/README.md +2 -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 +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- 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 +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -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 +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/justfile +8 -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/crates/tish/src/main.rs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
//! Tish CLI - run, REPL, build to native or other targets.
|
|
2
2
|
|
|
3
|
+
mod cargo_native_registry;
|
|
3
4
|
mod cli_help;
|
|
4
5
|
mod repl_completion;
|
|
5
6
|
|
|
6
7
|
use std::cell::RefCell;
|
|
8
|
+
use tishlang_core::VmRef;
|
|
7
9
|
use std::collections::HashSet;
|
|
8
10
|
use std::fs;
|
|
9
11
|
use std::io::{self, IsTerminal, Read, Write};
|
|
@@ -15,13 +17,13 @@ use rustyline::{Behavior, ColorMode, CompletionType, Config, Editor};
|
|
|
15
17
|
|
|
16
18
|
use cli_help::{Cli, Commands};
|
|
17
19
|
|
|
18
|
-
/// Normalize `--feature` / `--feature http,fs` / `--feature full` for VM runs and native builds.
|
|
20
|
+
/// Normalize `--feature` / `--feature http,timers,fs` / `--feature full` for VM runs and native builds.
|
|
19
21
|
fn normalize_capability_flags(features: &[String]) -> HashSet<String> {
|
|
20
22
|
let mut out = HashSet::new();
|
|
21
23
|
for s in features {
|
|
22
24
|
for part in s.split(',').map(str::trim).filter(|p| !p.is_empty()) {
|
|
23
25
|
if part == "full" {
|
|
24
|
-
for name in ["http", "fs", "process", "regex", "ws"] {
|
|
26
|
+
for name in ["http", "timers", "fs", "process", "regex", "ws"] {
|
|
25
27
|
out.insert(name.to_string());
|
|
26
28
|
}
|
|
27
29
|
} else {
|
|
@@ -48,7 +50,9 @@ fn vm_capabilities_for_cli_run(cli_features: &[String]) -> HashSet<String> {
|
|
|
48
50
|
/// `--feature` list for `tish build --target native`: same default as `tish run` (all linked-in caps).
|
|
49
51
|
fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
|
|
50
52
|
if cli_features.is_empty() {
|
|
51
|
-
let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
|
|
53
|
+
let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
|
|
54
|
+
.into_iter()
|
|
55
|
+
.collect();
|
|
52
56
|
v.sort();
|
|
53
57
|
v
|
|
54
58
|
} else {
|
|
@@ -61,8 +65,7 @@ fn argv_with_implicit_run(mut argv: Vec<String>) -> Vec<String> {
|
|
|
61
65
|
if argv.len() >= 2 {
|
|
62
66
|
let first = argv[1].as_str();
|
|
63
67
|
const SUBCOMMANDS: &[&str] = &["run", "repl", "build", "dump-ast"];
|
|
64
|
-
let looks_like_file =
|
|
65
|
-
!first.starts_with('-') && !SUBCOMMANDS.iter().any(|&s| s == first);
|
|
68
|
+
let looks_like_file = !first.starts_with('-') && !SUBCOMMANDS.iter().any(|&s| s == first);
|
|
66
69
|
if looks_like_file {
|
|
67
70
|
argv.insert(1, "run".to_string());
|
|
68
71
|
}
|
|
@@ -95,7 +98,12 @@ fn main() {
|
|
|
95
98
|
let matches = cli_help::build_command().get_matches_from(&argv);
|
|
96
99
|
let cli = Cli::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
|
|
97
100
|
let result = match cli.command {
|
|
98
|
-
Some(Commands::Run(a)) => run_file(
|
|
101
|
+
Some(Commands::Run(a)) => run_file(
|
|
102
|
+
&a.file,
|
|
103
|
+
&a.backend,
|
|
104
|
+
&a.features,
|
|
105
|
+
a.no_optimize || no_opt_env,
|
|
106
|
+
),
|
|
99
107
|
Some(Commands::Repl(a)) => run_repl(&a.backend, a.no_optimize || no_opt_env, &a.features),
|
|
100
108
|
Some(Commands::Build(a)) => build_file(
|
|
101
109
|
&a.file,
|
|
@@ -104,6 +112,7 @@ fn main() {
|
|
|
104
112
|
&a.native_backend,
|
|
105
113
|
&a.features,
|
|
106
114
|
a.no_optimize || no_opt_env,
|
|
115
|
+
a.source_map,
|
|
107
116
|
),
|
|
108
117
|
Some(Commands::DumpAst { file }) => dump_ast(&file),
|
|
109
118
|
None => {
|
|
@@ -136,7 +145,8 @@ fn run_stdin_pipe(
|
|
|
136
145
|
if source.trim().is_empty() {
|
|
137
146
|
if fail_on_empty {
|
|
138
147
|
return Err(
|
|
139
|
-
"No source on stdin. Example: echo 'console.log(1)' | tish or tish run -"
|
|
148
|
+
"No source on stdin. Example: echo 'console.log(1)' | tish or tish run -"
|
|
149
|
+
.into(),
|
|
140
150
|
);
|
|
141
151
|
}
|
|
142
152
|
return Ok(());
|
|
@@ -153,7 +163,7 @@ fn run_stdin_source(
|
|
|
153
163
|
let cwd = std::env::current_dir().map_err(|e| e.to_string())?;
|
|
154
164
|
let modules = tishlang_compile::resolve_project_from_stdin(source, &cwd)?;
|
|
155
165
|
tishlang_compile::detect_cycles(&modules)?;
|
|
156
|
-
let prog = tishlang_compile::merge_modules(modules)
|
|
166
|
+
let prog = tishlang_compile::merge_modules(modules)?.program;
|
|
157
167
|
let program = if no_optimize {
|
|
158
168
|
prog
|
|
159
169
|
} else {
|
|
@@ -162,12 +172,18 @@ fn run_stdin_source(
|
|
|
162
172
|
run_program(&program, backend, no_optimize, features)
|
|
163
173
|
}
|
|
164
174
|
|
|
165
|
-
fn run_file(
|
|
175
|
+
fn run_file(
|
|
176
|
+
path: &str,
|
|
177
|
+
backend: &str,
|
|
178
|
+
features: &[String],
|
|
179
|
+
no_optimize: bool,
|
|
180
|
+
) -> Result<(), String> {
|
|
166
181
|
let program = if path == "-" {
|
|
167
182
|
return run_stdin_pipe(backend, features, no_optimize, true);
|
|
168
183
|
} else {
|
|
169
|
-
let path =
|
|
170
|
-
|
|
184
|
+
let path = Path::new(path)
|
|
185
|
+
.canonicalize()
|
|
186
|
+
.map_err(|e| format!("Cannot resolve {}: {}", path, e))?;
|
|
171
187
|
let project_root = path.parent().and_then(|p| {
|
|
172
188
|
if p.file_name().and_then(|n| n.to_str()) == Some("src") {
|
|
173
189
|
p.parent()
|
|
@@ -177,8 +193,10 @@ fn run_file(path: &str, backend: &str, features: &[String], no_optimize: bool) -
|
|
|
177
193
|
});
|
|
178
194
|
|
|
179
195
|
if path.extension().map(|e| e == "js") == Some(true) {
|
|
180
|
-
let prog = tishlang_js_to_tish::convert(
|
|
181
|
-
.map_err(|e| format!("{}", e))
|
|
196
|
+
let prog = tishlang_js_to_tish::convert(
|
|
197
|
+
&fs::read_to_string(&path).map_err(|e| format!("{}", e))?,
|
|
198
|
+
)
|
|
199
|
+
.map_err(|e| format!("{}", e))?;
|
|
182
200
|
if no_optimize {
|
|
183
201
|
prog
|
|
184
202
|
} else {
|
|
@@ -187,7 +205,7 @@ fn run_file(path: &str, backend: &str, features: &[String], no_optimize: bool) -
|
|
|
187
205
|
} else {
|
|
188
206
|
let modules = tishlang_compile::resolve_project(&path, project_root)?;
|
|
189
207
|
tishlang_compile::detect_cycles(&modules)?;
|
|
190
|
-
let prog = tishlang_compile::merge_modules(modules)
|
|
208
|
+
let prog = tishlang_compile::merge_modules(modules)?.program;
|
|
191
209
|
if no_optimize {
|
|
192
210
|
prog
|
|
193
211
|
} else {
|
|
@@ -208,8 +226,18 @@ fn run_program(
|
|
|
208
226
|
if backend == "interp" {
|
|
209
227
|
let mut eval = tishlang_eval::Evaluator::new();
|
|
210
228
|
let value = eval.eval_program(program)?;
|
|
229
|
+
#[cfg(feature = "timers")]
|
|
230
|
+
{
|
|
231
|
+
let _ = eval.run_timer_phase();
|
|
232
|
+
}
|
|
211
233
|
if !matches!(value, tishlang_eval::Value::Null) {
|
|
212
|
-
println!(
|
|
234
|
+
println!(
|
|
235
|
+
"{}",
|
|
236
|
+
tishlang_eval::format_value_for_console(
|
|
237
|
+
&value,
|
|
238
|
+
tishlang_core::use_console_colors()
|
|
239
|
+
)
|
|
240
|
+
);
|
|
213
241
|
}
|
|
214
242
|
return Ok(());
|
|
215
243
|
}
|
|
@@ -220,15 +248,14 @@ fn run_program(
|
|
|
220
248
|
tishlang_bytecode::compile(program).map_err(|e| e.to_string())?
|
|
221
249
|
};
|
|
222
250
|
let caps = vm_capabilities_for_cli_run(features);
|
|
223
|
-
let
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
repl_mode: false,
|
|
227
|
-
capabilities: caps,
|
|
228
|
-
},
|
|
229
|
-
)?;
|
|
251
|
+
let mut vm = tishlang_vm::Vm::with_capabilities(caps);
|
|
252
|
+
cargo_native_registry::register_bytecode_native_modules(&mut vm);
|
|
253
|
+
let value = vm.run_with_options(&chunk, false)?;
|
|
230
254
|
if !matches!(value, tishlang_core::Value::Null) {
|
|
231
|
-
println!(
|
|
255
|
+
println!(
|
|
256
|
+
"{}",
|
|
257
|
+
tishlang_core::format_value_styled(&value, tishlang_core::use_console_colors())
|
|
258
|
+
);
|
|
232
259
|
}
|
|
233
260
|
Ok(())
|
|
234
261
|
}
|
|
@@ -246,7 +273,11 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
|
|
|
246
273
|
print!("{}", prompt);
|
|
247
274
|
io::stdout().flush().map_err(|e| e.to_string())?;
|
|
248
275
|
buffer.clear();
|
|
249
|
-
if io::stdin()
|
|
276
|
+
if io::stdin()
|
|
277
|
+
.read_line(&mut buffer)
|
|
278
|
+
.map_err(|e| e.to_string())?
|
|
279
|
+
== 0
|
|
280
|
+
{
|
|
250
281
|
if !multiline.is_empty() {
|
|
251
282
|
let _ = tishlang_parser::parse(multiline.trim());
|
|
252
283
|
}
|
|
@@ -266,8 +297,18 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
|
|
|
266
297
|
Ok(program) => {
|
|
267
298
|
match eval.eval_program(&program) {
|
|
268
299
|
Ok(v) => {
|
|
300
|
+
#[cfg(feature = "timers")]
|
|
301
|
+
{
|
|
302
|
+
let _ = eval.run_timer_phase();
|
|
303
|
+
}
|
|
269
304
|
if !matches!(v, tishlang_eval::Value::Null) {
|
|
270
|
-
println!(
|
|
305
|
+
println!(
|
|
306
|
+
"{}",
|
|
307
|
+
tishlang_eval::format_value_for_console(
|
|
308
|
+
&v,
|
|
309
|
+
tishlang_core::use_console_colors()
|
|
310
|
+
)
|
|
311
|
+
);
|
|
271
312
|
}
|
|
272
313
|
}
|
|
273
314
|
Err(e) => eprintln!("{}", e),
|
|
@@ -291,11 +332,11 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
|
|
|
291
332
|
if !std::io::stdin().is_terminal() {
|
|
292
333
|
eprintln!("Note: Tab completion and grey preview require an interactive terminal (TTY).");
|
|
293
334
|
}
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
)
|
|
335
|
+
let mut vm0 = tishlang_vm::Vm::with_capabilities(vm_capabilities_for_cli_run(features));
|
|
336
|
+
cargo_native_registry::register_bytecode_native_modules(&mut vm0);
|
|
337
|
+
let vm = VmRef::new(vm0);
|
|
297
338
|
let completer = repl_completion::ReplCompleter {
|
|
298
|
-
vm:
|
|
339
|
+
vm: vm.clone(),
|
|
299
340
|
no_optimize,
|
|
300
341
|
};
|
|
301
342
|
let config = Config::builder()
|
|
@@ -364,16 +405,20 @@ fn run_repl(backend: &str, no_optimize: bool, features: &[String]) -> Result<(),
|
|
|
364
405
|
tishlang_bytecode::compile_for_repl
|
|
365
406
|
};
|
|
366
407
|
match compile_fn(&program) {
|
|
367
|
-
Ok(chunk) => {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
408
|
+
Ok(chunk) => match vm.borrow_mut().run_with_options(&chunk, true) {
|
|
409
|
+
Ok(v) => {
|
|
410
|
+
if !matches!(v, tishlang_core::Value::Null) {
|
|
411
|
+
println!(
|
|
412
|
+
"{}",
|
|
413
|
+
tishlang_core::format_value_styled(
|
|
414
|
+
&v,
|
|
415
|
+
tishlang_core::use_console_colors()
|
|
416
|
+
)
|
|
417
|
+
);
|
|
373
418
|
}
|
|
374
|
-
Err(e) => eprintln!("{}", e),
|
|
375
419
|
}
|
|
376
|
-
|
|
420
|
+
Err(e) => eprintln!("{}", e),
|
|
421
|
+
},
|
|
377
422
|
Err(e) => eprintln!("Compile error: {}", e),
|
|
378
423
|
}
|
|
379
424
|
let _ = rl.add_history_entry(buffer.trim());
|
|
@@ -413,12 +458,31 @@ fn repl_prompt(primary: bool) -> String {
|
|
|
413
458
|
|
|
414
459
|
/// Path to REPL history file (Python-style: ~/.tish_history).
|
|
415
460
|
fn tish_history_path() -> Option<PathBuf> {
|
|
416
|
-
let home = std::env::var_os("HOME")
|
|
417
|
-
.or_else(|| std::env::var_os("USERPROFILE"));
|
|
461
|
+
let home = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE"));
|
|
418
462
|
home.map(|h| PathBuf::from(h).join(".tish_history"))
|
|
419
463
|
}
|
|
420
464
|
|
|
421
|
-
fn compile_to_js(
|
|
465
|
+
fn compile_to_js(
|
|
466
|
+
input_path: &Path,
|
|
467
|
+
output_path: &str,
|
|
468
|
+
optimize: bool,
|
|
469
|
+
source_map: bool,
|
|
470
|
+
) -> Result<(), String> {
|
|
471
|
+
if source_map && optimize {
|
|
472
|
+
return Err(
|
|
473
|
+
"tish build --target js --source-map requires --no-optimize (mappings follow unmerged statement order)."
|
|
474
|
+
.into(),
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if source_map
|
|
478
|
+
&& (input_path.extension().map(|e| e == "jsx") == Some(true)
|
|
479
|
+
|| input_path.extension().map(|e| e == "js") == Some(true))
|
|
480
|
+
{
|
|
481
|
+
return Err(
|
|
482
|
+
"tish build --target js --source-map is only supported for .tish project builds (not single-file .jsx / .js inputs)."
|
|
483
|
+
.into(),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
422
486
|
let project_root = input_path.parent().and_then(|p| {
|
|
423
487
|
if p.file_name().and_then(|n| n.to_str()) == Some("src") {
|
|
424
488
|
p.parent()
|
|
@@ -426,43 +490,70 @@ fn compile_to_js(input_path: &Path, output_path: &str, optimize: bool) -> Result
|
|
|
426
490
|
Some(p)
|
|
427
491
|
}
|
|
428
492
|
});
|
|
429
|
-
let
|
|
493
|
+
let out_path = Path::new(output_path);
|
|
494
|
+
let out_path = if out_path.extension().is_none()
|
|
495
|
+
|| out_path.extension() == Some(std::ffi::OsStr::new(""))
|
|
496
|
+
{
|
|
497
|
+
out_path.with_extension("js")
|
|
498
|
+
} else {
|
|
499
|
+
out_path.to_path_buf()
|
|
500
|
+
};
|
|
501
|
+
let out_js_name = out_path
|
|
502
|
+
.file_name()
|
|
503
|
+
.and_then(|s| s.to_str())
|
|
504
|
+
.unwrap_or("out.js");
|
|
505
|
+
|
|
506
|
+
let (js, map_json) = if input_path.extension().map(|e| e == "jsx") == Some(true) {
|
|
430
507
|
let source = fs::read_to_string(input_path).map_err(|e| format!("{}", e))?;
|
|
431
508
|
let wrapped = format!(
|
|
432
509
|
"export fn __TishJsxRoot() {{\n return (\n{}\n )\n}}",
|
|
433
510
|
source.trim()
|
|
434
511
|
);
|
|
435
|
-
let program =
|
|
436
|
-
.map_err(|e| format!("JSX wrapper parse: {}", e))?;
|
|
512
|
+
let program =
|
|
513
|
+
tishlang_parser::parse(&wrapped).map_err(|e| format!("JSX wrapper parse: {}", e))?;
|
|
437
514
|
let p = if optimize {
|
|
438
515
|
tishlang_opt::optimize(&program)
|
|
439
516
|
} else {
|
|
440
517
|
program
|
|
441
518
|
};
|
|
442
|
-
tishlang_compile_js::compile_with_jsx(&p, optimize).map_err(|e| format!("{}", e))
|
|
519
|
+
let js = tishlang_compile_js::compile_with_jsx(&p, optimize).map_err(|e| format!("{}", e))?;
|
|
520
|
+
(js, None)
|
|
443
521
|
} else if input_path.extension().map(|e| e == "js") == Some(true) {
|
|
444
522
|
let source = fs::read_to_string(input_path).map_err(|e| format!("{}", e))?;
|
|
445
523
|
let program = tishlang_js_to_tish::convert(&source).map_err(|e| format!("{}", e))?;
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
524
|
+
let js =
|
|
525
|
+
tishlang_compile_js::compile_with_jsx(&program, optimize).map_err(|e| format!("{}", e))?;
|
|
526
|
+
(js, None)
|
|
527
|
+
} else if source_map {
|
|
528
|
+
let bundle = tishlang_compile_js::compile_project_with_jsx_and_source_map(
|
|
529
|
+
input_path,
|
|
530
|
+
project_root,
|
|
531
|
+
out_js_name,
|
|
532
|
+
)
|
|
533
|
+
.map_err(|e| format!("{}", e))?;
|
|
534
|
+
(bundle.js, bundle.source_map_json)
|
|
457
535
|
} else {
|
|
458
|
-
|
|
536
|
+
let js = tishlang_compile_js::compile_project_with_jsx(input_path, project_root, optimize)
|
|
537
|
+
.map_err(|e| format!("{}", e))?;
|
|
538
|
+
(js, None)
|
|
459
539
|
};
|
|
460
540
|
|
|
461
541
|
if let Some(parent) = out_path.parent() {
|
|
462
542
|
fs::create_dir_all(parent)
|
|
463
543
|
.map_err(|e| format!("Cannot create output directory {}: {}", parent.display(), e))?;
|
|
464
544
|
}
|
|
465
|
-
|
|
545
|
+
let mut js_out = js;
|
|
546
|
+
if let Some(map) = &map_json {
|
|
547
|
+
let map_path = out_path.with_extension("js.map");
|
|
548
|
+
fs::write(&map_path, map).map_err(|e| format!("Cannot write {}: {}", map_path.display(), e))?;
|
|
549
|
+
let map_url = map_path
|
|
550
|
+
.file_name()
|
|
551
|
+
.and_then(|s| s.to_str())
|
|
552
|
+
.unwrap_or("out.js.map");
|
|
553
|
+
js_out.push_str(&format!("\n//# sourceMappingURL={map_url}\n"));
|
|
554
|
+
println!("Built: {}", map_path.display());
|
|
555
|
+
}
|
|
556
|
+
fs::write(&out_path, js_out).map_err(|e| format!("Cannot write {}: {}", out_path.display(), e))?;
|
|
466
557
|
println!("Built: {}", out_path.display());
|
|
467
558
|
Ok(())
|
|
468
559
|
}
|
|
@@ -475,15 +566,17 @@ fn build_file(
|
|
|
475
566
|
native_backend: &str,
|
|
476
567
|
cli_features: &[String],
|
|
477
568
|
no_optimize: bool,
|
|
569
|
+
source_map: bool,
|
|
478
570
|
) -> Result<(), String> {
|
|
479
571
|
let optimize = !no_optimize;
|
|
480
|
-
let input_path =
|
|
481
|
-
|
|
572
|
+
let input_path = Path::new(input_path)
|
|
573
|
+
.canonicalize()
|
|
574
|
+
.map_err(|e| format!("Cannot resolve {}: {}", input_path, e))?;
|
|
482
575
|
|
|
483
576
|
let is_js = input_path.extension().map(|e| e == "js") == Some(true);
|
|
484
577
|
|
|
485
578
|
if target == "js" {
|
|
486
|
-
return compile_to_js(&input_path, output_path, optimize);
|
|
579
|
+
return compile_to_js(&input_path, output_path, optimize, source_map);
|
|
487
580
|
}
|
|
488
581
|
|
|
489
582
|
if target == "wasm" && is_js {
|
|
@@ -501,8 +594,13 @@ fn build_file(
|
|
|
501
594
|
Some(p)
|
|
502
595
|
}
|
|
503
596
|
});
|
|
504
|
-
return tishlang_wasm::compile_to_wasm(
|
|
505
|
-
|
|
597
|
+
return tishlang_wasm::compile_to_wasm(
|
|
598
|
+
&input_path,
|
|
599
|
+
project_root,
|
|
600
|
+
Path::new(output_path),
|
|
601
|
+
optimize,
|
|
602
|
+
)
|
|
603
|
+
.map_err(|e| e.to_string());
|
|
506
604
|
}
|
|
507
605
|
|
|
508
606
|
if target == "wasi" {
|
|
@@ -513,8 +611,13 @@ fn build_file(
|
|
|
513
611
|
Some(p)
|
|
514
612
|
}
|
|
515
613
|
});
|
|
516
|
-
return tishlang_wasm::compile_to_wasi(
|
|
517
|
-
|
|
614
|
+
return tishlang_wasm::compile_to_wasi(
|
|
615
|
+
&input_path,
|
|
616
|
+
project_root,
|
|
617
|
+
Path::new(output_path),
|
|
618
|
+
optimize,
|
|
619
|
+
)
|
|
620
|
+
.map_err(|e| e.to_string());
|
|
518
621
|
}
|
|
519
622
|
|
|
520
623
|
if target != "native" {
|
|
@@ -570,8 +673,6 @@ fn build_file(
|
|
|
570
673
|
Ok(())
|
|
571
674
|
}
|
|
572
675
|
|
|
573
|
-
|
|
574
|
-
|
|
575
676
|
#[cfg(test)]
|
|
576
677
|
mod cli_tests {
|
|
577
678
|
use clap::Parser;
|
|
@@ -582,10 +683,7 @@ mod cli_tests {
|
|
|
582
683
|
|
|
583
684
|
#[test]
|
|
584
685
|
fn implicit_run_inserts_run_before_file() {
|
|
585
|
-
let argv = argv_with_implicit_run(vec![
|
|
586
|
-
"tish".to_string(),
|
|
587
|
-
"hello.tish".to_string(),
|
|
588
|
-
]);
|
|
686
|
+
let argv = argv_with_implicit_run(vec!["tish".to_string(), "hello.tish".to_string()]);
|
|
589
687
|
let cli = Cli::try_parse_from(argv).unwrap();
|
|
590
688
|
match cli.command {
|
|
591
689
|
Some(Commands::Run(a)) => assert_eq!(a.file, "hello.tish"),
|
|
@@ -595,26 +693,15 @@ mod cli_tests {
|
|
|
595
693
|
|
|
596
694
|
#[test]
|
|
597
695
|
fn explicit_subcommand_not_treated_as_file() {
|
|
598
|
-
let argv = argv_with_implicit_run(vec![
|
|
599
|
-
"tish".to_string(),
|
|
600
|
-
"repl".to_string(),
|
|
601
|
-
]);
|
|
696
|
+
let argv = argv_with_implicit_run(vec!["tish".to_string(), "repl".to_string()]);
|
|
602
697
|
let cli = Cli::try_parse_from(argv).unwrap();
|
|
603
698
|
assert!(matches!(cli.command, Some(Commands::Repl(_))));
|
|
604
699
|
}
|
|
605
700
|
|
|
606
701
|
#[test]
|
|
607
702
|
fn build_js_target_parses() {
|
|
608
|
-
let cli = Cli::try_parse_from([
|
|
609
|
-
|
|
610
|
-
"build",
|
|
611
|
-
"m.tish",
|
|
612
|
-
"--target",
|
|
613
|
-
"js",
|
|
614
|
-
"-o",
|
|
615
|
-
"x.js",
|
|
616
|
-
])
|
|
617
|
-
.unwrap();
|
|
703
|
+
let cli = Cli::try_parse_from(["tish", "build", "m.tish", "--target", "js", "-o", "x.js"])
|
|
704
|
+
.unwrap();
|
|
618
705
|
match cli.command {
|
|
619
706
|
Some(Commands::Build(a)) => assert_eq!(a.file, "m.tish"),
|
|
620
707
|
_ => panic!("expected Build"),
|
|
@@ -632,8 +719,7 @@ mod cli_tests {
|
|
|
632
719
|
}
|
|
633
720
|
|
|
634
721
|
fn dump_ast(path: &str) -> Result<(), String> {
|
|
635
|
-
let source =
|
|
636
|
-
fs::read_to_string(path).map_err(|e| format!("Cannot read {}: {}", path, e))?;
|
|
722
|
+
let source = fs::read_to_string(path).map_err(|e| format!("Cannot read {}: {}", path, e))?;
|
|
637
723
|
let program = tishlang_parser::parse(&source)?;
|
|
638
724
|
println!("{:#?}", program);
|
|
639
725
|
Ok(())
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//! Grey preview hint below the line (like Node) and Tab for full list.
|
|
3
3
|
|
|
4
4
|
use std::borrow::Cow;
|
|
5
|
-
|
|
6
|
-
use
|
|
5
|
+
|
|
6
|
+
use tishlang_core::VmRef;
|
|
7
7
|
|
|
8
8
|
use rustyline::completion::{Completer, Pair};
|
|
9
9
|
use rustyline::highlight::Highlighter;
|
|
@@ -29,7 +29,7 @@ const ANSI_RESET: &str = "\x1b[0m";
|
|
|
29
29
|
|
|
30
30
|
/// Tab completer that evaluates the expression before the last `.` and suggests property/method names.
|
|
31
31
|
pub struct ReplCompleter {
|
|
32
|
-
pub vm:
|
|
32
|
+
pub vm: VmRef<Vm>,
|
|
33
33
|
pub no_optimize: bool,
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -8,7 +8,9 @@ use tishlang_compile::{compile_project_full, merge_modules, resolve_project};
|
|
|
8
8
|
|
|
9
9
|
fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
|
|
10
10
|
if cli_features.is_empty() {
|
|
11
|
-
let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
|
|
11
|
+
let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
|
|
12
|
+
.into_iter()
|
|
13
|
+
.collect();
|
|
12
14
|
v.sort();
|
|
13
15
|
v
|
|
14
16
|
} else {
|
|
@@ -37,7 +39,7 @@ fn resolve_and_merge_cargo_example_fixture() {
|
|
|
37
39
|
panic!("expected import, got {:?}", first);
|
|
38
40
|
};
|
|
39
41
|
assert_eq!(from.as_ref(), "cargo:demo_shim");
|
|
40
|
-
merge_modules(modules).unwrap();
|
|
42
|
+
let _ = merge_modules(modules).unwrap();
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
#[test]
|