use tishlang_bytecode::{compile, Chunk, Opcode};
use tishlang_parser::parse;
fn count_opcode(chunk: &Chunk, op: u8) -> usize {
let mut n = chunk.code.iter().filter(|&&b| b == op).count();
for nested in &chunk.nested {
n += count_opcode(nested, op);
}
n
}
fn chunk_of(src: &str) -> Chunk {
let program = parse(src).expect("parse");
let optimized = tishlang_opt::optimize(&program);
compile(&optimized).expect("compile")
}
#[test]
fn string_plus_assign_uses_append_local() {
let chunk = chunk_of("let s = \"\"\nlet i = 0\nwhile (i < 10) { s = s + \"x\"; i = i + 1 }\n");
assert!(
count_opcode(&chunk, Opcode::AppendLocal as u8) >= 1,
"`s = s + \"x\"` must emit AppendLocal"
);
}
#[test]
fn numeric_plus_assign_does_not_use_append_local() {
let chunk = chunk_of("let n = 0\nlet i = 0\nwhile (i < 10) { n = n + 2; i = i + 1 }\n");
assert_eq!(
count_opcode(&chunk, Opcode::AppendLocal as u8),
0,
"`n = n + 2` / `i = i + 1` must NOT emit AppendLocal (would thrash the single builder slot)"
);
}
#[test]
fn template_literal_rhs_uses_append_local() {
let chunk = chunk_of("let t = \"\"\nlet j = 0\nwhile (j < 3) { t = t + `[${j}]`; j = j + 1 }\n");
assert!(
count_opcode(&chunk, Opcode::AppendLocal as u8) >= 1,
"`t = t + `[${{j}}]`` (template RHS) must emit AppendLocal"
);
}
#[test]
fn prepend_does_not_use_append_local() {
let chunk = chunk_of("let p = \"z\"\np = \"a\" + p\n");
assert_eq!(
count_opcode(&chunk, Opcode::AppendLocal as u8),
0,
"prepend `p = \"a\" + p` must not use the append builder"
);
}