#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
use rexact_syntax::{Dialect, Flags, Pattern};
use std::collections::BTreeMap;
const KNOWN_GAPS: &[(&str, &str, &str, &str)] = &[];
fn decode(hex: &str) -> String {
hex.split_whitespace()
.map(|h| char::from_u32(u32::from_str_radix(h, 16).expect("hex")).expect("scalar"))
.collect()
}
fn run_file(name: &str) -> (usize, usize, BTreeMap<String, Vec<String>>) {
let path = format!("{}/../../corpus/{name}", env!("CARGO_MANIFEST_DIR"));
let text = std::fs::read_to_string(path).expect("corpus readable");
let (mut total, mut unsupported) = (0usize, 0usize);
let mut failures: BTreeMap<String, Vec<String>> = BTreeMap::new();
for line in text
.lines()
.filter(|l| !l.starts_with('#') && !l.is_empty())
{
let cols: Vec<&str> = line.split('\t').collect();
let dialect = Dialect::from_name(cols[0]).expect("dialect");
let letters = cols[1];
let engine_accepts = cols[2] == "1";
let pattern = decode(cols.get(3).copied().unwrap_or(""));
let flags = Flags::from_letters(dialect, letters).expect("flags").0;
total += 1;
let key = format!("{}/{:?}", cols[0], letters);
let label = format!("{:?}", pattern);
match (engine_accepts, Pattern::parse(&pattern, dialect, flags)) {
(true, Ok(_)) => {}
(true, Err(e)) if e.code().starts_with("RX-U") => unsupported += 1,
(true, Err(e)) => failures.entry(key).or_default().push(format!(
"engine ACCEPTS, parser rejects {label}: {}",
e.code()
)),
(false, Err(_)) => {}
(false, Ok(_)) => {
let known = KNOWN_GAPS
.iter()
.any(|(d, f, p, _)| *d == cols[0] && *f == letters && *p == pattern);
if !known {
failures
.entry(key)
.or_default()
.push(format!("engine REJECTS, parser accepts {label}"));
}
}
}
}
(total, unsupported, failures)
}
fn check(name: &str, min_rows: usize) {
let (total, unsupported, failures) = run_file(name);
assert!(total > min_rows, "corpus unexpectedly small: {total}");
if !failures.is_empty() {
let n: usize = failures.values().map(Vec::len).sum();
let mut msg = format!("{n} disagreements out of {total} in {name}:\n");
for (k, v) in &failures {
msg.push_str(&format!("--- {k}: {} disagreements\n", v.len()));
for l in v.iter().take(25) {
msg.push_str(&format!("{l}\n"));
}
}
panic!("{msg}");
}
eprintln!("{name}: {total} rows agree; {unsupported} deliberately unsupported");
}
#[test]
fn parser_agrees_with_real_engines() {
check("syntax_accept.tsv", 100_000);
}
#[test]
fn go_rust_pcre2_parsers_agree_with_real_engines() {
check("syntax_accept_re2.tsv", 100_000);
}