From 2cf580d111282d4d24f1442dc2276cb91fc191ae Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Aug 2024 06:46:24 -0400 Subject: [PATCH] Add division --- dust-lang/src/lex.rs | 20 ++++++++++++++++++++ dust-lang/src/parse.rs | 38 ++++++++++++++++++++++++++++++++++++++ dust-lang/src/token.rs | 6 ++++++ dust-lang/src/value.rs | 26 ++++++++++++++++++++++++++ dust-lang/src/vm.rs | 12 +++++++++++- 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/dust-lang/src/lex.rs b/dust-lang/src/lex.rs index a9a4008..4509c79 100644 --- a/dust-lang/src/lex.rs +++ b/dust-lang/src/lex.rs @@ -199,6 +199,11 @@ impl Lexer { (Token::RightCurlyBrace, (self.position - 1, self.position)) } + '/' => { + self.position += 1; + + (Token::Slash, (self.position - 1, self.position)) + } _ => { self.position += 1; @@ -425,6 +430,21 @@ impl From for LexError { mod tests { use super::*; + #[test] + fn divide() { + let input = "42 / 2"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Integer(42), (0, 2)), + (Token::Slash, (3, 4)), + (Token::Integer(2), (5, 6)), + (Token::Eof, (6, 6)), + ]) + ) + } + #[test] fn map() { let input = "{ x = 42, y = 'foobar' }"; diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index a3b494b..2622316 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -285,6 +285,23 @@ impl<'src> Parser<'src> { (left_start, right_end), )); } + (Token::Slash, _) => { + let operator = Node::new(BinaryOperator::Divide, self.current.1); + + self.next_token()?; + + let right_node = self.parse_node(self.current_precedence())?; + let right_end = right_node.position.1; + + return Ok(Node::new( + Statement::BinaryOperation { + left: Box::new(left_node), + operator, + right: Box::new(right_node), + }, + (left_start, right_end), + )); + } _ => {} } } @@ -508,6 +525,7 @@ impl<'src> Parser<'src> { Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5, Token::Dot => 4, Token::Star => 2, + Token::Slash => 2, Token::Plus => 1, Token::Minus => 1, _ => 0, @@ -577,6 +595,26 @@ mod tests { use super::*; + #[test] + fn divide() { + let input = "42 / 2"; + + assert_eq!( + parse(input), + Ok(AbstractSyntaxTree { + nodes: [Node::new( + Statement::BinaryOperation { + left: Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))), + operator: Node::new(BinaryOperator::Divide, (3, 4)), + right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))) + }, + (0, 6) + )] + .into() + }) + ); + } + #[test] fn malformed_assignment() { let input = "false = 1"; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index 46fcfb2..eb20175 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -41,6 +41,7 @@ pub enum Token<'src> { RightCurlyBrace, RightParenthesis, RightSquareBrace, + Slash, Star, } @@ -74,6 +75,7 @@ impl<'src> Token<'src> { Token::RightParenthesis => TokenOwned::RightParenthesis, Token::RightSquareBrace => TokenOwned::RightSquareBrace, Token::Star => TokenOwned::Star, + Token::Slash => TokenOwned::Slash, Token::String(text) => TokenOwned::String(text.to_string()), Token::WriteLine => TokenOwned::WriteLine, } @@ -109,6 +111,7 @@ impl<'src> Token<'src> { Token::RightSquareBrace => "]", Token::Star => "*", Token::String(_) => "string", + Token::Slash => "/", Token::WriteLine => "write_line", } } @@ -151,6 +154,7 @@ impl<'src> PartialEq for Token<'src> { (Token::RightParenthesis, Token::RightParenthesis) => true, (Token::RightSquareBrace, Token::RightSquareBrace) => true, (Token::Star, Token::Star) => true, + (Token::Slash, Token::Slash) => true, (Token::String(left), Token::String(right)) => left == right, (Token::WriteLine, Token::WriteLine) => true, _ => false, @@ -199,6 +203,7 @@ pub enum TokenOwned { RightParenthesis, RightSquareBrace, Star, + Slash, } impl Display for TokenOwned { @@ -231,6 +236,7 @@ impl Display for TokenOwned { TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f), TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f), TokenOwned::Star => Token::Star.fmt(f), + TokenOwned::Slash => Token::Slash.fmt(f), TokenOwned::String(string) => write!(f, "{string}"), TokenOwned::WriteLine => Token::WriteLine.fmt(f), } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 8090824..575fdaa 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -158,6 +158,26 @@ impl Value { } } + pub fn divide(&self, other: &Value) -> Result { + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Float(left), ValueInner::Float(right)) => { + if right == &0.0 { + Err(ValueError::DivisionByZero) + } else { + Ok(Value::float(left / right)) + } + } + (ValueInner::Integer(left), ValueInner::Integer(right)) => { + if right == &0 { + Err(ValueError::DivisionByZero) + } else { + Ok(Value::integer(left / right)) + } + } + _ => Err(ValueError::CannotDivide(self.clone(), other.clone())), + } + } + pub fn less_than(&self, other: &Value) -> Result { match (self.inner().as_ref(), other.inner().as_ref()) { (ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::boolean(left < right)), @@ -753,6 +773,7 @@ impl Display for Function { pub enum ValueError { CannotAdd(Value, Value), CannotAnd(Value, Value), + CannotDivide(Value, Value), CannotGreaterThan(Value, Value), CannotGreaterThanOrEqual(Value, Value), CannotLessThan(Value, Value), @@ -760,6 +781,7 @@ pub enum ValueError { CannotMultiply(Value, Value), CannotSubtract(Value, Value), CannotOr(Value, Value), + DivisionByZero, ExpectedList(Value), IndexOutOfBounds { value: Value, index: i64 }, } @@ -775,6 +797,9 @@ impl Display for ValueError { "Cannot use logical and operation on {} and {}", left, right ), + ValueError::CannotDivide(left, right) => { + write!(f, "Cannot divide {} by {}", left, right) + } ValueError::CannotMultiply(left, right) => { write!(f, "Cannot multiply {} and {}", left, right) } @@ -794,6 +819,7 @@ impl Display for ValueError { left, right ) } + ValueError::DivisionByZero => write!(f, "Division by zero"), ValueError::IndexOutOfBounds { value, index } => { write!(f, "{} does not have an index of {}", value, index) } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 05c6447..282fd8c 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -95,7 +95,7 @@ impl Vm { let result = match operator.inner { BinaryOperator::Add => left_value.add(&right_value), BinaryOperator::And => left_value.and(&right_value), - BinaryOperator::Divide => todo!(), + BinaryOperator::Divide => left_value.divide(&right_value), BinaryOperator::Greater => left_value.greater_than(&right_value), BinaryOperator::GreaterOrEqual => { left_value.greater_than_or_equal(&right_value) @@ -432,6 +432,16 @@ impl Display for VmError { mod tests { use super::*; + #[test] + fn divide() { + let input = "42 / 2"; + + assert_eq!( + run(input, &mut HashMap::new()), + Ok(Some(Value::integer(21))) + ); + } + #[test] fn less_than() { let input = "2 < 3";