From c51b14213015f43a3383d4a7f00b3ecec2187d0b Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 7 Mar 2024 06:33:54 -0500 Subject: [PATCH] Add math --- src/abstract_tree/expression.rs | 14 ++++-- src/abstract_tree/math.rs | 86 +++++++++++++++++++++++++++++++++ src/abstract_tree/mod.rs | 4 +- src/error.rs | 7 +++ src/lexer.rs | 20 ++++++-- src/parser.rs | 37 +++++++++++++- src/value.rs | 8 +++ 7 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 src/abstract_tree/math.rs diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index f5ec37c..3fa742a 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -4,12 +4,13 @@ use crate::{ Value, }; -use super::{AbstractTree, Identifier, Logic, Type, ValueNode}; +use super::{AbstractTree, Identifier, Logic, Math, Type, ValueNode}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Expression<'src> { Identifier(Identifier), Logic(Box>), + Math(Box>), Value(ValueNode<'src>), } @@ -18,6 +19,7 @@ impl<'src> AbstractTree for Expression<'src> { match self { Expression::Identifier(identifier) => identifier.expected_type(_context), Expression::Logic(logic) => logic.expected_type(_context), + Expression::Math(math) => math.expected_type(_context), Expression::Value(value_node) => value_node.expected_type(_context), } } @@ -26,15 +28,17 @@ impl<'src> AbstractTree for Expression<'src> { match self { Expression::Identifier(identifier) => identifier.validate(_context), Expression::Logic(logic) => logic.validate(_context), + Expression::Math(math) => math.validate(_context), Expression::Value(value_node) => value_node.validate(_context), } } - fn run(self, context: &Context) -> Result { + fn run(self, _context: &Context) -> Result { match self { - Expression::Identifier(identifier) => identifier.run(context), - Expression::Logic(logic) => logic.run(context), - Expression::Value(value_node) => value_node.run(context), + Expression::Identifier(identifier) => identifier.run(_context), + Expression::Logic(logic) => logic.run(_context), + Expression::Math(math) => math.run(_context), + Expression::Value(value_node) => value_node.run(_context), } } } diff --git a/src/abstract_tree/math.rs b/src/abstract_tree/math.rs new file mode 100644 index 0000000..d22a54c --- /dev/null +++ b/src/abstract_tree/math.rs @@ -0,0 +1,86 @@ +use crate::{ + context::Context, + error::{RuntimeError, ValidationError}, + value::ValueInner, + Value, +}; + +use super::{AbstractTree, Expression, Type}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum Math<'src> { + Add(Expression<'src>, Expression<'src>), + Subtract(Expression<'src>, Expression<'src>), + Multiply(Expression<'src>, Expression<'src>), + Divide(Expression<'src>, Expression<'src>), + Modulo(Expression<'src>, Expression<'src>), +} + +impl<'src> AbstractTree for Math<'src> { + fn expected_type(&self, _context: &Context) -> Result { + match self { + Math::Add(left, _) + | Math::Subtract(left, _) + | Math::Multiply(left, _) + | Math::Divide(left, _) + | Math::Modulo(left, _) => left.expected_type(_context), + } + } + + fn validate(&self, context: &Context) -> Result<(), ValidationError> { + match self { + Math::Add(left, right) + | Math::Subtract(left, right) + | Math::Multiply(left, right) + | Math::Divide(left, right) + | Math::Modulo(left, right) => { + let left_type = left.expected_type(context)?; + let right_type = right.expected_type(context)?; + + match (left_type, right_type) { + (Type::Integer, Type::Integer) + | (Type::Float, Type::Float) + | (Type::Integer, Type::Float) + | (Type::Float, Type::Integer) => Ok(()), + _ => Err(ValidationError::ExpectedIntegerOrFloat), + } + } + } + } + + fn run(self, context: &Context) -> Result { + match self { + Math::Add(left, right) => { + let left_value = left.run(context)?; + let right_value = right.run(context)?; + + if let (ValueInner::Integer(left), ValueInner::Integer(right)) = + (left_value.inner().as_ref(), right_value.inner().as_ref()) + { + Ok(Value::integer(left + right)) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedIntegerOrFloat, + )) + } + } + Math::Subtract(_, _) => todo!(), + Math::Multiply(left, right) => { + let left_value = left.run(context)?; + let right_value = right.run(context)?; + + if let (ValueInner::Integer(left), ValueInner::Integer(right)) = + (left_value.inner().as_ref(), right_value.inner().as_ref()) + { + Ok(Value::integer(left * right)) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedIntegerOrFloat, + )) + } + } + Math::Divide(_, _) => todo!(), + Math::Modulo(_, _) => todo!(), + } + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index 0684e5c..c38fefa 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -4,13 +4,15 @@ pub mod expression; pub mod identifier; pub mod logic; pub mod r#loop; +pub mod math; pub mod statement; pub mod r#type; pub mod value_node; pub use self::{ assignment::Assignment, block::Block, expression::Expression, identifier::Identifier, - logic::Logic, r#loop::Loop, r#type::Type, statement::Statement, value_node::ValueNode, + logic::Logic, math::Math, r#loop::Loop, r#type::Type, statement::Statement, + value_node::ValueNode, }; use crate::{ diff --git a/src/error.rs b/src/error.rs index 5b33579..f653b22 100644 --- a/src/error.rs +++ b/src/error.rs @@ -67,6 +67,12 @@ impl Error { Label::new(span.start..span.end).with_message("Expected boolean."), ); } + ValidationError::ExpectedIntegerOrFloat => { + report = report.with_label( + Label::new(span.start..span.end) + .with_message("Expected integer or float."), + ); + } ValidationError::RwLockPoison(_) => todo!(), ValidationError::TypeCheck(TypeCheckError { actual, expected }) => { report = report.with_label(Label::new(span.start..span.end).with_message( @@ -132,6 +138,7 @@ impl From for RuntimeError { #[derive(Debug, PartialEq)] pub enum ValidationError { ExpectedBoolean, + ExpectedIntegerOrFloat, RwLockPoison(RwLockPoisonError), TypeCheck(TypeCheckError), VariableNotFound(Identifier), diff --git a/src/lexer.rs b/src/lexer.rs index 327ea0c..c2aa7b0 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -144,16 +144,16 @@ pub fn lexer<'src>() -> impl Parser< just("!").padded().to(Operator::Not), just("!=").padded().to(Operator::NotEqual), just("||").padded().to(Operator::Or), + // assignment + just("=").padded().to(Operator::Assign), + just("+=").padded().to(Operator::AddAssign), + just("-=").padded().to(Operator::SubAssign), // math just("+").padded().to(Operator::Add), just("-").padded().to(Operator::Subtract), just("*").padded().to(Operator::Multiply), just("/").padded().to(Operator::Divide), just("%").padded().to(Operator::Modulo), - // assignment - just("=").padded().to(Operator::Assign), - just("+=").padded().to(Operator::AddAssign), - just("-=").padded().to(Operator::SubAssign), )) .map(Token::Operator); @@ -196,6 +196,18 @@ pub fn lexer<'src>() -> impl Parser< mod tests { use super::*; + #[test] + fn math_operators() { + assert_eq!( + lex("1 + 1").unwrap(), + vec![ + (Token::Integer(1), (0..1).into()), + (Token::Operator(Operator::Add), (2..4).into()), + (Token::Integer(1), (4..5).into()) + ] + ) + } + #[test] fn keywords() { assert_eq!(lex("int").unwrap()[0].0, Token::Keyword("int")) diff --git a/src/parser.rs b/src/parser.rs index e5c0d1c..d96b609 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -90,7 +90,8 @@ pub fn parser<'src>() -> DustParser<'src> { use Operator::*; - let logic = atom + let logic_and_math = atom + .clone() .pratt(( prefix(2, just(Token::Operator(Not)), |expression| { Expression::Logic(Box::new(Logic::Not(expression))) @@ -123,10 +124,31 @@ pub fn parser<'src>() -> DustParser<'src> { infix(left(1), just(Token::Operator(Or)), |left, right| { Expression::Logic(Box::new(Logic::Or(left, right))) }), + infix(left(1), just(Token::Operator(Add)), |left, right| { + Expression::Math(Box::new(Math::Add(left, right))) + }), + infix(left(1), just(Token::Operator(Subtract)), |left, right| { + Expression::Math(Box::new(Math::Subtract(left, right))) + }), + infix(left(2), just(Token::Operator(Multiply)), |left, right| { + Expression::Math(Box::new(Math::Multiply(left, right))) + }), + infix(left(2), just(Token::Operator(Divide)), |left, right| { + Expression::Math(Box::new(Math::Divide(left, right))) + }), + infix(left(1), just(Token::Operator(Modulo)), |left, right| { + Expression::Math(Box::new(Math::Modulo(left, right))) + }), )) .boxed(); - choice((r#enum, logic, identifier_expression, list, basic_value)) + choice(( + r#enum, + logic_and_math, + identifier_expression, + list, + basic_value, + )) }); let statement = recursive(|statement| { @@ -208,6 +230,17 @@ mod tests { use super::*; + #[test] + fn math() { + assert_eq!( + parse(&lex("1 + 1").unwrap()).unwrap()[0].0, + Statement::Expression(Expression::Math(Box::new(Math::Add( + Expression::Value(ValueNode::Integer(1)), + Expression::Value(ValueNode::Integer(1)) + )))) + ); + } + #[test] fn r#loop() { assert_eq!( diff --git a/src/value.rs b/src/value.rs index f0f64cf..782e458 100644 --- a/src/value.rs +++ b/src/value.rs @@ -95,6 +95,14 @@ impl Value { Err(ValidationError::ExpectedBoolean) } + pub fn as_number(&self) -> Result { + if let ValueInner::Boolean(boolean) = self.0.as_ref() { + return Ok(*boolean); + } + + Err(ValidationError::ExpectedBoolean) + } + pub fn is_none(&self) -> bool { self == get_none() }