@tishlang/tish 1.0.12 → 1.0.14
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/crates/js_to_tish/Cargo.toml +4 -2
- package/crates/js_to_tish/src/span_util.rs +1 -1
- package/crates/js_to_tish/src/transform/expr.rs +15 -15
- package/crates/js_to_tish/src/transform/stmt.rs +7 -7
- package/crates/js_to_tish/src/transform.rs +1 -1
- package/crates/tish/Cargo.toml +22 -22
- package/crates/tish/src/main.rs +47 -46
- package/crates/tish/src/repl_completion.rs +4 -4
- package/crates/tish/tests/integration_test.rs +47 -27
- package/crates/tish_ast/Cargo.toml +3 -1
- package/crates/tish_ast/src/ast.rs +1 -1
- package/crates/tish_build_utils/Cargo.toml +3 -1
- package/crates/tish_build_utils/src/lib.rs +18 -6
- package/crates/tish_builtins/Cargo.toml +5 -3
- package/crates/tish_builtins/src/array.rs +2 -2
- package/crates/tish_builtins/src/globals.rs +3 -3
- package/crates/tish_builtins/src/helpers.rs +1 -1
- package/crates/tish_builtins/src/lib.rs +3 -3
- package/crates/tish_builtins/src/math.rs +1 -1
- package/crates/tish_builtins/src/object.rs +2 -2
- package/crates/tish_builtins/src/string.rs +1 -1
- package/crates/tish_bytecode/Cargo.toml +9 -7
- package/crates/tish_bytecode/src/chunk.rs +1 -1
- package/crates/tish_bytecode/src/compiler.rs +1 -1
- package/crates/tish_bytecode/src/encoding.rs +6 -6
- package/crates/tish_bytecode/tests/constant_folding.rs +7 -7
- package/crates/tish_bytecode/tests/shortcircuit.rs +11 -11
- package/crates/tish_bytecode/tests/sort_optimization.rs +3 -3
- package/crates/tish_compile/Cargo.toml +6 -4
- package/crates/tish_compile/src/codegen.rs +96 -96
- package/crates/tish_compile/src/lib.rs +2 -2
- package/crates/tish_compile/src/resolve.rs +7 -7
- package/crates/tish_compile/src/types.rs +1 -1
- package/crates/tish_compile_js/Cargo.toml +8 -6
- package/crates/tish_compile_js/src/codegen.rs +12 -12
- package/crates/tish_compile_js/src/js_intrinsics.rs +1 -1
- package/crates/tish_compile_js/src/lib.rs +2 -2
- package/crates/tish_compile_js/src/tests_jsx.rs +2 -2
- package/crates/tish_compiler_wasm/Cargo.toml +8 -6
- package/crates/tish_compiler_wasm/src/lib.rs +12 -12
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +3 -3
- package/crates/tish_core/Cargo.toml +3 -1
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/macros.rs +1 -1
- package/crates/tish_core/src/value.rs +1 -1
- package/crates/tish_cranelift/Cargo.toml +6 -4
- package/crates/tish_cranelift/src/lib.rs +2 -2
- package/crates/tish_cranelift/src/link.rs +11 -11
- package/crates/tish_cranelift/src/lower.rs +3 -3
- package/crates/tish_cranelift_runtime/Cargo.toml +9 -7
- package/crates/tish_cranelift_runtime/src/lib.rs +2 -2
- package/crates/tish_eval/Cargo.toml +10 -8
- package/crates/tish_eval/src/eval.rs +35 -35
- package/crates/tish_eval/src/http.rs +1 -1
- package/crates/tish_eval/src/lib.rs +3 -3
- package/crates/tish_eval/src/natives.rs +2 -2
- package/crates/tish_eval/src/regex.rs +2 -2
- package/crates/tish_eval/src/value.rs +7 -7
- package/crates/tish_eval/src/value_convert.rs +3 -3
- package/crates/tish_fmt/Cargo.toml +3 -3
- package/crates/tish_fmt/src/bin/tish-fmt.rs +1 -1
- package/crates/tish_fmt/src/lib.rs +3 -3
- package/crates/tish_jsx_web/Cargo.toml +3 -1
- package/crates/tish_lexer/Cargo.toml +3 -1
- package/crates/tish_lint/Cargo.toml +3 -3
- package/crates/tish_lint/src/bin/tish-lint.rs +3 -3
- package/crates/tish_lint/src/lib.rs +15 -15
- package/crates/tish_llvm/Cargo.toml +7 -5
- package/crates/tish_llvm/src/lib.rs +10 -10
- package/crates/tish_lsp/Cargo.toml +5 -5
- package/crates/tish_lsp/src/main.rs +35 -35
- package/crates/tish_native/Cargo.toml +10 -8
- package/crates/tish_native/src/build.rs +8 -8
- package/crates/tish_native/src/lib.rs +39 -39
- package/crates/tish_opt/Cargo.toml +5 -3
- package/crates/tish_opt/src/lib.rs +27 -27
- package/crates/tish_parser/Cargo.toml +5 -3
- package/crates/tish_parser/src/lib.rs +4 -4
- package/crates/tish_parser/src/parser.rs +2 -2
- package/crates/tish_runtime/Cargo.toml +7 -5
- package/crates/tish_runtime/src/http.rs +1 -1
- package/crates/tish_runtime/src/http_fetch.rs +2 -2
- package/crates/tish_runtime/src/lib.rs +21 -21
- package/crates/tish_runtime/src/native_promise.rs +1 -1
- package/crates/tish_runtime/src/promise.rs +2 -2
- package/crates/tish_runtime/src/promise_io.rs +1 -1
- package/crates/tish_runtime/src/timers.rs +1 -1
- package/crates/tish_runtime/src/ws.rs +1 -1
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
- package/crates/tish_vm/Cargo.toml +14 -12
- package/crates/tish_vm/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +42 -42
- package/crates/tish_wasm/Cargo.toml +8 -6
- package/crates/tish_wasm/src/lib.rs +17 -17
- package/crates/tish_wasm_runtime/Cargo.toml +9 -7
- package/crates/tish_wasm_runtime/src/lib.rs +4 -4
- package/justfile +4 -4
- 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
|
@@ -163,13 +163,33 @@ fn tish_bin() -> PathBuf {
|
|
|
163
163
|
default
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
/// tish -V and --version print the version.
|
|
167
|
+
#[test]
|
|
168
|
+
fn test_tish_version_flag() {
|
|
169
|
+
let bin = tish_bin();
|
|
170
|
+
assert!(bin.exists(), "tish binary not found. Run `cargo build -p tishlang` first.");
|
|
171
|
+
let out = Command::new(&bin).arg("-V").output().expect("run tish -V");
|
|
172
|
+
assert!(out.status.success(), "tish -V failed: {}", String::from_utf8_lossy(&out.stderr));
|
|
173
|
+
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
174
|
+
assert!(
|
|
175
|
+
stdout.contains(env!("CARGO_PKG_VERSION")),
|
|
176
|
+
"tish -V should print version {}; got: {}",
|
|
177
|
+
env!("CARGO_PKG_VERSION"),
|
|
178
|
+
stdout
|
|
179
|
+
);
|
|
180
|
+
let out2 = Command::new(&bin).arg("--version").output().expect("run tish --version");
|
|
181
|
+
assert!(out2.status.success());
|
|
182
|
+
let stdout2 = String::from_utf8_lossy(&out2.stdout);
|
|
183
|
+
assert!(stdout2.contains(env!("CARGO_PKG_VERSION")), "tish --version should print version");
|
|
184
|
+
}
|
|
185
|
+
|
|
166
186
|
/// Parse async-await example (validates async fn parsing).
|
|
167
187
|
#[test]
|
|
168
188
|
fn test_async_await_parse() {
|
|
169
189
|
let path = workspace_root().join("examples").join("async-await").join("src").join("main.tish");
|
|
170
190
|
if path.exists() {
|
|
171
191
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
172
|
-
let result =
|
|
192
|
+
let result = tishlang_parser::parse(&source);
|
|
173
193
|
assert!(result.is_ok(), "Parse failed for {}: {:?}", path.display(), result.err());
|
|
174
194
|
}
|
|
175
195
|
}
|
|
@@ -259,8 +279,8 @@ fn test_async_parallel_vs_sequential_timing() {
|
|
|
259
279
|
);
|
|
260
280
|
}
|
|
261
281
|
|
|
262
|
-
/// Run async-await example via
|
|
263
|
-
/// Ignored:
|
|
282
|
+
/// Run async-await example via tishlang_eval (same path as `tish run`).
|
|
283
|
+
/// Ignored: tishlang_eval::run() is synchronous and does not run the event loop.
|
|
264
284
|
#[test]
|
|
265
285
|
#[cfg(feature = "http")]
|
|
266
286
|
#[ignore = "requires async runtime; use test_async_await_compile_via_binary for CI"]
|
|
@@ -268,13 +288,13 @@ fn test_async_await_run() {
|
|
|
268
288
|
let path = workspace_root().join("examples").join("async-await").join("src").join("main.tish");
|
|
269
289
|
if path.exists() {
|
|
270
290
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
271
|
-
let result =
|
|
291
|
+
let result = tishlang_eval::run(&source);
|
|
272
292
|
assert!(result.is_ok(), "Run failed for {}: {:?}", path.display(), result.err());
|
|
273
293
|
}
|
|
274
294
|
}
|
|
275
295
|
|
|
276
296
|
/// Run Promise and setTimeout module tests (require http feature).
|
|
277
|
-
/// Ignored:
|
|
297
|
+
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
278
298
|
#[test]
|
|
279
299
|
#[cfg(feature = "http")]
|
|
280
300
|
#[ignore = "requires async runtime"]
|
|
@@ -283,7 +303,7 @@ fn test_promise_and_settimeout() {
|
|
|
283
303
|
let path = workspace_root().join("tests").join("modules").join(format!("{}.tish", name));
|
|
284
304
|
if path.exists() {
|
|
285
305
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
286
|
-
let result =
|
|
306
|
+
let result = tishlang_eval::run(&source);
|
|
287
307
|
assert!(
|
|
288
308
|
result.is_ok(),
|
|
289
309
|
"Failed to run {}: {:?}",
|
|
@@ -295,7 +315,7 @@ fn test_promise_and_settimeout() {
|
|
|
295
315
|
}
|
|
296
316
|
|
|
297
317
|
/// Combined validation: async/await + Promise + setTimeout + multiple HTTP requests.
|
|
298
|
-
/// Ignored:
|
|
318
|
+
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
299
319
|
#[test]
|
|
300
320
|
#[cfg(feature = "http")]
|
|
301
321
|
#[ignore = "requires async runtime"]
|
|
@@ -306,7 +326,7 @@ fn test_async_promise_settimeout_combined() {
|
|
|
306
326
|
.join("async_promise_settimeout.tish");
|
|
307
327
|
if path.exists() {
|
|
308
328
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
309
|
-
let result =
|
|
329
|
+
let result = tishlang_eval::run(&source);
|
|
310
330
|
assert!(
|
|
311
331
|
result.is_ok(),
|
|
312
332
|
"Failed to run async_promise_settimeout: {:?}",
|
|
@@ -323,11 +343,11 @@ fn test_vm_date_now() {
|
|
|
323
343
|
return;
|
|
324
344
|
}
|
|
325
345
|
// Library path
|
|
326
|
-
let modules =
|
|
327
|
-
|
|
328
|
-
let program =
|
|
329
|
-
let chunk =
|
|
330
|
-
let result =
|
|
346
|
+
let modules = tishlang_compile::resolve_project(&path, path.parent()).expect("resolve");
|
|
347
|
+
tishlang_compile::detect_cycles(&modules).expect("cycles");
|
|
348
|
+
let program = tishlang_compile::merge_modules(modules).expect("merge");
|
|
349
|
+
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
350
|
+
let result = tishlang_vm::run(&chunk);
|
|
331
351
|
assert!(result.is_ok(), "VM run (library) failed: {:?}", result.err());
|
|
332
352
|
// Binary path - same flow as `tish run <file>`
|
|
333
353
|
let bin = tish_bin();
|
|
@@ -350,9 +370,9 @@ fn test_vm_date_now() {
|
|
|
350
370
|
#[test]
|
|
351
371
|
fn test_vm_index_assign_direct() {
|
|
352
372
|
let source = r#"let arr = [1, 2, 3]; arr[1] = 99; console.log(arr[1]);"#;
|
|
353
|
-
let program =
|
|
354
|
-
let chunk =
|
|
355
|
-
let result =
|
|
373
|
+
let program = tishlang_parser::parse(source).expect("parse");
|
|
374
|
+
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
375
|
+
let result = tishlang_vm::run(&chunk);
|
|
356
376
|
assert!(result.is_ok(), "VM IndexAssign failed: {:?}", result.err());
|
|
357
377
|
}
|
|
358
378
|
|
|
@@ -360,11 +380,11 @@ fn test_vm_index_assign_direct() {
|
|
|
360
380
|
#[test]
|
|
361
381
|
fn test_vm_index_assign_via_resolve() {
|
|
362
382
|
let path = workspace_root().join("tests").join("core").join("array_sort_minimal.tish");
|
|
363
|
-
let modules =
|
|
364
|
-
|
|
365
|
-
let program =
|
|
366
|
-
let chunk =
|
|
367
|
-
let result =
|
|
383
|
+
let modules = tishlang_compile::resolve_project(&path, path.parent()).expect("resolve");
|
|
384
|
+
tishlang_compile::detect_cycles(&modules).expect("cycles");
|
|
385
|
+
let program = tishlang_compile::merge_modules(modules).expect("merge");
|
|
386
|
+
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
387
|
+
let result = tishlang_vm::run(&chunk);
|
|
368
388
|
assert!(result.is_ok(), "VM IndexAssign via resolve failed: {:?}", result.err());
|
|
369
389
|
}
|
|
370
390
|
|
|
@@ -402,7 +422,7 @@ fn test_full_stack_parse() {
|
|
|
402
422
|
let path = entry.unwrap().path();
|
|
403
423
|
if path.extension().map(|e| e == "tish").unwrap_or(false) {
|
|
404
424
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
405
|
-
let result =
|
|
425
|
+
let result = tishlang_parser::parse(&source);
|
|
406
426
|
assert!(
|
|
407
427
|
result.is_ok(),
|
|
408
428
|
"Parse failed for {}: {:?}",
|
|
@@ -464,7 +484,7 @@ fn test_mvp_programs_interpreter() {
|
|
|
464
484
|
let bin = tish_bin();
|
|
465
485
|
assert!(
|
|
466
486
|
bin.exists(),
|
|
467
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
487
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
468
488
|
bin.display()
|
|
469
489
|
);
|
|
470
490
|
let regenerate = std::env::var("REGENERATE_EXPECTED").as_deref() == Ok("1");
|
|
@@ -507,7 +527,7 @@ fn test_mvp_programs_native() {
|
|
|
507
527
|
let bin = tish_bin();
|
|
508
528
|
assert!(
|
|
509
529
|
bin.exists(),
|
|
510
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
530
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
511
531
|
bin.display()
|
|
512
532
|
);
|
|
513
533
|
let errors: Vec<String> = MVP_TEST_FILES
|
|
@@ -574,7 +594,7 @@ fn test_mvp_programs_cranelift() {
|
|
|
574
594
|
let bin = tish_bin();
|
|
575
595
|
assert!(
|
|
576
596
|
bin.exists(),
|
|
577
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
597
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
578
598
|
bin.display()
|
|
579
599
|
);
|
|
580
600
|
let errors: Vec<String> = CRANELIFT_TEST_FILES
|
|
@@ -630,7 +650,7 @@ fn test_mvp_programs_wasi() {
|
|
|
630
650
|
let bin = tish_bin();
|
|
631
651
|
assert!(
|
|
632
652
|
bin.exists(),
|
|
633
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
653
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
634
654
|
bin.display()
|
|
635
655
|
);
|
|
636
656
|
let errors: Vec<String> = CRANELIFT_TEST_FILES
|
|
@@ -689,7 +709,7 @@ fn test_mvp_programs_js() {
|
|
|
689
709
|
let bin = tish_bin();
|
|
690
710
|
assert!(
|
|
691
711
|
bin.exists(),
|
|
692
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
712
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
693
713
|
bin.display()
|
|
694
714
|
);
|
|
695
715
|
for name in MVP_TEST_FILES {
|
|
@@ -325,7 +325,7 @@ pub enum Expr {
|
|
|
325
325
|
children: Vec<JsxChild>,
|
|
326
326
|
span: Span,
|
|
327
327
|
},
|
|
328
|
-
/// Native module load: import { x } from 'tish:egui' → loads from
|
|
328
|
+
/// Native module load: import { x } from 'tish:egui' → loads from tishlang_runtime
|
|
329
329
|
NativeModuleLoad {
|
|
330
330
|
spec: Arc<str>,
|
|
331
331
|
export_name: Arc<str>,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
[package]
|
|
2
|
-
name = "
|
|
2
|
+
name = "tishlang_build_utils"
|
|
3
3
|
version = "0.1.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Shared build utilities for Tish (workspace discovery, path resolution)"
|
|
6
|
+
license-file = { workspace = true }
|
|
7
|
+
repository = { workspace = true }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Shared build utilities for Tish.
|
|
2
2
|
//!
|
|
3
3
|
//! Provides workspace discovery, path resolution, and Cargo build orchestration
|
|
4
|
-
//! used by
|
|
4
|
+
//! used by tishlang_wasm, tishlang_cranelift, tishlang_native, and the tish CLI.
|
|
5
5
|
|
|
6
6
|
use std::fs;
|
|
7
7
|
use std::path::{Path, PathBuf};
|
|
@@ -66,27 +66,39 @@ pub fn find_workspace_root() -> Result<PathBuf, String> {
|
|
|
66
66
|
Err("Cannot find Tish workspace root. Run from workspace root or use cargo run.".to_string())
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/// Find the path to the
|
|
69
|
+
/// Find the path to the tishlang_runtime crate.
|
|
70
70
|
///
|
|
71
71
|
/// Returns a canonical path string suitable for Cargo.toml path dependencies.
|
|
72
72
|
pub fn find_runtime_path() -> Result<String, String> {
|
|
73
73
|
let workspace = find_workspace_root()?;
|
|
74
74
|
let runtime = workspace.join("crates").join("tish_runtime");
|
|
75
75
|
if !runtime.exists() {
|
|
76
|
-
return Err("
|
|
76
|
+
return Err("tishlang_runtime crate not found".to_string());
|
|
77
77
|
}
|
|
78
78
|
runtime
|
|
79
79
|
.canonicalize()
|
|
80
|
-
.map_err(|e| format!("Cannot canonicalize
|
|
80
|
+
.map_err(|e| format!("Cannot canonicalize tishlang_runtime: {}", e))
|
|
81
81
|
.map(|p| p.display().to_string().replace('\\', "/"))
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/// Crate package name -> directory name (directories kept as tish_* for historical reasons).
|
|
85
|
+
const CRATE_NAME_TO_DIR: &[(&str, &str)] = &[
|
|
86
|
+
("tishlang_runtime", "tish_runtime"),
|
|
87
|
+
("tishlang_cranelift_runtime", "tish_cranelift_runtime"),
|
|
88
|
+
("tishlang_wasm_runtime", "tish_wasm_runtime"),
|
|
89
|
+
]; // directory names kept as tish_* for historical reasons
|
|
90
|
+
|
|
84
91
|
/// Find the path to a crate within the workspace by name.
|
|
85
92
|
///
|
|
86
|
-
/// e.g. `find_crate_path("
|
|
93
|
+
/// e.g. `find_crate_path("tishlang_cranelift_runtime")` returns the path to crates/tish_cranelift_runtime.
|
|
87
94
|
pub fn find_crate_path(crate_name: &str) -> Result<PathBuf, String> {
|
|
88
95
|
let workspace = find_workspace_root()?;
|
|
89
|
-
let
|
|
96
|
+
let dir_name = CRATE_NAME_TO_DIR
|
|
97
|
+
.iter()
|
|
98
|
+
.find(|(name, _)| *name == crate_name)
|
|
99
|
+
.map(|(_, dir)| *dir)
|
|
100
|
+
.unwrap_or(crate_name);
|
|
101
|
+
let crate_path = workspace.join("crates").join(dir_name);
|
|
90
102
|
if !crate_path.exists() {
|
|
91
103
|
return Err(format!("Crate {} not found", crate_name));
|
|
92
104
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
[package]
|
|
2
|
-
name = "
|
|
2
|
+
name = "tishlang_builtins"
|
|
3
3
|
version = "0.1.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
|
+
license-file = { workspace = true }
|
|
7
|
+
repository = { workspace = true }
|
|
6
8
|
[dependencies]
|
|
7
9
|
rand = "0.10.0"
|
|
8
|
-
|
|
10
|
+
tishlang_core = { path = "../tish_core" }
|
|
9
11
|
|
|
10
12
|
[features]
|
|
11
13
|
default = []
|
|
12
|
-
regex = ["
|
|
14
|
+
regex = ["tishlang_core/regex"]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
4
|
use std::rc::Rc;
|
|
5
|
-
use
|
|
5
|
+
use tishlang_core::Value;
|
|
6
6
|
use crate::helpers::normalize_index;
|
|
7
7
|
|
|
8
8
|
/// Create a new array Value from a Vec of Values.
|
|
@@ -203,7 +203,7 @@ pub fn flat(arr: &Value, depth: &Value) -> Value {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// Higher-order array methods require a callback function.
|
|
206
|
-
// These take NativeFn from
|
|
206
|
+
// These take NativeFn from tishlang_core::Value::Function
|
|
207
207
|
|
|
208
208
|
pub fn map(arr: &Value, callback: &Value) -> Value {
|
|
209
209
|
if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
//! Global builtin functions with signature (args: &[Value]) -> Value.
|
|
2
2
|
//!
|
|
3
|
-
//! Used by both
|
|
4
|
-
//! independent of
|
|
3
|
+
//! Used by both tishlang_vm (bytecode) and tishlang_runtime (compiled). Keeps tishlang_vm
|
|
4
|
+
//! independent of tishlang_runtime.
|
|
5
5
|
|
|
6
6
|
use std::cell::RefCell;
|
|
7
7
|
use std::collections::HashMap;
|
|
8
8
|
use std::rc::Rc;
|
|
9
9
|
use std::sync::Arc;
|
|
10
|
-
use
|
|
10
|
+
use tishlang_core::{percent_decode, percent_encode, Value};
|
|
11
11
|
|
|
12
12
|
/// Boolean(value) - coerce to bool
|
|
13
13
|
pub fn boolean(args: &[Value]) -> Value {
|
|
@@ -4,7 +4,7 @@ use std::cell::RefCell;
|
|
|
4
4
|
use std::collections::HashMap;
|
|
5
5
|
use std::rc::Rc;
|
|
6
6
|
use std::sync::Arc;
|
|
7
|
-
use
|
|
7
|
+
use tishlang_core::Value;
|
|
8
8
|
|
|
9
9
|
/// Normalize an array index, handling negative indices.
|
|
10
10
|
/// Returns a valid index within bounds or the default value.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Shared builtin implementations for Tish.
|
|
2
2
|
//!
|
|
3
|
-
//! Used by the compiled runtime (
|
|
4
|
-
//! interpreter (
|
|
3
|
+
//! Used by the compiled runtime (tishlang_runtime) and bytecode VM (tishlang_vm). The
|
|
4
|
+
//! interpreter (tishlang_eval) implements builtins inline due to different Value
|
|
5
5
|
//! and native signatures.
|
|
6
6
|
|
|
7
7
|
pub mod array;
|
|
@@ -11,4 +11,4 @@ pub mod math;
|
|
|
11
11
|
pub mod helpers;
|
|
12
12
|
pub mod globals;
|
|
13
13
|
|
|
14
|
-
pub use
|
|
14
|
+
pub use tishlang_core::Value;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
//! Object builtin methods.
|
|
2
2
|
//!
|
|
3
3
|
//! This module will contain shared object method implementations.
|
|
4
|
-
//! Functions will be migrated here from
|
|
4
|
+
//! Functions will be migrated here from tishlang_runtime and tishlang_eval.
|
|
5
5
|
|
|
6
6
|
use std::cell::RefCell;
|
|
7
7
|
use std::collections::HashMap;
|
|
8
8
|
use std::rc::Rc;
|
|
9
9
|
use std::sync::Arc;
|
|
10
|
-
use
|
|
10
|
+
use tishlang_core::Value;
|
|
11
11
|
|
|
12
12
|
/// Create a new empty object Value.
|
|
13
13
|
pub fn new() -> Value {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
[package]
|
|
2
|
-
name = "
|
|
2
|
+
name = "tishlang_bytecode"
|
|
3
3
|
version = "0.1.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Bytecode compiler for Tish (AST → bytecode)"
|
|
6
6
|
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
7
9
|
[dependencies]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
tishlang_ast = { path = "../tish_ast" }
|
|
11
|
+
tishlang_core = { path = "../tish_core" }
|
|
10
12
|
|
|
11
13
|
[dev-dependencies]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
tishlang_compile = { path = "../tish_compile" }
|
|
15
|
+
tishlang_parser = { path = "../tish_parser" }
|
|
16
|
+
tishlang_opt = { path = "../tish_opt" }
|
|
17
|
+
tishlang_vm = { path = "../tish_vm" }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
use std::collections::HashMap;
|
|
4
4
|
use std::sync::Arc;
|
|
5
5
|
|
|
6
|
-
use
|
|
6
|
+
use tishlang_ast::{
|
|
7
7
|
ArrayElement, ArrowBody, BinOp, CallArg, DestructElement, DestructPattern, Expr,
|
|
8
8
|
JsxAttrValue, JsxChild, JsxProp, Literal, MemberProp, ObjectProp, Program, Span, Statement,
|
|
9
9
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
//! Canonical u8 encoding for AST operators in bytecode.
|
|
2
2
|
//! Single source of truth: compiler encodes with *\_to_u8, VM decodes with u8_to\_*.
|
|
3
3
|
|
|
4
|
-
use
|
|
4
|
+
use tishlang_ast::{BinOp, CompoundOp, UnaryOp};
|
|
5
5
|
|
|
6
6
|
/// Encode BinOp for bytecode operand. Used by compiler.
|
|
7
7
|
pub fn binop_to_u8(op: BinOp) -> u8 {
|
|
8
|
-
use
|
|
8
|
+
use tishlang_ast::BinOp::*;
|
|
9
9
|
match op {
|
|
10
10
|
Add => 0,
|
|
11
11
|
Sub => 1,
|
|
@@ -34,7 +34,7 @@ pub fn binop_to_u8(op: BinOp) -> u8 {
|
|
|
34
34
|
|
|
35
35
|
/// Decode bytecode operand to BinOp. Used by VM.
|
|
36
36
|
pub fn u8_to_binop(b: u8) -> Option<BinOp> {
|
|
37
|
-
use
|
|
37
|
+
use tishlang_ast::BinOp::*;
|
|
38
38
|
Some(match b {
|
|
39
39
|
0 => Add,
|
|
40
40
|
1 => Sub,
|
|
@@ -64,7 +64,7 @@ pub fn u8_to_binop(b: u8) -> Option<BinOp> {
|
|
|
64
64
|
|
|
65
65
|
/// Encode CompoundOp for bytecode (same numeric subset as BinOp: Add,Sub,Mul,Div,Mod).
|
|
66
66
|
pub fn compound_op_to_u8(op: CompoundOp) -> u8 {
|
|
67
|
-
use
|
|
67
|
+
use tishlang_ast::CompoundOp::*;
|
|
68
68
|
match op {
|
|
69
69
|
Add => 0,
|
|
70
70
|
Sub => 1,
|
|
@@ -76,7 +76,7 @@ pub fn compound_op_to_u8(op: CompoundOp) -> u8 {
|
|
|
76
76
|
|
|
77
77
|
/// Encode UnaryOp for bytecode operand. Used by compiler.
|
|
78
78
|
pub fn unaryop_to_u8(op: UnaryOp) -> u8 {
|
|
79
|
-
use
|
|
79
|
+
use tishlang_ast::UnaryOp::*;
|
|
80
80
|
match op {
|
|
81
81
|
Not => 0,
|
|
82
82
|
Neg => 1,
|
|
@@ -88,7 +88,7 @@ pub fn unaryop_to_u8(op: UnaryOp) -> u8 {
|
|
|
88
88
|
|
|
89
89
|
/// Decode bytecode operand to UnaryOp. Used by VM.
|
|
90
90
|
pub fn u8_to_unaryop(b: u8) -> Option<UnaryOp> {
|
|
91
|
-
use
|
|
91
|
+
use tishlang_ast::UnaryOp::*;
|
|
92
92
|
Some(match b {
|
|
93
93
|
0 => Not,
|
|
94
94
|
1 => Neg,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//! Verify AST optimization (constant folding) yields expected bytecode.
|
|
2
|
-
//! Uses
|
|
2
|
+
//! Uses tishlang_opt::optimize before compile to match the pipeline used by run/compile.
|
|
3
3
|
|
|
4
|
-
use
|
|
5
|
-
use
|
|
4
|
+
use tishlang_bytecode::{compile, Chunk, Opcode};
|
|
5
|
+
use tishlang_parser::parse;
|
|
6
6
|
|
|
7
7
|
fn chunk_contains_opcode(chunk: &Chunk, op: u8) -> bool {
|
|
8
8
|
if chunk.code.contains(&op) {
|
|
@@ -21,14 +21,14 @@ fn chunk_contains_opcode(chunk: &Chunk, op: u8) -> bool {
|
|
|
21
21
|
fn constant_fold_binary_no_binop() {
|
|
22
22
|
let source = "1 + 2";
|
|
23
23
|
let program = parse(source).expect("parse");
|
|
24
|
-
let optimized =
|
|
24
|
+
let optimized = tishlang_opt::optimize(&program);
|
|
25
25
|
let chunk = compile(&optimized).expect("compile");
|
|
26
26
|
assert!(
|
|
27
27
|
!chunk_contains_opcode(&chunk, Opcode::BinOp as u8),
|
|
28
28
|
"Expected no BinOp for 1+2 after constant folding"
|
|
29
29
|
);
|
|
30
30
|
assert!(
|
|
31
|
-
chunk.constants.iter().any(|c| matches!(c,
|
|
31
|
+
chunk.constants.iter().any(|c| matches!(c, tishlang_bytecode::Constant::Number(n) if (*n - 3.0).abs() < f64::EPSILON)),
|
|
32
32
|
"Expected constant 3 in chunk"
|
|
33
33
|
);
|
|
34
34
|
}
|
|
@@ -62,7 +62,7 @@ fn chunk_contains_dup_pop(chunk: &Chunk) -> bool {
|
|
|
62
62
|
fn peephole_remove_dup_pop() {
|
|
63
63
|
let source = "let o = {a:1}; o?.a";
|
|
64
64
|
let program = parse(source).expect("parse");
|
|
65
|
-
let optimized =
|
|
65
|
+
let optimized = tishlang_opt::optimize(&program);
|
|
66
66
|
let chunk = compile(&optimized).expect("compile");
|
|
67
67
|
assert!(
|
|
68
68
|
!chunk_contains_dup_pop(&chunk),
|
|
@@ -75,7 +75,7 @@ fn peephole_remove_dup_pop() {
|
|
|
75
75
|
fn constant_fold_unary_no_unaryop() {
|
|
76
76
|
let source = "-42";
|
|
77
77
|
let program = parse(source).expect("parse");
|
|
78
|
-
let optimized =
|
|
78
|
+
let optimized = tishlang_opt::optimize(&program);
|
|
79
79
|
let chunk = compile(&optimized).expect("compile");
|
|
80
80
|
assert!(
|
|
81
81
|
!chunk_contains_opcode(&chunk, Opcode::UnaryOp as u8),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//! Verify && and || short-circuit (JumpIfFalse before evaluating right side).
|
|
2
2
|
use std::path::Path;
|
|
3
|
-
use
|
|
4
|
-
use
|
|
5
|
-
use
|
|
6
|
-
use
|
|
7
|
-
use
|
|
3
|
+
use tishlang_bytecode::{compile, compile_unoptimized, Opcode};
|
|
4
|
+
use tishlang_compile::{merge_modules, resolve_project};
|
|
5
|
+
use tishlang_parser::parse;
|
|
6
|
+
use tishlang_opt;
|
|
7
|
+
use tishlang_vm;
|
|
8
8
|
|
|
9
9
|
#[test]
|
|
10
10
|
fn test_and_shortcircuit_emits_jump() {
|
|
@@ -21,7 +21,7 @@ fn test_and_shortcircuit_runs_unoptimized() {
|
|
|
21
21
|
let source = "let x = null; let y = x != null && x.foo;";
|
|
22
22
|
let program = parse(source).expect("parse");
|
|
23
23
|
let chunk = compile_unoptimized(&program).expect("compile");
|
|
24
|
-
let result =
|
|
24
|
+
let result = tishlang_vm::run(&chunk);
|
|
25
25
|
assert!(result.is_ok(), "Should not throw (short-circuit avoids x.foo): {:?}", result.err());
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -29,9 +29,9 @@ fn test_and_shortcircuit_runs_unoptimized() {
|
|
|
29
29
|
fn test_and_shortcircuit_runs_optimized() {
|
|
30
30
|
let source = "let x = null; let y = x != null && x.foo;";
|
|
31
31
|
let program = parse(source).expect("parse");
|
|
32
|
-
let program =
|
|
33
|
-
let chunk =
|
|
34
|
-
let result =
|
|
32
|
+
let program = tishlang_opt::optimize(&program);
|
|
33
|
+
let chunk = tishlang_bytecode::compile(&program).expect("compile");
|
|
34
|
+
let result = tishlang_vm::run(&chunk);
|
|
35
35
|
assert!(result.is_ok(), "Should not throw with peephole (short-circuit): {:?}", result.err());
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -42,8 +42,8 @@ fn test_and_shortcircuit_via_resolve_project() {
|
|
|
42
42
|
let project_root = path.parent().unwrap();
|
|
43
43
|
let modules = resolve_project(&path, Some(project_root)).expect("resolve");
|
|
44
44
|
let program = merge_modules(modules).expect("merge");
|
|
45
|
-
let program =
|
|
45
|
+
let program = tishlang_opt::optimize(&program); // Mirror CLI
|
|
46
46
|
let chunk = compile(&program).expect("compile");
|
|
47
|
-
let result =
|
|
47
|
+
let result = tishlang_vm::run(&chunk);
|
|
48
48
|
assert!(result.is_ok(), "Should not throw via resolve+merge+opt (CLI path): {:?}", result.err());
|
|
49
49
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
//! Verify arr.sort((a,b)=>a-b) compiles to ArraySortNumeric (opcode 31).
|
|
2
2
|
|
|
3
|
-
use
|
|
4
|
-
use
|
|
3
|
+
use tishlang_bytecode::{compile, Opcode};
|
|
4
|
+
use tishlang_parser::parse;
|
|
5
5
|
|
|
6
|
-
fn chunk_contains_opcode(chunk: &
|
|
6
|
+
fn chunk_contains_opcode(chunk: &tishlang_bytecode::Chunk, op: u8) -> bool {
|
|
7
7
|
if chunk.code.contains(&op) {
|
|
8
8
|
return true;
|
|
9
9
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
[package]
|
|
2
|
-
name = "
|
|
2
|
+
name = "tishlang_compile"
|
|
3
3
|
version = "0.1.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Tish native compiler backend"
|
|
6
6
|
|
|
7
|
+
license-file = { workspace = true }
|
|
8
|
+
repository = { workspace = true }
|
|
7
9
|
[features]
|
|
8
10
|
default = []
|
|
9
11
|
http = []
|
|
@@ -13,9 +15,9 @@ regex = []
|
|
|
13
15
|
ws = []
|
|
14
16
|
|
|
15
17
|
[dependencies]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
tishlang_ast = { path = "../tish_ast" }
|
|
19
|
+
tishlang_opt = { path = "../tish_opt" }
|
|
20
|
+
tishlang_parser = { path = "../tish_parser" }
|
|
19
21
|
serde_json = "1.0"
|
|
20
22
|
|
|
21
23
|
[dev-dependencies]
|