@tishlang/tish 1.4.2 → 1.6.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.
Files changed (39) hide show
  1. package/bin/tish +0 -0
  2. package/crates/tish/Cargo.toml +2 -2
  3. package/crates/tish/src/cli_help.rs +504 -0
  4. package/crates/tish/src/main.rs +76 -90
  5. package/crates/tish/src/repl_completion.rs +1 -1
  6. package/crates/tish/tests/cargo_example_compile.rs +65 -0
  7. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  8. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  9. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  10. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  11. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  12. package/crates/tish/tests/integration_test.rs +48 -0
  13. package/crates/tish_build_utils/src/lib.rs +204 -1
  14. package/crates/tish_builtins/src/string.rs +248 -0
  15. package/crates/tish_bytecode/Cargo.toml +1 -0
  16. package/crates/tish_bytecode/src/compiler.rs +289 -66
  17. package/crates/tish_bytecode/src/opcode.rs +13 -3
  18. package/crates/tish_bytecode/src/peephole.rs +21 -16
  19. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  20. package/crates/tish_compile/Cargo.toml +1 -0
  21. package/crates/tish_compile/src/codegen.rs +277 -93
  22. package/crates/tish_compile/src/lib.rs +7 -4
  23. package/crates/tish_compile/src/resolve.rs +418 -40
  24. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +1 -0
  25. package/crates/tish_core/src/value.rs +1 -0
  26. package/crates/tish_eval/src/eval.rs +49 -1
  27. package/crates/tish_eval/src/lib.rs +1 -1
  28. package/crates/tish_native/src/build.rs +86 -17
  29. package/crates/tish_native/src/lib.rs +36 -16
  30. package/crates/tish_runtime/src/lib.rs +4 -0
  31. package/crates/tish_vm/src/lib.rs +1 -1
  32. package/crates/tish_vm/src/vm.rs +165 -19
  33. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  34. package/package.json +1 -1
  35. package/platform/darwin-arm64/tish +0 -0
  36. package/platform/darwin-x64/tish +0 -0
  37. package/platform/linux-arm64/tish +0 -0
  38. package/platform/linux-x64/tish +0 -0
  39. package/platform/win32-x64/tish.exe +0 -0
@@ -1,6 +1,8 @@
1
1
  //! Peephole optimizations on bytecode (post-emission).
2
2
  //! B2 from optimization plan: jump chaining, etc.
3
3
 
4
+ use std::collections::BTreeSet;
5
+
4
6
  use crate::opcode::Opcode;
5
7
  use crate::Chunk;
6
8
 
@@ -90,6 +92,18 @@ fn final_jump_target(code: &[u8], jump_ip: usize) -> Option<usize> {
90
92
  skip_unconditional_jump_chain(code, first_target)
91
93
  }
92
94
 
95
+ /// Instruction boundaries from a linear scan (aligned bytecode from the compiler).
96
+ fn collect_insn_starts(code: &[u8]) -> BTreeSet<usize> {
97
+ let mut out = BTreeSet::new();
98
+ let mut ip = 0usize;
99
+ while ip < code.len() {
100
+ out.insert(ip);
101
+ let sz = instruction_size(code, ip).unwrap_or(1);
102
+ ip += sz;
103
+ }
104
+ out
105
+ }
106
+
93
107
  /// Replace instruction at [ip..ip+len) with Nops (preserves length, no offset updates).
94
108
  fn nop_out(code: &mut [u8], ip: usize, len: usize) {
95
109
  for i in 0..len {
@@ -112,19 +126,6 @@ fn remove_dup_pop(code: &mut [u8]) {
112
126
  }
113
127
  }
114
128
 
115
- /// Remove redundant LoadConst + Pop (load constant then discard = no-op).
116
- fn remove_loadconst_pop(code: &mut [u8]) {
117
- let mut ip = 0;
118
- while ip + 4 <= code.len() {
119
- if Opcode::from_u8(code[ip]) == Some(Opcode::LoadConst)
120
- && Opcode::from_u8(code[ip + 3]) == Some(Opcode::Pop)
121
- {
122
- nop_out(code, ip, 4);
123
- }
124
- ip += instruction_size(code, ip).unwrap_or(1);
125
- }
126
- }
127
-
128
129
  /// Replace no-op jumps (Jump with offset 0) with Nops.
129
130
  fn remove_noop_jumps(code: &mut [u8]) {
130
131
  let mut ip = 0;
@@ -142,6 +143,7 @@ fn remove_noop_jumps(code: &mut [u8]) {
142
143
  /// Apply jump chaining: if Jump/JumpIfFalse targets another jump, update to
143
144
  /// jump directly to the final target.
144
145
  fn chain_jumps(code: &mut [u8]) {
146
+ let insn_starts = collect_insn_starts(code);
145
147
  let mut ip = 0;
146
148
  while ip < code.len() {
147
149
  let op = match Opcode::from_u8(code[ip]) {
@@ -160,9 +162,13 @@ fn chain_jumps(code: &mut [u8]) {
160
162
  let current_offset = read_i16(code, ip + 1) as isize;
161
163
  let current_target = (ip as isize + 3 + current_offset).max(0) as usize;
162
164
  if let Some(final_target) = final_jump_target(code, ip) {
163
- if final_target != current_target {
165
+ let target_ok = final_target == code.len()
166
+ || insn_starts.contains(&final_target);
167
+ if final_target != current_target && target_ok {
164
168
  let new_offset = final_target as i32 - (ip + 3) as i32;
165
- write_u16(code, ip + 1, (new_offset as i16) as u16);
169
+ if (i16::MIN as i32..=i16::MAX as i32).contains(&new_offset) {
170
+ write_u16(code, ip + 1, (new_offset as i16) as u16);
171
+ }
166
172
  }
167
173
  }
168
174
  }
@@ -174,7 +180,6 @@ fn chain_jumps(code: &mut [u8]) {
174
180
 
175
181
  /// Run peephole optimizations on a chunk (and nested chunks).
176
182
  pub fn optimize(chunk: &mut Chunk) {
177
- remove_loadconst_pop(&mut chunk.code);
178
183
  remove_dup_pop(&mut chunk.code);
179
184
  remove_noop_jumps(&mut chunk.code);
180
185
  chain_jumps(&mut chunk.code);
@@ -0,0 +1,44 @@
1
+ //! Regression: C-style `for` `continue` must jump forward to the update clause (not JumpBack).
2
+
3
+ use std::fs;
4
+
5
+ use tishlang_bytecode::{compile, compile_unoptimized};
6
+ use tishlang_vm::run;
7
+
8
+ #[test]
9
+ fn break_continue_fixture_runs_on_vm() {
10
+ let path = format!(
11
+ "{}/../../tests/core/break_continue.tish",
12
+ env!("CARGO_MANIFEST_DIR")
13
+ );
14
+ let src = fs::read_to_string(&path).unwrap();
15
+ let prog = tishlang_parser::parse(&src).expect("parse");
16
+ let chunk = compile_unoptimized(&prog).expect("compile");
17
+ run(&chunk).expect("VM run");
18
+ }
19
+
20
+ #[test]
21
+ fn mutation_vm_ast_opt_without_peephole() {
22
+ let path = format!(
23
+ "{}/../../tests/core/mutation.tish",
24
+ env!("CARGO_MANIFEST_DIR")
25
+ );
26
+ let src = fs::read_to_string(&path).unwrap();
27
+ let mut prog = tishlang_parser::parse(&src).expect("parse");
28
+ tishlang_opt::optimize(&mut prog);
29
+ let chunk = compile_unoptimized(&prog).expect("compile");
30
+ run(&chunk).expect("VM");
31
+ }
32
+
33
+ #[test]
34
+ fn mutation_vm_ast_opt_with_peephole() {
35
+ let path = format!(
36
+ "{}/../../tests/core/mutation.tish",
37
+ env!("CARGO_MANIFEST_DIR")
38
+ );
39
+ let src = fs::read_to_string(&path).unwrap();
40
+ let mut prog = tishlang_parser::parse(&src).expect("parse");
41
+ tishlang_opt::optimize(&mut prog);
42
+ let chunk = compile(&prog).expect("compile");
43
+ run(&chunk).expect("VM");
44
+ }
@@ -22,3 +22,4 @@ tishlang_ui = { path = "../tish_ui", version = ">=0.1", default-features = false
22
22
  serde_json = "1.0"
23
23
 
24
24
  [dev-dependencies]
25
+ tempfile = "3"