use rex::{
engine::{Engine, Module},
parser::parse,
typesystem::{TypeSystem, infer},
};
#[derive(Clone)]
struct XorShift64 {
state: u64,
}
impl XorShift64 {
fn new(seed: u64) -> Self {
Self { state: seed }
}
fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
fn gen_range(&mut self, lo: usize, hi: usize) -> usize {
let span = hi.saturating_sub(lo).max(1);
lo + (self.next_u64() as usize % span)
}
}
#[tokio::test]
async fn fuzz_smoke_pipeline_does_not_panic() {
let iters: usize = std::env::var("REX_FUZZ_ITERS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(200);
let charset: &[char] = &[
' ', '\n', '\t', 'a', 'b', 'c', 'x', 'y', 'z', 'A', 'B', 'C', '_', '0', '1', '2', '3', '(',
')', '[', ']', '{', '}', ',', ':', '=', '\\', '-', '>', '+', '*', '/', '%', '<', '>', '.',
'"', '\'', '|', '&', 'λ', '→',
];
let mut rng = XorShift64::new(0x7265_785f_6675_7a7a);
for _ in 0..iters {
let len = rng.gen_range(0, 250);
let mut s = String::with_capacity(len);
for _ in 0..len {
let idx = rng.gen_range(0, charset.len());
s.push(charset[idx]);
}
let input = s.clone();
let program = match parse(&input) {
Ok(p) => p,
Err(_) => continue,
};
let mut ts = TypeSystem::new_with_prelude().unwrap();
let _ = ts.register_decls(&program.decls);
let Some(body) = program.body.as_ref() else {
continue;
};
let _ = infer(&mut ts, body.as_ref());
let mut engine = Engine::with_prelude(()).unwrap();
let mut module = Module::global();
module.add_decls(program.decls.clone());
let _ = engine.inject_module(module);
let _ = engine.into_evaluator().eval(body.as_ref()).await;
}
}