use std::hint::black_box;
use std::time::Instant;
use mxsh::parser::Parser;
fn repeated_assignments(lines: usize) -> String {
let mut script = String::with_capacity(lines * 32);
for i in 0..lines {
script.push_str(&format!("VAR_{i}=$(({} + {}))\n", i, i + 1));
}
script
}
fn nested_loops(depth: usize, width: usize) -> String {
let mut script = String::new();
for level in 0..depth {
script.push_str(&format!("for i{level} in"));
for item in 0..width {
script.push_str(&format!(" {item}"));
}
script.push_str("; do\n");
}
script.push_str("echo done\n");
for _ in 0..depth {
script.push_str("done\n");
}
script
}
fn heredoc_fanout(count: usize, lines_per_doc: usize) -> String {
let mut script = String::new();
for idx in 0..count {
script.push_str(&format!("cat <<EOF_{idx}\n"));
for line in 0..lines_per_doc {
script.push_str(&format!("line_{idx}_{line} $HOME\n"));
}
script.push_str(&format!("EOF_{idx}\n"));
}
script
}
fn bench_case(name: &str, script: &str, iterations: usize) {
let start = Instant::now();
let mut parsed_bytes = 0usize;
for _ in 0..iterations {
let mut parser = Parser::from_string(script);
let program = parser
.parse_program()
.unwrap_or_else(|err| panic!("{name} should parse: {err}"));
parsed_bytes += black_box(program.body().len());
}
let elapsed = start.elapsed();
let total_bytes = script.len() * iterations;
println!(
"{name}: iterations={iterations} bytes_per_script={} total_bytes={total_bytes} elapsed_ms={} commands={parsed_bytes}",
script.len(),
elapsed.as_millis(),
);
}
fn main() {
let cases = [
("assignments-10k", repeated_assignments(10_000), 20),
("nested-loops", nested_loops(24, 8), 200),
("heredoc-fanout", heredoc_fanout(128, 8), 40),
];
for (name, script, iterations) in cases {
bench_case(name, &script, iterations);
}
}