use std::io::{BufReader, Cursor};
use std::process::Command;
use awk_rs::{Interpreter, Lexer, Parser};
fn gawk_available() -> bool {
Command::new("gawk")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn run_awk_rs(program: &str, input: &str) -> String {
let mut lexer = Lexer::new(program);
let tokens = lexer.tokenize().unwrap();
let mut parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let mut interpreter = Interpreter::new(&ast);
let mut output = Vec::new();
if input.is_empty() {
let inputs: Vec<BufReader<Cursor<&str>>> = vec![];
interpreter.run(inputs, &mut output).unwrap();
} else {
let inputs = vec![BufReader::new(Cursor::new(input))];
interpreter.run(inputs, &mut output).unwrap();
}
String::from_utf8(output).unwrap()
}
fn run_gawk(program: &str, input: &str) -> Option<String> {
let mut cmd = Command::new("gawk");
cmd.arg(program);
if !input.is_empty() {
use std::io::Write;
use std::process::Stdio;
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn().ok()?;
{
let stdin = child.stdin.as_mut()?;
stdin.write_all(input.as_bytes()).ok()?;
}
let output = child.wait_with_output().ok()?;
String::from_utf8(output.stdout).ok()
} else {
let output = cmd.output().ok()?;
String::from_utf8(output.stdout).ok()
}
}
fn compare_with_gawk(program: &str, input: &str) {
if !gawk_available() {
eprintln!("Skipping gawk comparison test (gawk not available)");
return;
}
let awk_rs_output = run_awk_rs(program, input);
let gawk_output = run_gawk(program, input).expect("Failed to run gawk");
assert_eq!(
awk_rs_output, gawk_output,
"Output mismatch for program: {}\nInput: {:?}\nawk-rs output: {:?}\ngawk output: {:?}",
program, input, awk_rs_output, gawk_output
);
}
#[test]
fn compat_hello_world() {
compare_with_gawk(r#"BEGIN { print "Hello, World!" }"#, "");
}
#[test]
fn compat_arithmetic() {
compare_with_gawk("BEGIN { print 1+2, 3*4, 10/2, 7%3, 2^8 }", "");
}
#[test]
fn compat_variables() {
compare_with_gawk("BEGIN { x = 5; y = 3; print x + y, x - y, x * y }", "");
}
#[test]
fn compat_field_access() {
compare_with_gawk("{ print $1, $2, $NF }", "one two three four");
}
#[test]
fn compat_field_separator() {
compare_with_gawk(r#"BEGIN { FS = ":" } { print $1, $3 }"#, "root:x:0:0:root");
}
#[test]
fn compat_nr_nf() {
compare_with_gawk("{ print NR, NF, $0 }", "a b c\nd e\nf");
}
#[test]
fn compat_for_loop() {
compare_with_gawk("BEGIN { for (i = 1; i <= 5; i++) print i }", "");
}
#[test]
fn compat_while_loop() {
compare_with_gawk("BEGIN { i = 0; while (i < 5) { print i; i++ } }", "");
}
#[test]
fn compat_if_else() {
compare_with_gawk(
r#"BEGIN { for (i = 1; i <= 5; i++) { if (i % 2 == 0) { print i, "even" } else { print i, "odd" } } }"#,
"",
);
}
#[test]
fn compat_arrays() {
compare_with_gawk(
"BEGIN { a[1] = 10; a[2] = 20; a[3] = 30; for (i = 1; i <= 3; i++) print a[i] }",
"",
);
}
#[test]
fn compat_length() {
compare_with_gawk(r#"BEGIN { print length("hello world") }"#, "");
}
#[test]
fn compat_substr() {
compare_with_gawk(r#"BEGIN { print substr("hello world", 1, 5) }"#, "");
compare_with_gawk(r#"BEGIN { print substr("hello world", 7) }"#, "");
}
#[test]
fn compat_index() {
compare_with_gawk(r#"BEGIN { print index("hello world", "wor") }"#, "");
compare_with_gawk(r#"BEGIN { print index("hello world", "xyz") }"#, "");
}
#[test]
fn compat_split() {
compare_with_gawk(r#"BEGIN { FS = ":"; } { print $1, $2, $3, $4 }"#, "a:b:c:d");
}
#[test]
fn compat_tolower_toupper() {
compare_with_gawk(r#"BEGIN { print tolower("HeLLo WoRLD") }"#, "");
compare_with_gawk(r#"BEGIN { print toupper("HeLLo WoRLD") }"#, "");
}
#[test]
fn compat_gsub() {
compare_with_gawk(r#"{ gsub("a", "X"); print }"#, "banana");
}
#[test]
fn compat_sub() {
compare_with_gawk(r#"{ sub("a", "X"); print }"#, "banana");
}
#[test]
fn compat_printf() {
compare_with_gawk(
r#"BEGIN { printf "%d %s %.2f\n", 42, "test", 3.14159 }"#,
"",
);
compare_with_gawk(r#"BEGIN { printf "%05d\n", 42 }"#, "");
compare_with_gawk(r#"BEGIN { printf "%-10s|\n", "hi" }"#, "");
}
#[test]
fn compat_math() {
compare_with_gawk("BEGIN { print int(3.7), int(-3.7) }", "");
compare_with_gawk("BEGIN { print sqrt(16) }", "");
}
#[test]
fn compat_regex_pattern() {
compare_with_gawk("/error/ { print }", "info\nerror\nwarning\nerror");
}
#[test]
fn compat_expression_pattern() {
compare_with_gawk("$1 > 2 { print }", "1 a\n2 b\n3 c\n4 d");
}
#[test]
fn compat_range_pattern() {
compare_with_gawk("/start/,/end/ { print }", "before\nstart\nmid\nend\nafter");
}
#[test]
fn compat_function() {
compare_with_gawk(
"function square(x) { return x * x } BEGIN { print square(5) }",
"",
);
}
#[test]
fn compat_recursion() {
compare_with_gawk(
"function fact(n) { return n <= 1 ? 1 : n * fact(n-1) } BEGIN { print fact(6) }",
"",
);
}
#[test]
fn compat_ternary() {
compare_with_gawk(
r#"BEGIN { for (i = 1; i <= 5; i++) print i, (i % 2 == 0 ? "even" : "odd") }"#,
"",
);
}
#[test]
fn compat_increment() {
compare_with_gawk("BEGIN { x = 5; print x++, x, ++x, x }", "");
}
#[test]
fn compat_compound_assignment() {
compare_with_gawk("BEGIN { x = 10; x += 5; x -= 2; x *= 3; print x }", "");
}
#[test]
fn compat_ofs() {
compare_with_gawk(r#"BEGIN { OFS = "," } { print $1, $2, $3 }"#, "a b c");
}
#[test]
fn compat_modify_field() {
compare_with_gawk(r#"BEGIN { OFS = ":" } { $2 = "X"; print }"#, "a b c");
}
#[test]
fn compat_sum() {
compare_with_gawk("{ sum += $1 } END { print sum }", "1\n2\n3\n4\n5");
}
#[test]
fn compat_count_pattern() {
compare_with_gawk("/x/ { count++ } END { print count+0 }", "a\nx\nb\nxx\nc");
}
#[test]
fn compat_word_frequency() {
if !gawk_available() {
return;
}
let program =
"{ for (i=1; i<=NF; i++) count[$i]++ } END { for (w in count) print w, count[w] }";
let input = "a b a c b a";
let awk_rs_output = run_awk_rs(program, input);
let gawk_output = run_gawk(program, input).unwrap();
assert_eq!(awk_rs_output.lines().count(), gawk_output.lines().count());
}
#[test]
fn compat_next() {
compare_with_gawk("/skip/ { next } { print }", "a\nskip\nb\nskip\nc");
}
#[test]
fn compat_break_continue() {
compare_with_gawk(
"BEGIN { for (i=1; i<=10; i++) { if (i==3) continue; if (i==7) break; print i } }",
"",
);
}