From 7c809fa7644bc0a4338280252749d6cbe380446a Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 18 Jun 2024 19:42:04 -0400 Subject: [PATCH] Clean up and prettify parsing errors --- dust-lang/src/abstract_tree/mod.rs | 12 ++++++------ dust-lang/src/error.rs | 10 +++++----- dust-lang/src/lexer.rs | 4 ++-- dust-lang/src/lib.rs | 25 +++++++++++++------------ dust-lang/src/parser.rs | 22 ++++++++++++---------- dust-lang/tests/functions.rs | 14 +++++++------- dust-lang/tests/structs.rs | 6 +++--- dust-lang/tests/values.rs | 4 ++-- dust-lang/tests/variables.rs | 4 ++-- dust-shell/src/main.rs | 14 +++++++++++++- 10 files changed, 65 insertions(+), 50 deletions(-) diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index 74b2330..6356a69 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -49,7 +49,7 @@ pub use self::{ use crate::{ context::Context, - error::{Error, RuntimeError, ValidationError}, + error::{DustError, RuntimeError, ValidationError}, Value, }; @@ -110,7 +110,7 @@ impl AbstractTree { self, context: &mut Context, manage_memory: bool, - ) -> Result, Vec> { + ) -> Result, Vec> { let valid_statements = self.validate(context, manage_memory)?; let mut previous_value = None; @@ -125,7 +125,7 @@ impl AbstractTree { _ => {} }, Err(runtime_error) => { - return Err(vec![Error::Runtime { + return Err(vec![DustError::Runtime { error: runtime_error, position, }]); @@ -140,7 +140,7 @@ impl AbstractTree { self, context: &mut Context, manage_memory: bool, - ) -> Result, Vec> { + ) -> Result, Vec> { let mut errors = Vec::new(); let mut valid_statements = Vec::new(); @@ -148,7 +148,7 @@ impl AbstractTree { let validation = statement.validate(context, manage_memory); if let Err(validation_error) = validation { - errors.push(Error::Validation { + errors.push(DustError::Validation { error: validation_error, position: statement.position(), }) @@ -158,7 +158,7 @@ impl AbstractTree { let run = statement.evaluate(context, true); if let Err(runtime_error) = run { - errors.push(Error::Runtime { + errors.push(DustError::Runtime { error: runtime_error, position, }); diff --git a/dust-lang/src/error.rs b/dust-lang/src/error.rs index 2bea4cb..6c343c7 100644 --- a/dust-lang/src/error.rs +++ b/dust-lang/src/error.rs @@ -9,7 +9,7 @@ use crate::{ }; #[derive(Debug, PartialEq)] -pub enum Error { +pub enum DustError { Lex { expected: String, span: (usize, usize), @@ -30,9 +30,9 @@ pub enum Error { }, } -impl From> for Error { +impl From> for DustError { fn from(error: Rich<'_, char>) -> Self { - Error::Lex { + DustError::Lex { expected: error.expected().map(|error| error.to_string()).collect(), span: (error.span().start(), error.span().end()), reason: error.reason().to_string(), @@ -40,9 +40,9 @@ impl From> for Error { } } -impl<'src> From>> for Error { +impl<'src> From>> for DustError { fn from(error: Rich<'_, Token<'src>>) -> Self { - Error::Parse { + DustError::Parse { expected: error.expected().map(|error| error.to_string()).collect(), span: (error.span().start(), error.span().end()), found: error.found().map(|token| token.to_string()), diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index a50e3d9..79bfab1 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -5,7 +5,7 @@ use std::{ use chumsky::prelude::*; -use crate::error::Error; +use crate::error::DustError; #[derive(Copy, Clone, Debug, PartialEq)] pub enum Token<'src> { @@ -187,7 +187,7 @@ impl Display for Control { } } -pub fn lex<'src>(source: &'src str) -> Result, SimpleSpan)>, Vec> { +pub fn lex<'src>(source: &'src str) -> Result, SimpleSpan)>, Vec> { lexer() .parse(source) .into_result() diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 6756dc7..c4ca668 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -16,7 +16,7 @@ use abstract_tree::{AbstractTree, Type}; use ariadne::{Color, Fmt, Label, Report, ReportKind}; use chumsky::prelude::*; use context::Context; -use error::{Error, RuntimeError, TypeConflict, ValidationError}; +use error::{DustError, RuntimeError, TypeConflict, ValidationError}; use lexer::{lex, Token}; use parser::{parse, parser}; use rayon::prelude::*; @@ -155,7 +155,7 @@ impl<'a> Interpreter<'a> { .into_result() .map_err(|errors| InterpreterError { source_id: source_id.clone(), - errors: errors.into_iter().map(Error::from).collect(), + errors: errors.into_iter().map(DustError::from).collect(), }); let abstract_tree = match parse_result { Ok(statements) => AbstractTree::new(statements), @@ -185,22 +185,26 @@ impl<'a> Interpreter<'a> { #[derive(Debug, PartialEq)] pub struct InterpreterError { source_id: Arc, - errors: Vec, + errors: Vec, } impl InterpreterError { - pub fn errors(&self) -> &Vec { + pub fn errors(&self) -> &Vec { &self.errors } } impl InterpreterError { pub fn build_reports<'a>(self) -> Vec, Range)>> { + let token_color = Color::Yellow; + let type_color = Color::Green; + let identifier_color = Color::Blue; + let mut reports = Vec::new(); for error in self.errors { let (mut builder, validation_error) = match error { - Error::Lex { + DustError::Lex { expected, span, reason, @@ -226,7 +230,7 @@ impl InterpreterError { None, ) } - Error::Parse { + DustError::Parse { expected, span, found, @@ -236,7 +240,7 @@ impl InterpreterError { } else { format!("Expected {expected}.") }; - let found = found.unwrap_or_else(|| "End of input".to_string()); + let found = found.unwrap_or_else(|| "End of input".to_string()).fg(token_color); ( Report::build( @@ -253,7 +257,7 @@ impl InterpreterError { None, ) } - Error::Validation { error, position } => ( + DustError::Validation { error, position } => ( Report::build( ReportKind::Custom("Validation Error", Color::Magenta), self.source_id.clone(), @@ -263,7 +267,7 @@ impl InterpreterError { .with_note("This error was detected by the interpreter before running the code."), Some(error), ), - Error::Runtime { error, position } => ( + DustError::Runtime { error, position } => ( Report::build( ReportKind::Custom("Runtime Error", Color::Red), self.source_id.clone(), @@ -287,9 +291,6 @@ impl InterpreterError { ), }; - let type_color = Color::Green; - let identifier_color = Color::Blue; - if let Some(validation_error) = validation_error { match validation_error { diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index ad44b93..a007a0e 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -4,7 +4,7 @@ use chumsky::{input::SpannedInput, pratt::*, prelude::*}; use crate::{ abstract_tree::*, - error::Error, + error::DustError, identifier::Identifier, lexer::{Control, Keyword, Operator, Token}, }; @@ -14,15 +14,17 @@ pub type ParserInput<'src> = pub type ParserExtra<'src> = extra::Err, SimpleSpan>>; -pub fn parse<'src>(tokens: &'src [(Token<'src>, SimpleSpan)]) -> Result> { +pub fn parse<'src>( + tokens: &'src [(Token<'src>, SimpleSpan)], +) -> Result> { let statements = parser(false) .parse(tokens.spanned((tokens.len()..tokens.len()).into())) .into_result() .map_err(|errors| { errors .into_iter() - .map(|error| Error::from(error)) - .collect::>() + .map(|error| DustError::from(error)) + .collect::>() })?; Ok(AbstractTree::new(statements)) @@ -224,9 +226,9 @@ pub fn parser<'src>( .separated_by(just(Token::Control(Control::Comma))) .at_least(1) .allow_trailing() - .collect() - .or_not(), + .collect(), ) + .or_not() .then( identifier .clone() @@ -744,8 +746,8 @@ mod tests { .map_err(|errors| { errors .into_iter() - .map(|error| Error::from(error)) - .collect::>() + .map(|error| DustError::from(error)) + .collect::>() }) .unwrap(); @@ -763,8 +765,8 @@ mod tests { .map_err(|errors| { errors .into_iter() - .map(|error| Error::from(error)) - .collect::>() + .map(|error| DustError::from(error)) + .collect::>() }) .unwrap(); diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 2bd4e96..47aef58 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -6,7 +6,7 @@ fn function_call_with_type_argument() { interpret( "test", " - foobar = fn (T)(x : T) T { x } + foobar = fn T (x: T) -> T { x } foobar::(int)::(42) ", ), @@ -20,7 +20,7 @@ fn function_call() { interpret( "test", " - foobar = fn (message : str) str { message } + foobar = fn (message: str) -> str { message } foobar('Hiya') ", ), @@ -34,10 +34,10 @@ fn callback() { interpret( "test", " - foobar = fn (cb: fn() -> str) str { + foobar = fn (cb: fn () -> str) -> str { cb() } - foobar(fn () str { 'Hiya' }) + foobar(fn () -> str { 'Hiya' }) ", ), Ok(Some(Value::string("Hiya".to_string()))) @@ -55,8 +55,8 @@ fn function_context_captures_values() { interpret( "test", " - bar = fn () int { 2 } - foo = fn () int { bar() } + bar = fn () -> int { 2 } + foo = fn () -> int { bar() } foo() " ), @@ -70,7 +70,7 @@ fn recursion() { interpret( "test", " - fib = fn (i: int) int { + fib = fn (i: int) -> int { if i <= 1 { 1 } else { diff --git a/dust-lang/tests/structs.rs b/dust-lang/tests/structs.rs index 331a4e1..99b2bea 100644 --- a/dust-lang/tests/structs.rs +++ b/dust-lang/tests/structs.rs @@ -1,6 +1,6 @@ use dust_lang::{ abstract_tree::{Type, WithPos}, - error::{Error, TypeConflict, ValidationError}, + error::{DustError, TypeConflict, ValidationError}, identifier::Identifier, interpret, Value, }; @@ -49,7 +49,7 @@ fn field_type_error() { ) .unwrap_err() .errors(), - &vec![Error::Validation { + &vec![DustError::Validation { error: ValidationError::TypeCheck { conflict: TypeConflict { actual: Type::String, @@ -109,7 +109,7 @@ fn undefined_struct() { ) .unwrap_err() .errors(), - &vec![Error::Validation { + &vec![DustError::Validation { error: ValidationError::VariableNotFound { identifier: Identifier::new("Foo"), position: (17, 20).into() diff --git a/dust-lang/tests/values.rs b/dust-lang/tests/values.rs index b9d7683..60b91b9 100644 --- a/dust-lang/tests/values.rs +++ b/dust-lang/tests/values.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use dust_lang::{ abstract_tree::{Type, WithPos}, - error::{Error, TypeConflict, ValidationError}, + error::{DustError, TypeConflict, ValidationError}, identifier::Identifier, *, }; @@ -141,7 +141,7 @@ fn map_type_errors() { interpret("test", "{ foo : bool = 'bar' }") .unwrap_err() .errors(), - &vec![Error::Validation { + &vec![DustError::Validation { error: ValidationError::TypeCheck { conflict: TypeConflict { actual: Type::String, diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index f663d4a..4cb9f6b 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -1,6 +1,6 @@ use dust_lang::{ abstract_tree::{Block, Expression, Statement, Type, WithPos}, - error::{Error, TypeConflict, ValidationError}, + error::{DustError, TypeConflict, ValidationError}, identifier::Identifier, *, }; @@ -27,7 +27,7 @@ fn set_variable_with_type_error() { interpret("test", "foobar: str = true") .unwrap_err() .errors(), - &vec![Error::Validation { + &vec![DustError::Validation { error: ValidationError::TypeCheck { conflict: TypeConflict { actual: Type::Boolean, diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index eabeb84..bc249da 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -60,7 +60,19 @@ fn main() { let mut interpreter = Interpreter::new(context.clone()); if !args.no_std { - interpreter.load_std().unwrap(); + let load_std_result = interpreter.load_std(); + + if let Err(error) = load_std_result { + eprintln!("Failed to load standard library"); + + for report in error.build_reports() { + report + .write_for_stdout(sources(interpreter.sources()), stderr()) + .unwrap(); + } + + return; + } } let (source_id, source): (Arc, Arc) = if let Some(path) = args.path {