use regexr::hir::translate;
use regexr::parser::parse;
const CL100K: &str = r"'(?i:[sdmt]|ll|ve|re)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s+$|\s*[\r\n]|\s+(?!\S)|\s";
const O200K: &str = r"[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?|[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n/]*|\s*[\r\n]+|\s+(?!\S)|\s+";
const PATTERNS: &[&str] = &[
r"\s+\s+",
r"\s+(?!\S)",
r"\s+(?!\S)|\s",
r"\s*[\r\n]",
r"\s+$",
r"world$",
r"^[a-z]+$",
r"\d{1,3}$",
r"x\Z",
r"x\z",
r"[a-z]+\n$",
r"\p{L}+$",
r"[^\s]+$",
r"\p{N}+$",
r"\p{L}*$",
r"[^\s]*$",
r"\p{L}+\p{N}",
r"[^\s]+(?!a)",
r"\s+(?=\S)",
r"a+(?!\S)",
r"\w+\s+error\s+\w+",
r"\s*",
r" *",
r"\S+?x",
r"\s+?\S",
r"\p{L}+?\p{N}",
r"\s*?[\r\n]",
r"[^\s]*?\S",
r"\p{L}*?\p{L}",
r"\p{L}{1,3}\s+?\S",
r"(\p{L}+)\s\1",
r"(\S)\1",
CL100K,
O200K,
];
fn inputs() -> Vec<String> {
let alphabet: &[char] = &[
'a',
'Z',
'0',
'9',
' ',
'\n',
'\r',
'\t',
'.',
',',
'!',
'\'',
'/',
'\u{00A0}',
'\u{2003}',
'\u{3000}',
'\u{00E9}',
'\u{4E2D}',
'\u{1F600}',
'\u{00B2}',
'x',
'e',
'r',
];
let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let mut out = vec![
String::new(),
" ".into(),
" \n".into(),
" x".into(),
"hello world".into(),
"中文 \n test".into(),
];
for _ in 0..4000 {
let len = (next() % 24) as usize;
let s: String = (0..len)
.map(|_| alphabet[(next() as usize) % alphabet.len()])
.collect();
out.push(s);
}
out
}
#[test]
fn engines_match_reference() {
let inputs = inputs();
let mut failures = Vec::new();
for &pat in PATTERNS {
let hir = parse(pat)
.and_then(|ast| translate(&ast))
.expect("pattern should compile");
let ncaps = hir.props.capture_count as usize;
let jit = regexr::RegexBuilder::new(pat).jit(true).build().unwrap();
let interp = regexr::RegexBuilder::new(pat).jit(false).build().unwrap();
for s in &inputs {
let bytes = s.as_bytes();
let expected = regexr::reference::find(&hir.expr, ncaps, bytes);
let j = jit.find(s).map(|m| (m.start(), m.end()));
let n = interp.find(s).map(|m| (m.start(), m.end()));
if j != expected || n != expected {
failures.push(format!(
"pat={pat:?} input={s:?}: ref={expected:?} jit={j:?} interp={n:?}"
));
if failures.len() > 20 {
break;
}
}
}
}
assert!(
failures.is_empty(),
"engine/reference divergences ({}):\n{}",
failures.len(),
failures.join("\n")
);
}