Clean up and prettify parsing errors

This commit is contained in:
Jeff 2024-06-18 19:42:04 -04:00
parent c0791ebb83
commit 7c809fa764
10 changed files with 65 additions and 50 deletions

View File

@ -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<Option<Value>, Vec<Error>> {
) -> Result<Option<Value>, Vec<DustError>> {
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<Statement>, Vec<Error>> {
) -> Result<Vec<Statement>, Vec<DustError>> {
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,
});

View File

@ -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<Rich<'_, char>> for Error {
impl From<Rich<'_, char>> 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<Rich<'_, char>> for Error {
}
}
impl<'src> From<Rich<'_, Token<'src>>> for Error {
impl<'src> From<Rich<'_, Token<'src>>> 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()),

View File

@ -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<Vec<(Token<'src>, SimpleSpan)>, Vec<Error>> {
pub fn lex<'src>(source: &'src str) -> Result<Vec<(Token<'src>, SimpleSpan)>, Vec<DustError>> {
lexer()
.parse(source)
.into_result()

View File

@ -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<str>,
errors: Vec<Error>,
errors: Vec<DustError>,
}
impl InterpreterError {
pub fn errors(&self) -> &Vec<Error> {
pub fn errors(&self) -> &Vec<DustError> {
&self.errors
}
}
impl InterpreterError {
pub fn build_reports<'a>(self) -> Vec<Report<'a, (Arc<str>, Range<usize>)>> {
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 {

View File

@ -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<Rich<'src, Token<'src>, SimpleSpan>>;
pub fn parse<'src>(tokens: &'src [(Token<'src>, SimpleSpan)]) -> Result<AbstractTree, Vec<Error>> {
pub fn parse<'src>(
tokens: &'src [(Token<'src>, SimpleSpan)],
) -> Result<AbstractTree, Vec<DustError>> {
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::<Vec<Error>>()
.map(|error| DustError::from(error))
.collect::<Vec<DustError>>()
})?;
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::<Vec<Error>>()
.map(|error| DustError::from(error))
.collect::<Vec<DustError>>()
})
.unwrap();
@ -763,8 +765,8 @@ mod tests {
.map_err(|errors| {
errors
.into_iter()
.map(|error| Error::from(error))
.collect::<Vec<Error>>()
.map(|error| DustError::from(error))
.collect::<Vec<DustError>>()
})
.unwrap();

View File

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

View File

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

View File

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

View File

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

View File

@ -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<str>, Arc<str>) = if let Some(path) = args.path {