diff --git a/crates/kind-cli/src/main.rs b/crates/kind-cli/src/main.rs index d45a9563..e2da1ca9 100644 --- a/crates/kind-cli/src/main.rs +++ b/crates/kind-cli/src/main.rs @@ -1,14 +1,12 @@ #![feature(panic_info_message)] use std::path::PathBuf; -use std::time::Instant; use std::{fmt, io}; use clap::{Parser, Subcommand}; -use driver::resolution::ResolutionError; use kind_driver::session::Session; -use kind_report::data::{Diagnostic, FileCache, Log, Severity}; +use kind_report::data::{FileCache, Log}; use kind_report::RenderConfig; use std::panic; @@ -143,7 +141,7 @@ where pub fn render_to_stderr(render_config: &RenderConfig, session: &T, err: &E) where T: FileCache, - E: Report, + E: Report + ?Sized, { Report::render( err, @@ -154,77 +152,16 @@ where .unwrap(); } -pub fn compile_in_session( +pub fn run_in_session( render_config: &RenderConfig, root: PathBuf, file: String, compiled: bool, - fun: &mut dyn FnMut(&mut Session) -> anyhow::Result, + action: &mut dyn FnMut(&mut Session) -> anyhow::Result, ) -> anyhow::Result { - 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::>>(); - - let mut contains_error = false; - - let mut hidden = 0; - let total = diagnostics.len() as u64; - - for diagnostic in diagnostics { - if diagnostic.get_severity() == Severity::Error { - contains_error = true; - } - - let is_root = diagnostic - .get_syntax_ctx() - .map(|x| x.is_root()) - .unwrap_or_default(); - - if render_config.only_main && !is_root { - hidden += 1; - continue; - } - - render_to_stderr(render_config, &session, &diagnostic) - } - - if !contains_error { - render_to_stderr( - render_config, - &session, - &if compiled { - Log::Compiled(start.elapsed()) - } else { - Log::Checked(start.elapsed()) - }, - ); - - eprintln!(); - - res - } else { - render_to_stderr(render_config, &session, &Log::Failed(start.elapsed(), total, hidden)); - eprintln!(); - - match res { - Ok(_) => Err(ResolutionError.into()), - Err(res) => Err(res), - } - } + let log = + |session: &Session, report: &dyn Report| render_to_stderr(render_config, session, report); + driver::run_in_session(render_config, root, file, compiled, action, &log) } pub fn run_cli(config: Cli) -> anyhow::Result<()> { @@ -254,7 +191,7 @@ pub fn run_cli(config: Cli) -> anyhow::Result<()> { match config.command { Command::Check { file, coverage } => { - compile_in_session(&render_config, root, file.clone(), false, &mut |session| { + run_in_session(&render_config, root, file.clone(), false, &mut |session| { let (_, rewrites) = driver::type_check_book( session, &PathBuf::from(file.clone()), @@ -270,7 +207,7 @@ pub fn run_cli(config: Cli) -> anyhow::Result<()> { } Command::ToHVM { file } => { let result = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { + run_in_session(&render_config, root, file.clone(), true, &mut |session| { let book = driver::erase_book( session, &PathBuf::from(file.clone()), @@ -282,22 +219,21 @@ pub fn run_cli(config: Cli) -> anyhow::Result<()> { println!("{}", result); } Command::Run { file } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - let path = PathBuf::from(file.clone()); - let book = driver::erase_book(session, &path, entrypoints.clone())?; - driver::check_main_entry(session, &book)?; - let book = driver::compile_book_to_hvm(book, config.trace); - let (result, rewrites) = driver::execute_file(&book.to_string(), config.tids)?; + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + let path = PathBuf::from(file.clone()); + let book = driver::erase_book(session, &path, entrypoints.clone())?; + driver::check_main_entry(session, &book)?; + let book = driver::compile_book_to_hvm(book, config.trace); + let (result, rewrites) = driver::execute_file(&book.to_string(), config.tids)?; - render_to_stderr(&render_config, session, &Log::Rewrites(rewrites)); + render_to_stderr(&render_config, session, &Log::Rewrites(rewrites)); - Ok(result) - })?; + Ok(result) + })?; println!("{}", res); } Command::Show { file } => { - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { + run_in_session(&render_config, root, file.clone(), true, &mut |session| { driver::to_book(session, &PathBuf::from(file.clone())) }) .map(|res| { @@ -306,49 +242,44 @@ pub fn run_cli(config: Cli) -> anyhow::Result<()> { })?; } Command::ToKindCore { file } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - driver::desugar_book(session, &PathBuf::from(file.clone())) - })?; + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + driver::desugar_book(session, &PathBuf::from(file.clone())) + })?; print!("{}", res); } Command::Erase { file } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - driver::erase_book(session, &PathBuf::from(file.clone()), entrypoints.clone()) - })?; + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + driver::erase_book(session, &PathBuf::from(file.clone()), entrypoints.clone()) + })?; print!("{}", res); } Command::GenChecker { file, coverage } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - driver::check_erasure_book(session, &PathBuf::from(file.clone())) - })?; + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + driver::check_erasure_book(session, &PathBuf::from(file.clone())) + })?; print!("{}", driver::generate_checker(&res, coverage)); } Command::Eval { file } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - let book = driver::desugar_book(session, &PathBuf::from(file.clone()))?; - driver::check_main_desugared_entry(session, &book)?; - let (res, rewrites) = driver::eval_in_checker(&book); + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + let book = driver::desugar_book(session, &PathBuf::from(file.clone()))?; + driver::check_main_desugared_entry(session, &book)?; + let (res, rewrites) = driver::eval_in_checker(&book); - render_to_stderr(&render_config, session, &Log::Rewrites(rewrites)); + render_to_stderr(&render_config, session, &Log::Rewrites(rewrites)); - Ok(res) - })?; + Ok(res) + })?; println!("{}", res); } Command::ToKDL { file, namespace } => { - let res = - compile_in_session(&render_config, root, file.clone(), true, &mut |session| { - driver::compile_book_to_kdl( - &PathBuf::from(file.clone()), - session, - &namespace.clone().unwrap_or("".to_string()), - entrypoints.clone(), - ) - })?; + let res = run_in_session(&render_config, root, file.clone(), true, &mut |session| { + driver::compile_book_to_kdl( + &PathBuf::from(file.clone()), + session, + &namespace.clone().unwrap_or("".to_string()), + entrypoints.clone(), + ) + })?; println!("{}", res); } } @@ -357,9 +288,12 @@ pub fn run_cli(config: Cli) -> anyhow::Result<()> { } pub fn main() { - panic::set_hook(Box::new(|e| { - println!("\n[Error]: internal compiler error '{:?}' at {}", e.message().unwrap(), e.location().unwrap()); + println!( + "\n[Error]: internal compiler error '{:?}' at {}", + e.message().unwrap(), + e.location().unwrap() + ); println!("Please submit a full report about this error at: https://github.com/HigherOrderCO/Kind/issues/new"); println!("It would help us a lot :)\n"); })); diff --git a/crates/kind-driver/src/lib.rs b/crates/kind-driver/src/lib.rs index 0efb3d81..c1e1b79c 100644 --- a/crates/kind-driver/src/lib.rs +++ b/crates/kind-driver/src/lib.rs @@ -1,14 +1,18 @@ use checker::eval; use diagnostic::{DriverDiagnostic, GenericDriverError}; use kind_pass::{desugar, erasure, inline::inline_book}; -use kind_report::data::FileCache; +use kind_report::{ + data::{FileCache, Log, Severity}, + report::Report, + RenderConfig, +}; use kind_span::SyntaxCtxIndex; -use hvm::language::{syntax as backend}; +use hvm::language::syntax as backend; use kind_tree::{concrete, desugared, untyped}; use resolution::ResolutionError; use session::Session; -use std::{path::PathBuf}; +use std::{path::PathBuf, time::Instant}; use kind_checker as checker; @@ -147,9 +151,7 @@ pub fn check_main_desugared_entry( pub fn execute_file(file: &str, tids: Option) -> anyhow::Result<(String, u64)> { match eval(file, "Main", false, tids) { - Ok((res, rewrites)) => { - Ok((res.to_string(), rewrites)) - }, + Ok((res, rewrites)) => Ok((res.to_string(), rewrites)), Err(_) => anyhow::Result::Err(GenericDriverError.into()), } } @@ -161,3 +163,69 @@ pub fn eval_in_checker(book: &desugared::Book) -> (String, u64) { pub fn generate_checker(book: &desugared::Book, check_coverage: bool) -> String { checker::gen_checker(book, check_coverage, book.entrs.keys().cloned().collect()) } + +pub fn run_in_session( + render_config: &RenderConfig, + root: PathBuf, + file: String, + compiled: bool, + action: &mut dyn FnMut(&mut Session) -> anyhow::Result, + log: &dyn Fn(&Session, &dyn Report), +) -> anyhow::Result { + let (rx, tx) = std::sync::mpsc::channel(); + + let mut session = Session::new(root, rx); + + log(&session, &Log::Empty); + log(&session, &Log::Checking(format!("The file '{}'", file))); + + let start = Instant::now(); + + let res = action(&mut session); + + let diagnostics = tx.try_iter().collect::>(); + + let mut contains_error = false; + + let mut hidden = 0; + let total = diagnostics.len() as u64; + + for diagnostic in diagnostics { + if diagnostic.get_severity() == Severity::Error { + contains_error = true; + } + + let is_root = diagnostic + .get_syntax_ctx() + .map(|x| x.is_root()) + .unwrap_or_default(); + + if render_config.only_main && !is_root { + hidden += 1; + continue; + } + log(&session, &diagnostic); + } + + if !contains_error { + log( + &session, + &if compiled { + Log::Compiled(start.elapsed()) + } else { + Log::Checked(start.elapsed()) + }, + ); + log(&session, &Log::Empty); + + res + } else { + log(&session, &Log::Failed(start.elapsed(), total, hidden)); + log(&session, &Log::Empty); + + match res { + Ok(_) => Err(ResolutionError.into()), + Err(res) => Err(res), + } + } +} diff --git a/crates/kind-report/src/data.rs b/crates/kind-report/src/data.rs index 2c1fb38b..8afe2de3 100644 --- a/crates/kind-report/src/data.rs +++ b/crates/kind-report/src/data.rs @@ -73,6 +73,7 @@ pub enum Log { Compiled(Duration), Rewrites(u64), Failed(Duration, u64, u64), + Empty, } pub trait Diagnostic { diff --git a/crates/kind-report/src/report/mode/classic.rs b/crates/kind-report/src/report/mode/classic.rs index 4f1f6d3c..cfb88d88 100644 --- a/crates/kind-report/src/report/mode/classic.rs +++ b/crates/kind-report/src/report/mode/classic.rs @@ -11,11 +11,11 @@ use std::fmt::Write; use std::path::PathBuf; use yansi::Paint; -fn colorize_code( +fn colorize_code( markers: &mut [&(Point, Point, &Marker)], code_line: &str, modify: &dyn Fn(&str) -> String, - fmt: &mut T, + fmt: &mut dyn Write, ) -> std::fmt::Result { markers.sort_by(|x, y| x.0.column.cmp(&y.0.column)); let mut start = 0; @@ -47,12 +47,12 @@ fn colorize_code( Ok(()) } -fn mark_inlined( +fn mark_inlined( prefix: &str, code: &str, config: &RenderConfig, inline_markers: &mut [&(Point, Point, &Marker)], - fmt: &mut T, + fmt: &mut dyn Write, ) -> std::fmt::Result { inline_markers.sort_by(|x, y| x.0.column.cmp(&y.0.column)); let mut start = 0; @@ -148,7 +148,7 @@ impl Color { } impl Renderable for Severity { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { use Severity::*; let painted = match self { @@ -162,7 +162,7 @@ impl Renderable for Severity { } impl<'a> Renderable for Header<'a> { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { Renderable::::render(self.severity, fmt, cache, config)?; fmt.write_str(&Paint::new(&self.title).bold().to_string())?; fmt.write_char('\n') @@ -170,7 +170,7 @@ impl<'a> Renderable for Header<'a> { } impl Renderable for Subtitle { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { match self { Subtitle::Normal(color, phr) | Subtitle::Field(color, phr) => { let bullet = color.colorize(config.chars.bullet); @@ -194,7 +194,7 @@ impl Renderable for Subtitle { } impl<'a> Renderable for Word { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { match self { Word::Normal(str) => write!(fmt, "{} ", Paint::new(str)), Word::Dimmed(str) => write!(fmt, "{} ", Paint::new(str).dimmed()), @@ -205,7 +205,7 @@ impl<'a> Renderable for Word { } impl<'a> Renderable for Subtitles<'a> { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { if !self.0.is_empty() { writeln!(fmt)?; } @@ -215,7 +215,7 @@ impl<'a> Renderable for Subtitles<'a> { } impl<'a> Renderable for CodeBlock<'a> { - fn render(&self, fmt: &mut U, _: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, config: &RenderConfig) -> Res { let guide = LineGuide::get(self.code); let point = guide.find(self.markers.0[0].position.start); @@ -342,7 +342,7 @@ impl<'a> Renderable for CodeBlock<'a> { } impl Renderable for Log { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { match self { Log::Checking(file) => { writeln!( @@ -390,12 +390,13 @@ impl Renderable for Log { u64 ) } + Log::Empty => writeln!(fmt, ""), } } } impl<'a> Renderable for Markers<'a> { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { let groups = group_markers(&self.0); let is_empty = groups.is_empty(); let current = PathBuf::from(".").canonicalize().unwrap(); @@ -424,7 +425,7 @@ impl<'a> Renderable for Markers<'a> { } impl<'a> Renderable for Hints<'a> { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { for hint in self.0 { writeln!( fmt, @@ -440,7 +441,7 @@ impl<'a> Renderable for Hints<'a> { } impl Renderable for DiagnosticFrame { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { write!(fmt, " ")?; Renderable::::render(&self.header(), fmt, cache, config)?; diff --git a/crates/kind-report/src/report/mode/compact.rs b/crates/kind-report/src/report/mode/compact.rs index cb19dde3..477d428c 100644 --- a/crates/kind-report/src/report/mode/compact.rs +++ b/crates/kind-report/src/report/mode/compact.rs @@ -7,10 +7,10 @@ use crate::{report::*, RenderConfig}; use super::{CodeBlock, Compact, Renderable, Res}; -fn mark_code( +fn mark_code( markers: &mut [&(Point, Point, &Marker)], code_line: &str, - fmt: &mut T, + fmt: &mut dyn Write, ) -> std::fmt::Result { markers.sort_by(|x, y| x.0.column.cmp(&y.0.column)); let mut start = 0; @@ -42,7 +42,7 @@ fn mark_code( } impl<'a> Renderable for Word { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { match self { Word::Normal(str) => write!(fmt, "{} ", str), Word::Dimmed(str) => write!(fmt, "{} ", str), @@ -53,7 +53,7 @@ impl<'a> Renderable for Word { } impl Renderable for Subtitle { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { match self { Subtitle::Field(_, phr) => writeln!(fmt, "{}", phr.to_lowercase()), Subtitle::Normal(_, phr) => writeln!(fmt, "- {}", phr), @@ -69,7 +69,7 @@ impl Renderable for Subtitle { } impl<'a> Renderable for Subtitles<'a> { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { if !self.0.is_empty() { writeln!(fmt)?; } @@ -79,7 +79,7 @@ impl<'a> Renderable for Subtitles<'a> { } impl Renderable for Severity { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { use Severity::*; let painted = match self { @@ -93,13 +93,13 @@ impl Renderable for Severity { } impl<'a> Renderable for Header<'a> { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { writeln!(fmt, "{}", &self.title.to_lowercase()) } } impl<'a> Renderable for CodeBlock<'a> { - fn render(&self, fmt: &mut U, _: &C, _8778: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { writeln!(fmt, "location")?; let guide = LineGuide::get(self.code); @@ -133,7 +133,7 @@ impl<'a> Renderable for CodeBlock<'a> { } impl<'a> Renderable for Markers<'a> { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { let groups = group_markers(&self.0); let current = PathBuf::from(".").canonicalize().unwrap(); @@ -155,12 +155,7 @@ impl<'a> Renderable for Markers<'a> { } impl Renderable for DiagnosticFrame { - fn render( - &self, - fmt: &mut U, - cache: &C, - config: &crate::RenderConfig, - ) -> super::Res { + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { Renderable::::render(&self.header(), fmt, cache, config)?; Renderable::::render(&self.subtitles(), fmt, cache, config)?; Renderable::::render(&self.markers(), fmt, cache, config)?; @@ -170,7 +165,7 @@ impl Renderable for DiagnosticFrame { } impl Renderable for Log { - fn render(&self, fmt: &mut U, _: &C, _: &RenderConfig) -> Res { + fn render(&self, fmt: &mut dyn Write, _: &dyn FileCache, _: &RenderConfig) -> Res { match self { Log::Compiled(_) => writeln!(fmt, "compiled"), Log::Checked(_) => writeln!(fmt, "checked"), diff --git a/crates/kind-report/src/report/mode/mod.rs b/crates/kind-report/src/report/mode/mod.rs index 4391565f..e1d9953b 100644 --- a/crates/kind-report/src/report/mode/mod.rs +++ b/crates/kind-report/src/report/mode/mod.rs @@ -1,7 +1,10 @@ use std::{fmt::Write, path::Path}; -use crate::{data::{FileCache, Diagnostic, DiagnosticFrame, Log}, RenderConfig}; use super::code::FileMarkers; +use crate::{ + data::{Diagnostic, DiagnosticFrame, FileCache, Log}, + RenderConfig, +}; pub mod classic; pub mod compact; @@ -35,17 +38,20 @@ pub enum Mode { pub(crate) struct CodeBlock<'a> { pub code: &'a str, pub path: &'a Path, - pub markers: &'a FileMarkers + pub markers: &'a FileMarkers, } /// A type class for renderable error reports and messages. It's useful /// to change easily things without problems. pub trait Renderable { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res; + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res; } -impl<'a, T, E> Renderable for Vec where E : Renderable { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { +impl<'a, T, E> Renderable for Vec +where + E: Renderable, +{ + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { for elem in self { elem.render(fmt, cache, config)?; } @@ -53,14 +59,20 @@ impl<'a, T, E> Renderable for Vec where E : Renderable { } } -impl Renderable for Box where DiagnosticFrame: Renderable { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { +impl Renderable for Box +where + DiagnosticFrame: Renderable, +{ + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { Renderable::::render(&self.to_diagnostic_frame(config), fmt, cache, config) } } -pub trait Report where Self : Renderable + Renderable { - fn render(&self, fmt: &mut U, cache: &C, config: &RenderConfig) -> Res { +pub trait Report +where + Self: Renderable + Renderable, +{ + fn render(&self, fmt: &mut dyn Write, cache: &dyn FileCache, config: &RenderConfig) -> Res { match config.mode { Mode::Classic => Renderable::::render(self, fmt, cache, config), Mode::Compact => Renderable::::render(self, fmt, cache, config), @@ -69,4 +81,4 @@ pub trait Report where Self : Renderable + Renderable { } impl Report for Box {} -impl Report for Log {} \ No newline at end of file +impl Report for Log {}