From bbbfed791378ee614909a575d3653ed8885a902f Mon Sep 17 00:00:00 2001 From: Erin Date: Thu, 21 Jul 2022 13:03:47 +0200 Subject: [PATCH] Improved error reports --- Cargo.lock | 16 ++++++++++ Cargo.toml | 5 ++-- src/error.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 14 +++++---- 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index bb72475..4a000d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "ariadne" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c" +dependencies = [ + "yansi", +] + [[package]] name = "atomic_refcell" version = "0.1.8" @@ -1556,6 +1565,7 @@ dependencies = [ name = "web-lisp" version = "0.1.0" dependencies = [ + "ariadne", "chumsky", "eframe", "logos", @@ -1745,3 +1755,9 @@ name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index c4d6189..decf283 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -logos = "*" -eframe = "*" +ariadne = "0.1" chumsky = "0.8" +eframe = "*" +logos = "*" ordered-float = "3.0" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..ce0ba7a --- /dev/null +++ b/src/error.rs @@ -0,0 +1,85 @@ +use crate::lexer::Token; +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; +use chumsky::error::{Simple, SimpleReason}; +use std::fmt::Display; + +#[derive(Debug, Clone)] +pub enum Error<'a> { + Parse(Simple>), +} + +impl<'a> Error<'a> { + pub fn report(&self, src: &str) -> std::io::Result<()> { + match self { + Error::Parse(e) => { + let report = + Report::>::build(ReportKind::Error, (), e.span().start); + match e.reason() { + SimpleReason::Unexpected => report + .with_message(match e.found() { + Some(Token::Error) => { + format!("Invalid token: {}", &src[e.span()].fg(Color::Yellow)) + } + Some(t) => format!("Unexpected token `{t:?}`"), + None => "Unexpected end of input".to_owned(), + }) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Expected: {}", + e.expected() + .map(|exp| match exp { + Some(expected) => format!("{expected:?}"), + None => "end of input".to_owned(), + }) + .collect::>() + .join(", ") + )) + .with_color(Color::Red), + ), + SimpleReason::Unclosed { span, delimiter } => { + let msg = format!( + "Unclosed delimiter: {:?}", + format!("{delimiter:?}").fg(Color::Yellow) + ); + report + .with_message(&msg) + .with_label( + Label::new(span.clone()) + .with_message(msg) + .with_color(Color::Red), + ) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Must be closed before {}", + match e.found() { + Some(x) => format!("{x:?}"), + None => "the end of input".to_owned(), + } + )) + .with_color(Color::Yellow), + ) + } + SimpleReason::Custom(msg) => report.with_message(msg).with_label( + Label::new(e.span()) + .with_message(msg.fg(Color::Red)) + .with_color(Color::Red), + ), + } + .finish() + .print(Source::from(&src)) + } + } + } +} + +impl<'a> Display for Error<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::Parse(e) => write!(f, "{e:?}"), + } + } +} + +impl<'a> std::error::Error for Error<'a> {} diff --git a/src/lib.rs b/src/lib.rs index d8fc6e0..b1e3461 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod error; pub mod lexer; pub mod list; pub mod parser; diff --git a/src/main.rs b/src/main.rs index 45d65d8..612b582 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,21 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use web_lisp::parser; +use web_lisp::{error::Error, parser}; fn main() -> Result<(), Box> { - match parser::read(&std::fs::read_to_string( - std::env::args().nth(1).ok_or("no filename provided")?, - )?) { + let src = std::fs::read_to_string(std::env::args().nth(1).ok_or("no filename provided")?)?; + + match parser::read(&src) { Ok(vals) => { for val in vals { println!("{val}"); } } - Err(e) => eprintln!("Parse error: {e:?}"), + Err(errs) => { + for err in errs { + Error::Parse(err).report(&src)?; + } + } } Ok(())