@tishlang/tish 1.0.13 → 1.0.15
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 +46 -46
- package/crates/tish/src/repl_completion.rs +4 -4
- package/crates/tish/tests/integration_test.rs +28 -28
- 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
|
@@ -167,7 +167,7 @@ fn tish_bin() -> PathBuf {
|
|
|
167
167
|
#[test]
|
|
168
168
|
fn test_tish_version_flag() {
|
|
169
169
|
let bin = tish_bin();
|
|
170
|
-
assert!(bin.exists(), "tish binary not found. Run `cargo build -p
|
|
170
|
+
assert!(bin.exists(), "tish binary not found. Run `cargo build -p tishlang` first.");
|
|
171
171
|
let out = Command::new(&bin).arg("-V").output().expect("run tish -V");
|
|
172
172
|
assert!(out.status.success(), "tish -V failed: {}", String::from_utf8_lossy(&out.stderr));
|
|
173
173
|
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
@@ -189,7 +189,7 @@ fn test_async_await_parse() {
|
|
|
189
189
|
let path = workspace_root().join("examples").join("async-await").join("src").join("main.tish");
|
|
190
190
|
if path.exists() {
|
|
191
191
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
192
|
-
let result =
|
|
192
|
+
let result = tishlang_parser::parse(&source);
|
|
193
193
|
assert!(result.is_ok(), "Parse failed for {}: {:?}", path.display(), result.err());
|
|
194
194
|
}
|
|
195
195
|
}
|
|
@@ -279,8 +279,8 @@ fn test_async_parallel_vs_sequential_timing() {
|
|
|
279
279
|
);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
/// Run async-await example via
|
|
283
|
-
/// 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.
|
|
284
284
|
#[test]
|
|
285
285
|
#[cfg(feature = "http")]
|
|
286
286
|
#[ignore = "requires async runtime; use test_async_await_compile_via_binary for CI"]
|
|
@@ -288,13 +288,13 @@ fn test_async_await_run() {
|
|
|
288
288
|
let path = workspace_root().join("examples").join("async-await").join("src").join("main.tish");
|
|
289
289
|
if path.exists() {
|
|
290
290
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
291
|
-
let result =
|
|
291
|
+
let result = tishlang_eval::run(&source);
|
|
292
292
|
assert!(result.is_ok(), "Run failed for {}: {:?}", path.display(), result.err());
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
/// Run Promise and setTimeout module tests (require http feature).
|
|
297
|
-
/// Ignored:
|
|
297
|
+
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
298
298
|
#[test]
|
|
299
299
|
#[cfg(feature = "http")]
|
|
300
300
|
#[ignore = "requires async runtime"]
|
|
@@ -303,7 +303,7 @@ fn test_promise_and_settimeout() {
|
|
|
303
303
|
let path = workspace_root().join("tests").join("modules").join(format!("{}.tish", name));
|
|
304
304
|
if path.exists() {
|
|
305
305
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
306
|
-
let result =
|
|
306
|
+
let result = tishlang_eval::run(&source);
|
|
307
307
|
assert!(
|
|
308
308
|
result.is_ok(),
|
|
309
309
|
"Failed to run {}: {:?}",
|
|
@@ -315,7 +315,7 @@ fn test_promise_and_settimeout() {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
/// Combined validation: async/await + Promise + setTimeout + multiple HTTP requests.
|
|
318
|
-
/// Ignored:
|
|
318
|
+
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
319
319
|
#[test]
|
|
320
320
|
#[cfg(feature = "http")]
|
|
321
321
|
#[ignore = "requires async runtime"]
|
|
@@ -326,7 +326,7 @@ fn test_async_promise_settimeout_combined() {
|
|
|
326
326
|
.join("async_promise_settimeout.tish");
|
|
327
327
|
if path.exists() {
|
|
328
328
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
329
|
-
let result =
|
|
329
|
+
let result = tishlang_eval::run(&source);
|
|
330
330
|
assert!(
|
|
331
331
|
result.is_ok(),
|
|
332
332
|
"Failed to run async_promise_settimeout: {:?}",
|
|
@@ -343,11 +343,11 @@ fn test_vm_date_now() {
|
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
345
345
|
// Library path
|
|
346
|
-
let modules =
|
|
347
|
-
|
|
348
|
-
let program =
|
|
349
|
-
let chunk =
|
|
350
|
-
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);
|
|
351
351
|
assert!(result.is_ok(), "VM run (library) failed: {:?}", result.err());
|
|
352
352
|
// Binary path - same flow as `tish run <file>`
|
|
353
353
|
let bin = tish_bin();
|
|
@@ -370,9 +370,9 @@ fn test_vm_date_now() {
|
|
|
370
370
|
#[test]
|
|
371
371
|
fn test_vm_index_assign_direct() {
|
|
372
372
|
let source = r#"let arr = [1, 2, 3]; arr[1] = 99; console.log(arr[1]);"#;
|
|
373
|
-
let program =
|
|
374
|
-
let chunk =
|
|
375
|
-
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);
|
|
376
376
|
assert!(result.is_ok(), "VM IndexAssign failed: {:?}", result.err());
|
|
377
377
|
}
|
|
378
378
|
|
|
@@ -380,11 +380,11 @@ fn test_vm_index_assign_direct() {
|
|
|
380
380
|
#[test]
|
|
381
381
|
fn test_vm_index_assign_via_resolve() {
|
|
382
382
|
let path = workspace_root().join("tests").join("core").join("array_sort_minimal.tish");
|
|
383
|
-
let modules =
|
|
384
|
-
|
|
385
|
-
let program =
|
|
386
|
-
let chunk =
|
|
387
|
-
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);
|
|
388
388
|
assert!(result.is_ok(), "VM IndexAssign via resolve failed: {:?}", result.err());
|
|
389
389
|
}
|
|
390
390
|
|
|
@@ -422,7 +422,7 @@ fn test_full_stack_parse() {
|
|
|
422
422
|
let path = entry.unwrap().path();
|
|
423
423
|
if path.extension().map(|e| e == "tish").unwrap_or(false) {
|
|
424
424
|
let source = std::fs::read_to_string(&path).unwrap();
|
|
425
|
-
let result =
|
|
425
|
+
let result = tishlang_parser::parse(&source);
|
|
426
426
|
assert!(
|
|
427
427
|
result.is_ok(),
|
|
428
428
|
"Parse failed for {}: {:?}",
|
|
@@ -484,7 +484,7 @@ fn test_mvp_programs_interpreter() {
|
|
|
484
484
|
let bin = tish_bin();
|
|
485
485
|
assert!(
|
|
486
486
|
bin.exists(),
|
|
487
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
487
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
488
488
|
bin.display()
|
|
489
489
|
);
|
|
490
490
|
let regenerate = std::env::var("REGENERATE_EXPECTED").as_deref() == Ok("1");
|
|
@@ -527,7 +527,7 @@ fn test_mvp_programs_native() {
|
|
|
527
527
|
let bin = tish_bin();
|
|
528
528
|
assert!(
|
|
529
529
|
bin.exists(),
|
|
530
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
530
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
531
531
|
bin.display()
|
|
532
532
|
);
|
|
533
533
|
let errors: Vec<String> = MVP_TEST_FILES
|
|
@@ -594,7 +594,7 @@ fn test_mvp_programs_cranelift() {
|
|
|
594
594
|
let bin = tish_bin();
|
|
595
595
|
assert!(
|
|
596
596
|
bin.exists(),
|
|
597
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
597
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
598
598
|
bin.display()
|
|
599
599
|
);
|
|
600
600
|
let errors: Vec<String> = CRANELIFT_TEST_FILES
|
|
@@ -650,7 +650,7 @@ fn test_mvp_programs_wasi() {
|
|
|
650
650
|
let bin = tish_bin();
|
|
651
651
|
assert!(
|
|
652
652
|
bin.exists(),
|
|
653
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
653
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
654
654
|
bin.display()
|
|
655
655
|
);
|
|
656
656
|
let errors: Vec<String> = CRANELIFT_TEST_FILES
|
|
@@ -709,7 +709,7 @@ fn test_mvp_programs_js() {
|
|
|
709
709
|
let bin = tish_bin();
|
|
710
710
|
assert!(
|
|
711
711
|
bin.exists(),
|
|
712
|
-
"tish binary not found at {}. Run `cargo build -p
|
|
712
|
+
"tish binary not found at {}. Run `cargo build -p tishlang` first.",
|
|
713
713
|
bin.display()
|
|
714
714
|
);
|
|
715
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]
|