diff --git a/src/abstract_tree/logic.rs b/src/abstract_tree/logic.rs index b453197..df89a7e 100644 --- a/src/abstract_tree/logic.rs +++ b/src/abstract_tree/logic.rs @@ -21,11 +21,42 @@ pub enum Logic<'src> { impl<'src> AbstractTree for Logic<'src> { fn expected_type(&self, _context: &Context) -> Result { - todo!() + Ok(Type::Boolean) } - fn validate(&self, _context: &Context) -> Result<(), ValidationError> { - todo!() + fn validate(&self, context: &Context) -> Result<(), ValidationError> { + 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 { diff --git a/src/abstract_tree/type.rs b/src/abstract_tree/type.rs index e792b2f..3b96ad9 100644 --- a/src/abstract_tree/type.rs +++ b/src/abstract_tree/type.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::{ abstract_tree::Identifier, 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)] mod tests { use super::*; diff --git a/src/error.rs b/src/error.rs index af84cc2..5b33579 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ use std::sync::PoisonError; -use ariadne::{Label, Report, ReportKind}; +use ariadne::{Color, Label, Report, ReportKind}; use chumsky::{prelude::Rich, span::SimpleSpan}; 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> for Error { fn from(error: Rich<'_, char>) -> Self { Error::Lex { @@ -101,42 +163,3 @@ pub struct TypeCheckError { pub actual: 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() -} diff --git a/src/main.rs b/src/main.rs index 1355189..21e36ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use colored::Colorize; 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. #[derive(Parser, Debug)] @@ -52,8 +52,10 @@ fn main() { println!("{value}") } } - Err(errors) => create_report(&errors) - .eprint(Source::from(&source)) - .unwrap(), + Err(errors) => { + for error in errors { + error.report().eprint(Source::from(&source)).unwrap(); + } + } } } diff --git a/src/parser.rs b/src/parser.rs index f97749a..c090ccd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -107,10 +107,10 @@ pub fn parser<'src>() -> DustParser<'src> { infix(left(1), just(Token::Operator("<=")), |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))) }), - infix(left(1), just(Token::Operator("||")), |left, right| { + infix(right(1), just(Token::Operator("||")), |left, right| { Expression::Logic(Box::new(Logic::Or(left, right))) }), )) diff --git a/tests/expressions.rs b/tests/expressions.rs new file mode 100644 index 0000000..7c63d11 --- /dev/null +++ b/tests/expressions.rs @@ -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)) + ); +}