Clean up and prettify parsing errors
This commit is contained in:
parent
c0791ebb83
commit
7c809fa764
@ -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,
|
||||||
});
|
});
|
||||||
|
@ -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()),
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user