104 lines
4.3 KiB
Rust
104 lines
4.3 KiB
Rust
extern crate lalrpop;
|
|
|
|
use std::env;
|
|
use std::fs::{self, File};
|
|
use std::io::Write;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
fn main() {
|
|
lalrpop::process_root().unwrap();
|
|
if let Err(e) = generate_example_tests() {
|
|
eprintln!("Failure building example tests: {}", e);
|
|
std::process::exit(3);
|
|
}
|
|
}
|
|
|
|
fn generate_example_tests() -> std::io::Result<()> {
|
|
let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR");
|
|
let dest_path = Path::new(&out_dir).join("examples.rs");
|
|
let mut output = File::create(dest_path)?;
|
|
|
|
generate_tests(&mut output, PathBuf::from("examples"))?;
|
|
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
println!("cargo:rerun-if-changed=example.rs");
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> {
|
|
for entry in fs::read_dir(path_so_far)? {
|
|
let entry = entry?;
|
|
let file_name = entry
|
|
.file_name()
|
|
.into_string()
|
|
.expect("reasonable file name");
|
|
let name = file_name.trim_end_matches(".ngr");
|
|
|
|
if entry.file_type()?.is_dir() {
|
|
writeln!(f, "mod {} {{", name)?;
|
|
writeln!(f, " use super::*;")?;
|
|
generate_tests(f, entry.path())?;
|
|
writeln!(f, "}}")?;
|
|
} else {
|
|
writeln!(f, "#[test]")?;
|
|
writeln!(f, "fn {}() {{", name)?;
|
|
writeln!(f, " let mut file_database = SimpleFiles::new();")?;
|
|
writeln!(
|
|
f,
|
|
" let syntax = Syntax::parse_file(&mut file_database, {:?});",
|
|
entry.path().display()
|
|
)?;
|
|
if entry.path().to_string_lossy().contains("broken") {
|
|
writeln!(f, " if syntax.is_err() {{")?;
|
|
writeln!(f, " return;")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " let (errors, _) = syntax.unwrap().validate();")?;
|
|
writeln!(
|
|
f,
|
|
" assert_ne!(errors.len(), 0, \"should have seen an error\");"
|
|
)?;
|
|
} else {
|
|
// NOTE: Since the advent of defaulting rules and type checking, we
|
|
// can't guarantee that syntax.eval() will return the same result as
|
|
// ir.eval() or backend::eval(). We must do type checking to force
|
|
// constants into the right types, first. So this now checks only that
|
|
// the result of ir.eval() and backend::eval() are the same.
|
|
writeln!(
|
|
f,
|
|
" let syntax = syntax.expect(\"file should have parsed\");"
|
|
)?;
|
|
writeln!(f, " let (errors, _) = syntax.validate();")?;
|
|
writeln!(
|
|
f,
|
|
" assert_eq!(errors.len(), 0, \"file should have no validation errors\");"
|
|
)?;
|
|
writeln!(f, " let syntax_result = syntax.eval();")?;
|
|
writeln!(
|
|
f,
|
|
" let ir = syntax.type_infer().expect(\"example is typed correctly\");"
|
|
)?;
|
|
writeln!(f, " let ir_result = ir.eval();")?;
|
|
writeln!(f, " match (&syntax_result, &ir_result) {{")?;
|
|
writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?;
|
|
writeln!(f, " (Ok((v1, o1)), Ok((v2, o2))) => {{")?;
|
|
writeln!(f, " assert_eq!(v1, v2);")?;
|
|
writeln!(f, " assert_eq!(o1, o2);")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " _ => panic!(\"mismatched outputs, {{:?}} and {{:?}}\", syntax_result, ir_result)")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " let compiled_result = Backend::<JITModule>::eval(ir);")?;
|
|
writeln!(f, " match (&compiled_result, &ir_result) {{")?;
|
|
writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?;
|
|
writeln!(f, " (Ok(o1), Ok((_, o2))) => {{")?;
|
|
writeln!(f, " assert_eq!(o1, o2);")?;
|
|
writeln!(f, " }}")?;
|
|
writeln!(f, " _ => panic!(\"mismatched outputs, {{:?}} and {{:?}}\", compiled_result, ir_result)")?;
|
|
writeln!(f, " }}")?;
|
|
}
|
|
writeln!(f, "}}")?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|