Make one report for each error
This commit is contained in:
parent
37a88df613
commit
fdf6983ab2
@ -21,11 +21,42 @@ pub enum Logic<'src> {
|
|||||||
|
|
||||||
impl<'src> AbstractTree for Logic<'src> {
|
impl<'src> AbstractTree for Logic<'src> {
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||||
todo!()
|
Ok(Type::Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
||||||
todo!()
|
match self {
|
||||||
|
Logic::Equal(left, right)
|
||||||
|
| Logic::NotEqual(left, right)
|
||||||
|
| Logic::Greater(left, right)
|
||||||
|
| Logic::Less(left, right)
|
||||||
|
| Logic::GreaterOrEqual(left, right)
|
||||||
|
| Logic::LessOrEqual(left, right) => {
|
||||||
|
let left = left.expected_type(context)?;
|
||||||
|
let right = right.expected_type(context)?;
|
||||||
|
|
||||||
|
left.check(&right)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Logic::And(left, right) | Logic::Or(left, right) => {
|
||||||
|
let left = left.expected_type(context)?;
|
||||||
|
let right = right.expected_type(context)?;
|
||||||
|
|
||||||
|
if let (Type::Boolean, Type::Boolean) = (left, right) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ValidationError::ExpectedBoolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logic::Not(expression) => {
|
||||||
|
if let Type::Boolean = expression.expected_type(context)? {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ValidationError::ExpectedBoolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
fn run(self, _context: &Context) -> Result<Value, RuntimeError> {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::Identifier,
|
abstract_tree::Identifier,
|
||||||
context::Context,
|
context::Context,
|
||||||
@ -103,6 +105,37 @@ impl AbstractTree for Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Type {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Any => write!(f, "any"),
|
||||||
|
Type::Boolean => write!(f, "boolean"),
|
||||||
|
Type::Custom(name) => write!(f, "{name}"),
|
||||||
|
Type::Float => write!(f, "float"),
|
||||||
|
Type::Integer => write!(f, "integer"),
|
||||||
|
Type::List => write!(f, "list"),
|
||||||
|
Type::ListOf(item_type) => write!(f, "list of {item_type}"),
|
||||||
|
Type::ListExact(item_types) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
for (index, item_type) in item_types.into_iter().enumerate() {
|
||||||
|
if index == item_types.len() - 1 {
|
||||||
|
write!(f, "{item_type}")?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{item_type}, ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
Type::Map => write!(f, "map"),
|
||||||
|
Type::None => write!(f, "none"),
|
||||||
|
Type::Range => write!(f, "range"),
|
||||||
|
Type::String => write!(f, "string"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
103
src/error.rs
103
src/error.rs
@ -1,6 +1,6 @@
|
|||||||
use std::sync::PoisonError;
|
use std::sync::PoisonError;
|
||||||
|
|
||||||
use ariadne::{Label, Report, ReportKind};
|
use ariadne::{Color, Label, Report, ReportKind};
|
||||||
use chumsky::{prelude::Rich, span::SimpleSpan};
|
use chumsky::{prelude::Rich, span::SimpleSpan};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -25,6 +25,68 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn report(&self) -> Report {
|
||||||
|
match self {
|
||||||
|
Error::Parse { expected, span } => Report::build(
|
||||||
|
ReportKind::Custom("Parsing Error", Color::White),
|
||||||
|
(),
|
||||||
|
span.start,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(span.start..span.end).with_message(format!("Expected {expected}.")),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
Error::Lex { expected, span } => {
|
||||||
|
let expected = match expected.as_str() {
|
||||||
|
"" => "something else",
|
||||||
|
expected => expected,
|
||||||
|
};
|
||||||
|
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Lexing Error", Color::White),
|
||||||
|
(),
|
||||||
|
span.start,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(span.start..span.end).with_message(format!("Expected {expected}.")),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
Error::Runtime(_) => todo!(),
|
||||||
|
Error::Validation { error, span } => {
|
||||||
|
let mut report = Report::build(
|
||||||
|
ReportKind::Custom("Lexing Error", Color::White),
|
||||||
|
(),
|
||||||
|
span.start,
|
||||||
|
);
|
||||||
|
|
||||||
|
match error {
|
||||||
|
ValidationError::ExpectedBoolean => {
|
||||||
|
report = report.with_label(
|
||||||
|
Label::new(span.start..span.end).with_message("Expected boolean."),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ValidationError::RwLockPoison(_) => todo!(),
|
||||||
|
ValidationError::TypeCheck(TypeCheckError { actual, expected }) => {
|
||||||
|
report = report.with_label(Label::new(span.start..span.end).with_message(
|
||||||
|
format!("Type error. Expected {expected} but got {actual}."),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ValidationError::VariableNotFound(identifier) => {
|
||||||
|
report = report
|
||||||
|
.with_label(Label::new(span.start..span.end).with_message(format!(
|
||||||
|
"The variable {identifier} does not exist."
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
report.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Rich<'_, char>> for Error {
|
impl From<Rich<'_, char>> for Error {
|
||||||
fn from(error: Rich<'_, char>) -> Self {
|
fn from(error: Rich<'_, char>) -> Self {
|
||||||
Error::Lex {
|
Error::Lex {
|
||||||
@ -101,42 +163,3 @@ pub struct TypeCheckError {
|
|||||||
pub actual: Type,
|
pub actual: Type,
|
||||||
pub expected: Type,
|
pub expected: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_report<'a>(errors: &'a [Error]) -> Report<'a> {
|
|
||||||
let mut report = Report::build(ReportKind::Error, (), 0);
|
|
||||||
|
|
||||||
for error in errors {
|
|
||||||
match &error {
|
|
||||||
Error::Parse { expected, span } => {
|
|
||||||
report = report.with_label(
|
|
||||||
Label::new(span.start..span.end).with_message(format!("Expected {expected}.")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Error::Lex { expected, span } => {
|
|
||||||
let expected = match expected.as_str() {
|
|
||||||
"" => "something else",
|
|
||||||
expected => expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
report = report.with_label(
|
|
||||||
Label::new(span.start..span.end).with_message(format!("Expected {expected}.")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Error::Runtime(_) => todo!(),
|
|
||||||
Error::Validation { error, span } => match error {
|
|
||||||
ValidationError::ExpectedBoolean => todo!(),
|
|
||||||
ValidationError::RwLockPoison(_) => todo!(),
|
|
||||||
ValidationError::TypeCheck(_) => todo!(),
|
|
||||||
ValidationError::VariableNotFound(identifier) => {
|
|
||||||
report =
|
|
||||||
report
|
|
||||||
.with_label(Label::new(span.start..span.end).with_message(format!(
|
|
||||||
"The variable {identifier} does not exist."
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
report.finish()
|
|
||||||
}
|
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -6,7 +6,7 @@ use colored::Colorize;
|
|||||||
|
|
||||||
use std::{fs::read_to_string, io::Write};
|
use std::{fs::read_to_string, io::Write};
|
||||||
|
|
||||||
use dust_lang::{context::Context, error::create_report, Interpreter};
|
use dust_lang::{context::Context, Interpreter};
|
||||||
|
|
||||||
/// Command-line arguments to be parsed.
|
/// Command-line arguments to be parsed.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -52,8 +52,10 @@ fn main() {
|
|||||||
println!("{value}")
|
println!("{value}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errors) => create_report(&errors)
|
Err(errors) => {
|
||||||
.eprint(Source::from(&source))
|
for error in errors {
|
||||||
.unwrap(),
|
error.report().eprint(Source::from(&source)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,10 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
infix(left(1), just(Token::Operator("<=")), |left, right| {
|
infix(left(1), just(Token::Operator("<=")), |left, right| {
|
||||||
Expression::Logic(Box::new(Logic::LessOrEqual(left, right)))
|
Expression::Logic(Box::new(Logic::LessOrEqual(left, right)))
|
||||||
}),
|
}),
|
||||||
infix(left(1), just(Token::Operator("&&")), |left, right| {
|
infix(right(1), just(Token::Operator("&&")), |left, right| {
|
||||||
Expression::Logic(Box::new(Logic::And(left, right)))
|
Expression::Logic(Box::new(Logic::And(left, right)))
|
||||||
}),
|
}),
|
||||||
infix(left(1), just(Token::Operator("||")), |left, right| {
|
infix(right(1), just(Token::Operator("||")), |left, right| {
|
||||||
Expression::Logic(Box::new(Logic::Or(left, right)))
|
Expression::Logic(Box::new(Logic::Or(left, right)))
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
|
10
tests/expressions.rs
Normal file
10
tests/expressions.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use dust_lang::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logic() {
|
||||||
|
assert_eq!(interpret("1 == 1"), Ok(Value::boolean(true)));
|
||||||
|
assert_eq!(
|
||||||
|
interpret("('42' == '42') && (42 != 0)"),
|
||||||
|
Ok(Value::boolean(true))
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user