refactor: started to refactor in order to make tests

This commit is contained in:
Felipe g 2022-11-15 08:14:40 -03:00
parent 856343b1cf
commit c7f9db59ba
7 changed files with 304 additions and 232 deletions

View File

@ -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"

View File

@ -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"

230
src/kind-cli/src/lib.rs Normal file
View File

@ -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<PathBuf>,
/// 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<String>,
},
/// 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<T>(pub T);
impl<T> fmt::Write for ToWriteFmt<T>
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<T, E>(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<T>(
render_config: RenderConfig,
root: PathBuf,
file: String,
fun: &mut dyn FnMut(&mut Session) -> Option<T>,
) -> Option<T> {
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::<Vec<DiagnosticFrame>>();
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!(),
}
}

View File

@ -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<PathBuf>,
/// 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<String>,
},
/// 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<T>(pub T);
impl<T> fmt::Write for ToWriteFmt<T>
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<T, E>(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<T>(
render_config: RenderConfig,
root: PathBuf,
file: String,
fun: &mut dyn FnMut(&mut Session) -> Option<T>,
) -> Option<T> {
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::<Vec<DiagnosticFrame>>();
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())
}

53
src/kind-cli/tests/mod.rs Normal file
View File

@ -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(())
}

View File

@ -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;
}

View File

@ -1,5 +1,4 @@
//! Crate to parse the kind2 grammar.
pub mod errors;
pub mod expr;
pub mod macros;