From c7f9db59ba575d1d75b04ca89d3d6a351b7b5e98 Mon Sep 17 00:00:00 2001 From: Felipe g Date: Tue, 15 Nov 2022 08:14:40 -0300 Subject: [PATCH] refactor: started to refactor in order to make tests --- src/kind-checker/Cargo.toml | 2 +- src/kind-cli/Cargo.toml | 6 + src/kind-cli/src/lib.rs | 230 +++++++++++++++++++++++++++++ src/kind-cli/src/main.rs | 235 +----------------------------- src/kind-cli/tests/mod.rs | 53 +++++++ src/kind-driver/src/resolution.rs | 9 ++ src/kind-parser/src/lib.rs | 1 - 7 files changed, 304 insertions(+), 232 deletions(-) create mode 100644 src/kind-cli/src/lib.rs create mode 100644 src/kind-cli/tests/mod.rs diff --git a/src/kind-checker/Cargo.toml b/src/kind-checker/Cargo.toml index c23106bf..c081d6cc 100644 --- a/src/kind-checker/Cargo.toml +++ b/src/kind-checker/Cargo.toml @@ -9,6 +9,6 @@ edition = "2021" kind-tree = { path = "../kind-tree" } kind-span = { path = "../kind-span" } kind-report = { path = "../kind-report" } -hvm = "0.1.81" +hvm = "0.1.89" fxhash = "0.2.1" im = "15.1.0" \ No newline at end of file diff --git a/src/kind-cli/Cargo.toml b/src/kind-cli/Cargo.toml index e291e59a..63c8d272 100644 --- a/src/kind-cli/Cargo.toml +++ b/src/kind-cli/Cargo.toml @@ -19,3 +19,9 @@ kind-report = { path = "../kind-report" } kind-checker = { path = "../kind-checker" } clap = { version = "4.0.10", features = ["derive"] } + +[dev-dependencies] + +pretty_assertions = "1.3.0" +ntest = "0.8.1" +walkdir = "2" diff --git a/src/kind-cli/src/lib.rs b/src/kind-cli/src/lib.rs new file mode 100644 index 00000000..410de237 --- /dev/null +++ b/src/kind-cli/src/lib.rs @@ -0,0 +1,230 @@ +use std::path::PathBuf; +use std::time::Instant; +use std::{fmt, io}; + +use clap::{Parser, Subcommand}; +use kind_driver::session::Session; +use kind_report::data::{Diagnostic, DiagnosticFrame, Log}; +use kind_report::report::{FileCache, Report}; +use kind_report::RenderConfig; + +use kind_driver as driver; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +pub struct Cli { + /// Configuration file to change information about + /// pretty printing or project root. + #[arg(short, long, value_name = "FILE")] + pub config: Option, + + /// Turn on the debugging information generated + /// by the compiler. + #[arg(short, long)] + pub debug: bool, + + /// Show warning messages + #[arg(short, long)] + pub warning: bool, + + /// Disable colors in error messages + #[arg(short, long)] + pub no_color: bool, + + /// Only ascii characters in error messages + #[arg(short, long)] + pub ascii: bool, + + #[command(subcommand)] + pub command: Command, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + /// Check a file + #[clap(aliases = &["c"])] + Check { file: String }, + + /// Evaluates Main on Kind2 + #[clap(aliases = &["e"])] + Eval { file: String }, + + #[clap(aliases = &["k"])] + ToKindCore { file : String }, + + /// Runs Main on the HVM + #[clap(aliases = &["r"])] + Run { file: String }, + + /// Generates a checker (.hvm) for a file + #[clap(aliases = &["gc"])] + GenChecker { file: String }, + + /// Stringifies a file + #[clap(aliases = &["s"])] + Show { file: String }, + + /// Compiles a file to Kindelia (.kdl) + #[clap(aliases = &["kdl"])] + ToKDL { + file: String, + /// If given, a namespace that goes before each compiled name. Can be at most 10 charaters long. + #[clap(long, aliases = &["ns"])] + namespace: Option, + }, + + /// Compiles a file to HVM (.hvm) + #[clap(aliases = &["hvm"])] + ToHVM { file: String }, + + /// Watch for file changes and then + /// check when some file change. + #[clap(aliases = &["w"])] + Watch { file: String }, + + /// Read eval print loop + #[clap(aliases = &["re"])] + Repl, +} + +/// Helper structure to use stderr as fmt::Write +struct ToWriteFmt(pub T); + +impl fmt::Write for ToWriteFmt +where + T: io::Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error) + } +} + +pub fn render_to_stderr(render_config: &RenderConfig, session: &T, err: &E) +where + T: FileCache, + E: Report, +{ + Report::render( + err, + session, + render_config, + &mut ToWriteFmt(std::io::stderr()), + ) + .unwrap(); +} + +pub fn compile_in_session( + render_config: RenderConfig, + root: PathBuf, + file: String, + fun: &mut dyn FnMut(&mut Session) -> Option, +) -> Option { + let (rx, tx) = std::sync::mpsc::channel(); + + let mut session = Session::new(root, rx); + + eprintln!(); + + render_to_stderr( + &render_config, + &session, + &Log::Checking(format!("the file '{}'", file)), + ); + + let start = Instant::now(); + + let res = fun(&mut session); + + let diagnostics = tx.try_iter().collect::>(); + + if diagnostics.is_empty() && res.is_some() { + render_to_stderr(&render_config, &session, &Log::Checked(start.elapsed())); + eprintln!(); + res + } else { + render_to_stderr(&render_config, &session, &Log::Failed(start.elapsed())); + eprintln!(); + for diagnostic in diagnostics { + let diagnostic: Diagnostic = (&diagnostic).into(); + render_to_stderr(&render_config, &session, &diagnostic) + } + None + } +} + +pub fn run_cli(config: Cli) { + + kind_report::check_if_colors_are_supported(config.no_color); + + let render_config = kind_report::check_if_utf8_is_supported(config.ascii, 2); + let root = PathBuf::from("."); + + match config.command { + Command::Check { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::type_check_book(session, &PathBuf::from(file.clone())) + }); + } + Command::ToHVM { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::compile_book_to_hvm(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + println!("{}", res); + res + }); + } + Command::Run { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::compile_book_to_hvm(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + println!("{}", driver::execute_file(&res)); + res + }); + } + Command::Eval { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::erase_book(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + println!("{}", driver::eval_in_checker(&res)); + res + }); + } + Command::Show { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::to_book(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + print!("{}", res); + res + }); + } + Command::ToKindCore { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::desugar_book(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + print!("{}", res); + res + }); + } + Command::GenChecker { file } => { + compile_in_session(render_config, root, file.clone(), &mut |session| { + driver::check_erasure_book(session, &PathBuf::from(file.clone())) + }) + .map(|res| { + print!("{}", driver::generate_checker(&res)); + res + }); + } + Command::ToKDL { + file: _, + namespace: _, + } => todo!(), + Command::Watch { file: _ } => todo!(), + Command::Repl => todo!(), + } +} diff --git a/src/kind-cli/src/main.rs b/src/kind-cli/src/main.rs index b73ac8a3..e2be27e2 100644 --- a/src/kind-cli/src/main.rs +++ b/src/kind-cli/src/main.rs @@ -1,231 +1,6 @@ -use std::path::PathBuf; -use std::time::Instant; -use std::{fmt, io}; +use clap::Parser; +use kind_cli::{run_cli, Cli}; -use clap::{Parser, Subcommand}; -use kind_driver::session::Session; -use kind_report::data::{Diagnostic, DiagnosticFrame, Log}; -use kind_report::report::{FileCache, Report}; -use kind_report::RenderConfig; - -use kind_driver as driver; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -#[clap(propagate_version = true)] -struct Cli { - /// Configuration file to change information about - /// pretty printing or project root. - #[arg(short, long, value_name = "FILE")] - config: Option, - - /// Turn on the debugging information generated - /// by the compiler. - #[arg(short, long)] - debug: bool, - - /// Show warning messages - #[arg(short, long)] - warning: bool, - - /// Disable colors in error messages - #[arg(short, long)] - no_color: bool, - - /// Only ascii characters in error messages - #[arg(short, long)] - ascii: bool, - - #[command(subcommand)] - command: Command, -} - -#[derive(Subcommand, Debug)] -enum Command { - /// Check a file - #[clap(aliases = &["c"])] - Check { file: String }, - - /// Evaluates Main on Kind2 - #[clap(aliases = &["e"])] - Eval { file: String }, - - #[clap(aliases = &["k"])] - ToKindCore { file : String }, - - /// Runs Main on the HVM - #[clap(aliases = &["r"])] - Run { file: String }, - - /// Generates a checker (.hvm) for a file - #[clap(aliases = &["gc"])] - GenChecker { file: String }, - - /// Stringifies a file - #[clap(aliases = &["s"])] - Show { file: String }, - - /// Compiles a file to Kindelia (.kdl) - #[clap(aliases = &["kdl"])] - ToKDL { - file: String, - /// If given, a namespace that goes before each compiled name. Can be at most 10 charaters long. - #[clap(long, aliases = &["ns"])] - namespace: Option, - }, - - /// Compiles a file to HVM (.hvm) - #[clap(aliases = &["hvm"])] - ToHVM { file: String }, - - /// Watch for file changes and then - /// check when some file change. - #[clap(aliases = &["w"])] - Watch { file: String }, - - /// Read eval print loop - #[clap(aliases = &["re"])] - Repl, -} - -/// Helper structure to use stderr as fmt::Write -struct ToWriteFmt(pub T); - -impl fmt::Write for ToWriteFmt -where - T: io::Write, -{ - fn write_str(&mut self, s: &str) -> fmt::Result { - self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error) - } -} - -pub fn render_to_stderr(render_config: &RenderConfig, session: &T, err: &E) -where - T: FileCache, - E: Report, -{ - Report::render( - err, - session, - render_config, - &mut ToWriteFmt(std::io::stderr()), - ) - .unwrap(); -} - -pub fn compile_in_session( - render_config: RenderConfig, - root: PathBuf, - file: String, - fun: &mut dyn FnMut(&mut Session) -> Option, -) -> Option { - let (rx, tx) = std::sync::mpsc::channel(); - - let mut session = Session::new(root, rx); - - eprintln!(); - - render_to_stderr( - &render_config, - &session, - &Log::Checking(format!("the file '{}'", file)), - ); - - let start = Instant::now(); - - let res = fun(&mut session); - - let diagnostics = tx.try_iter().collect::>(); - - if diagnostics.is_empty() && res.is_some() { - render_to_stderr(&render_config, &session, &Log::Checked(start.elapsed())); - eprintln!(); - res - } else { - render_to_stderr(&render_config, &session, &Log::Failed(start.elapsed())); - eprintln!(); - for diagnostic in diagnostics { - let diagnostic: Diagnostic = (&diagnostic).into(); - render_to_stderr(&render_config, &session, &diagnostic) - } - None - } -} - -fn main() { - let config = Cli::parse(); - - kind_report::check_if_colors_are_supported(config.no_color); - - let render_config = kind_report::check_if_utf8_is_supported(config.ascii, 2); - let root = PathBuf::from("."); - - match config.command { - Command::Check { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::type_check_book(session, &PathBuf::from(file.clone())) - }); - } - Command::ToHVM { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::compile_book_to_hvm(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - println!("{}", res); - res - }); - } - Command::Run { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::compile_book_to_hvm(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - println!("{}", driver::execute_file(&res)); - res - }); - } - Command::Eval { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::erase_book(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - println!("{}", driver::eval_in_checker(&res)); - res - }); - } - Command::Show { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::to_book(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - print!("{}", res); - res - }); - } - Command::ToKindCore { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::desugar_book(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - print!("{}", res); - res - }); - } - Command::GenChecker { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { - driver::check_erasure_book(session, &PathBuf::from(file.clone())) - }) - .map(|res| { - print!("{}", driver::generate_checker(&res)); - res - }); - } - Command::ToKDL { - file: _, - namespace: _, - } => todo!(), - Command::Watch { file: _ } => todo!(), - Command::Repl => todo!(), - } -} +pub fn main() { + run_cli(Cli::parse()) +} \ No newline at end of file diff --git a/src/kind-cli/tests/mod.rs b/src/kind-cli/tests/mod.rs new file mode 100644 index 00000000..34bde0b3 --- /dev/null +++ b/src/kind-cli/tests/mod.rs @@ -0,0 +1,53 @@ +use kind_cli::{run_cli, Cli}; + +use ntest::timeout; +use pretty_assertions::assert_eq; +use std::fs::{self, File}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use walkdir::{Error, WalkDir}; + +fn golden_test(path: &Path, run: fn(&Path) -> String) { + let result = run(path); + + let golden_path = path.with_extension("golden"); + if let Ok(to_check) = fs::read_to_string(golden_path.clone()) { + assert_eq!(result, to_check, "Testing file '{}'", path.display()); + } else { + let mut file = File::create(golden_path).unwrap(); + file.write_all(result.as_bytes()).unwrap(); + } +} + +fn test_kind2(path: &Path, run: fn(&Path) -> String) -> Result<(), Error> { + for entry in WalkDir::new(path).follow_links(true) { + let entry = entry?; + let path = entry.path(); + if path.is_file() && path.extension().map(|x| x == "kind2").unwrap_or(false) { + golden_test(path, run); + } + } + Ok(()) +} + + +#[test] +#[timeout(15000)] +fn test_checker() -> Result<(), Error> { + test_kind2(Path::new("./tests/suite/checker"), |path| { + let config = Cli { + ascii: true, + no_color: true, + config: None, + command: kind_cli::Command::Check { file: path.to_str().unwrap().to_string() }, + debug: false, + warning: true, + }; + + // let (rx, tx) = std::sync::mpsc::channel(); + let root = PathBuf::from("."); + run_cli(config); + todo!() + })?; + Ok(()) +} diff --git a/src/kind-driver/src/resolution.rs b/src/kind-driver/src/resolution.rs index 1290a814..d73067e3 100644 --- a/src/kind-driver/src/resolution.rs +++ b/src/kind-driver/src/resolution.rs @@ -165,6 +165,15 @@ fn parse_and_store_book_by_path<'a>( path: &PathBuf, book: &'a mut Book, ) -> bool { + + if !path.exists() { + session + .diagnostic_sender + .send(DriverError::CannotFindFile(path.to_str().unwrap().to_string()).into()) + .unwrap(); + return true; + } + if session.loaded_paths_map.contains_key(&fs::canonicalize(path).unwrap()) { return false; } diff --git a/src/kind-parser/src/lib.rs b/src/kind-parser/src/lib.rs index df8bebb9..9e59727d 100644 --- a/src/kind-parser/src/lib.rs +++ b/src/kind-parser/src/lib.rs @@ -1,5 +1,4 @@ //! Crate to parse the kind2 grammar. - pub mod errors; pub mod expr; pub mod macros;