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, but saw: {{:?}}\", 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::::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(()) }