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::{ use crate::{
context::Context, context::Context,
error::{Error, RuntimeError, ValidationError}, error::{DustError, RuntimeError, ValidationError},
Value, Value,
}; };
@ -110,7 +110,7 @@ impl AbstractTree {
self, self,
context: &mut Context, context: &mut Context,
manage_memory: bool, manage_memory: bool,
) -> Result<Option<Value>, Vec<Error>> { ) -> Result<Option<Value>, Vec<DustError>> {
let valid_statements = self.validate(context, manage_memory)?; let valid_statements = self.validate(context, manage_memory)?;
let mut previous_value = None; let mut previous_value = None;
@ -125,7 +125,7 @@ impl AbstractTree {
_ => {} _ => {}
}, },
Err(runtime_error) => { Err(runtime_error) => {
return Err(vec![Error::Runtime { return Err(vec![DustError::Runtime {
error: runtime_error, error: runtime_error,
position, position,
}]); }]);
@ -140,7 +140,7 @@ impl AbstractTree {
self, self,
context: &mut Context, context: &mut Context,
manage_memory: bool, manage_memory: bool,
) -> Result<Vec<Statement>, Vec<Error>> { ) -> Result<Vec<Statement>, Vec<DustError>> {
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut valid_statements = Vec::new(); let mut valid_statements = Vec::new();
@ -148,7 +148,7 @@ impl AbstractTree {
let validation = statement.validate(context, manage_memory); let validation = statement.validate(context, manage_memory);
if let Err(validation_error) = validation { if let Err(validation_error) = validation {
errors.push(Error::Validation { errors.push(DustError::Validation {
error: validation_error, error: validation_error,
position: statement.position(), position: statement.position(),
}) })
@ -158,7 +158,7 @@ impl AbstractTree {
let run = statement.evaluate(context, true); let run = statement.evaluate(context, true);
if let Err(runtime_error) = run { if let Err(runtime_error) = run {
errors.push(Error::Runtime { errors.push(DustError::Runtime {
error: runtime_error, error: runtime_error,
position, position,
}); });

View File

@ -9,7 +9,7 @@ use crate::{
}; };
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum DustError {
Lex { Lex {
expected: String, expected: String,
span: (usize, usize), 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 { fn from(error: Rich<'_, char>) -> Self {
Error::Lex { DustError::Lex {
expected: error.expected().map(|error| error.to_string()).collect(), expected: error.expected().map(|error| error.to_string()).collect(),
span: (error.span().start(), error.span().end()), span: (error.span().start(), error.span().end()),
reason: error.reason().to_string(), 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 { fn from(error: Rich<'_, Token<'src>>) -> Self {
Error::Parse { DustError::Parse {
expected: error.expected().map(|error| error.to_string()).collect(), expected: error.expected().map(|error| error.to_string()).collect(),
span: (error.span().start(), error.span().end()), span: (error.span().start(), error.span().end()),
found: error.found().map(|token| token.to_string()), found: error.found().map(|token| token.to_string()),

View File

@ -5,7 +5,7 @@ use std::{
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::error::Error; use crate::error::DustError;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Token<'src> { 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() lexer()
.parse(source) .parse(source)
.into_result() .into_result()

View File

@ -16,7 +16,7 @@ use abstract_tree::{AbstractTree, Type};
use ariadne::{Color, Fmt, Label, Report, ReportKind}; use ariadne::{Color, Fmt, Label, Report, ReportKind};
use chumsky::prelude::*; use chumsky::prelude::*;
use context::Context; use context::Context;
use error::{Error, RuntimeError, TypeConflict, ValidationError}; use error::{DustError, RuntimeError, TypeConflict, ValidationError};
use lexer::{lex, Token}; use lexer::{lex, Token};
use parser::{parse, parser}; use parser::{parse, parser};
use rayon::prelude::*; use rayon::prelude::*;
@ -155,7 +155,7 @@ impl<'a> Interpreter<'a> {
.into_result() .into_result()
.map_err(|errors| InterpreterError { .map_err(|errors| InterpreterError {
source_id: source_id.clone(), 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 { let abstract_tree = match parse_result {
Ok(statements) => AbstractTree::new(statements), Ok(statements) => AbstractTree::new(statements),
@ -185,22 +185,26 @@ impl<'a> Interpreter<'a> {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct InterpreterError { pub struct InterpreterError {
source_id: Arc<str>, source_id: Arc<str>,
errors: Vec<Error>, errors: Vec<DustError>,
} }
impl InterpreterError { impl InterpreterError {
pub fn errors(&self) -> &Vec<Error> { pub fn errors(&self) -> &Vec<DustError> {
&self.errors &self.errors
} }
} }
impl InterpreterError { impl InterpreterError {
pub fn build_reports<'a>(self) -> Vec<Report<'a, (Arc<str>, Range<usize>)>> { 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(); let mut reports = Vec::new();
for error in self.errors { for error in self.errors {
let (mut builder, validation_error) = match error { let (mut builder, validation_error) = match error {
Error::Lex { DustError::Lex {
expected, expected,
span, span,
reason, reason,
@ -226,7 +230,7 @@ impl InterpreterError {
None, None,
) )
} }
Error::Parse { DustError::Parse {
expected, expected,
span, span,
found, found,
@ -236,7 +240,7 @@ impl InterpreterError {
} else { } else {
format!("Expected {expected}.") 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( Report::build(
@ -253,7 +257,7 @@ impl InterpreterError {
None, None,
) )
} }
Error::Validation { error, position } => ( DustError::Validation { error, position } => (
Report::build( Report::build(
ReportKind::Custom("Validation Error", Color::Magenta), ReportKind::Custom("Validation Error", Color::Magenta),
self.source_id.clone(), self.source_id.clone(),
@ -263,7 +267,7 @@ impl InterpreterError {
.with_note("This error was detected by the interpreter before running the code."), .with_note("This error was detected by the interpreter before running the code."),
Some(error), Some(error),
), ),
Error::Runtime { error, position } => ( DustError::Runtime { error, position } => (
Report::build( Report::build(
ReportKind::Custom("Runtime Error", Color::Red), ReportKind::Custom("Runtime Error", Color::Red),
self.source_id.clone(), 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 { if let Some(validation_error) = validation_error {
match validation_error { match validation_error {

View File

@ -4,7 +4,7 @@ use chumsky::{input::SpannedInput, pratt::*, prelude::*};
use crate::{ use crate::{
abstract_tree::*, abstract_tree::*,
error::Error, error::DustError,
identifier::Identifier, identifier::Identifier,
lexer::{Control, Keyword, Operator, Token}, 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 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) let statements = parser(false)
.parse(tokens.spanned((tokens.len()..tokens.len()).into())) .parse(tokens.spanned((tokens.len()..tokens.len()).into()))
.into_result() .into_result()
.map_err(|errors| { .map_err(|errors| {
errors errors
.into_iter() .into_iter()
.map(|error| Error::from(error)) .map(|error| DustError::from(error))
.collect::<Vec<Error>>() .collect::<Vec<DustError>>()
})?; })?;
Ok(AbstractTree::new(statements)) Ok(AbstractTree::new(statements))
@ -224,9 +226,9 @@ pub fn parser<'src>(
.separated_by(just(Token::Control(Control::Comma))) .separated_by(just(Token::Control(Control::Comma)))
.at_least(1) .at_least(1)
.allow_trailing() .allow_trailing()
.collect() .collect(),
.or_not(),
) )
.or_not()
.then( .then(
identifier identifier
.clone() .clone()
@ -744,8 +746,8 @@ mod tests {
.map_err(|errors| { .map_err(|errors| {
errors errors
.into_iter() .into_iter()
.map(|error| Error::from(error)) .map(|error| DustError::from(error))
.collect::<Vec<Error>>() .collect::<Vec<DustError>>()
}) })
.unwrap(); .unwrap();
@ -763,8 +765,8 @@ mod tests {
.map_err(|errors| { .map_err(|errors| {
errors errors
.into_iter() .into_iter()
.map(|error| Error::from(error)) .map(|error| DustError::from(error))
.collect::<Vec<Error>>() .collect::<Vec<DustError>>()
}) })
.unwrap(); .unwrap();

View File

@ -6,7 +6,7 @@ fn function_call_with_type_argument() {
interpret( interpret(
"test", "test",
" "
foobar = fn (T)(x : T) T { x } foobar = fn T (x: T) -> T { x }
foobar::(int)::(42) foobar::(int)::(42)
", ",
), ),
@ -20,7 +20,7 @@ fn function_call() {
interpret( interpret(
"test", "test",
" "
foobar = fn (message : str) str { message } foobar = fn (message: str) -> str { message }
foobar('Hiya') foobar('Hiya')
", ",
), ),
@ -34,10 +34,10 @@ fn callback() {
interpret( interpret(
"test", "test",
" "
foobar = fn (cb: fn() -> str) str { foobar = fn (cb: fn () -> str) -> str {
cb() cb()
} }
foobar(fn () str { 'Hiya' }) foobar(fn () -> str { 'Hiya' })
", ",
), ),
Ok(Some(Value::string("Hiya".to_string()))) Ok(Some(Value::string("Hiya".to_string())))
@ -55,8 +55,8 @@ fn function_context_captures_values() {
interpret( interpret(
"test", "test",
" "
bar = fn () int { 2 } bar = fn () -> int { 2 }
foo = fn () int { bar() } foo = fn () -> int { bar() }
foo() foo()
" "
), ),
@ -70,7 +70,7 @@ fn recursion() {
interpret( interpret(
"test", "test",
" "
fib = fn (i: int) int { fib = fn (i: int) -> int {
if i <= 1 { if i <= 1 {
1 1
} else { } else {

View File

@ -1,6 +1,6 @@
use dust_lang::{ use dust_lang::{
abstract_tree::{Type, WithPos}, abstract_tree::{Type, WithPos},
error::{Error, TypeConflict, ValidationError}, error::{DustError, TypeConflict, ValidationError},
identifier::Identifier, identifier::Identifier,
interpret, Value, interpret, Value,
}; };
@ -49,7 +49,7 @@ fn field_type_error() {
) )
.unwrap_err() .unwrap_err()
.errors(), .errors(),
&vec![Error::Validation { &vec![DustError::Validation {
error: ValidationError::TypeCheck { error: ValidationError::TypeCheck {
conflict: TypeConflict { conflict: TypeConflict {
actual: Type::String, actual: Type::String,
@ -109,7 +109,7 @@ fn undefined_struct() {
) )
.unwrap_err() .unwrap_err()
.errors(), .errors(),
&vec![Error::Validation { &vec![DustError::Validation {
error: ValidationError::VariableNotFound { error: ValidationError::VariableNotFound {
identifier: Identifier::new("Foo"), identifier: Identifier::new("Foo"),
position: (17, 20).into() position: (17, 20).into()

View File

@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use dust_lang::{ use dust_lang::{
abstract_tree::{Type, WithPos}, abstract_tree::{Type, WithPos},
error::{Error, TypeConflict, ValidationError}, error::{DustError, TypeConflict, ValidationError},
identifier::Identifier, identifier::Identifier,
*, *,
}; };
@ -141,7 +141,7 @@ fn map_type_errors() {
interpret("test", "{ foo : bool = 'bar' }") interpret("test", "{ foo : bool = 'bar' }")
.unwrap_err() .unwrap_err()
.errors(), .errors(),
&vec![Error::Validation { &vec![DustError::Validation {
error: ValidationError::TypeCheck { error: ValidationError::TypeCheck {
conflict: TypeConflict { conflict: TypeConflict {
actual: Type::String, actual: Type::String,

View File

@ -1,6 +1,6 @@
use dust_lang::{ use dust_lang::{
abstract_tree::{Block, Expression, Statement, Type, WithPos}, abstract_tree::{Block, Expression, Statement, Type, WithPos},
error::{Error, TypeConflict, ValidationError}, error::{DustError, TypeConflict, ValidationError},
identifier::Identifier, identifier::Identifier,
*, *,
}; };
@ -27,7 +27,7 @@ fn set_variable_with_type_error() {
interpret("test", "foobar: str = true") interpret("test", "foobar: str = true")
.unwrap_err() .unwrap_err()
.errors(), .errors(),
&vec![Error::Validation { &vec![DustError::Validation {
error: ValidationError::TypeCheck { error: ValidationError::TypeCheck {
conflict: TypeConflict { conflict: TypeConflict {
actual: Type::Boolean, actual: Type::Boolean,

View File

@ -60,7 +60,19 @@ fn main() {
let mut interpreter = Interpreter::new(context.clone()); let mut interpreter = Interpreter::new(context.clone());
if !args.no_std { 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 { let (source_id, source): (Arc<str>, Arc<str>) = if let Some(path) = args.path {