From 24a2642f17ef77b64e6f6d2c7446e4c1e51c2785 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Aug 2024 07:02:55 -0400 Subject: [PATCH] Add modulo support --- README.md | 2 +- dust-lang/src/abstract_tree.rs | 2 ++ dust-lang/src/lex.rs | 26 ++++++++++++++++++++--- dust-lang/src/parse.rs | 38 ++++++++++++++++++++++++++++++++++ dust-lang/src/token.rs | 6 ++++++ dust-lang/src/value.rs | 18 ++++++++++++++++ dust-lang/src/vm.rs | 8 +++++++ rust-toolchain | 1 - std/core.ds | 3 --- std/fs.ds | 5 ----- std/io.ds | 9 -------- std/json.ds | 5 ----- std/thread.ds | 5 ----- 13 files changed, 96 insertions(+), 32 deletions(-) delete mode 100644 rust-toolchain delete mode 100644 std/core.ds delete mode 100644 std/fs.ds delete mode 100644 std/io.ds delete mode 100644 std/json.ds delete mode 100644 std/thread.ds diff --git a/README.md b/README.md index c13dded..9ad0557 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ count_slowly = fn ( async { count_slowly(200) # Finishes last - count_slowly(100) # Finishes seconds + count_slowly(100) # Finishes second count_slowly(50) # Finishes first } ``` diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 699dae4..b095ac8 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -233,6 +233,7 @@ pub enum BinaryOperator { // Math Add, Divide, + Modulo, Multiply, Subtract, @@ -257,6 +258,7 @@ impl Display for BinaryOperator { BinaryOperator::GreaterOrEqual => write!(f, ">="), BinaryOperator::Less => write!(f, "<"), BinaryOperator::LessOrEqual => write!(f, "<="), + BinaryOperator::Modulo => write!(f, "%"), BinaryOperator::Multiply => write!(f, "*"), BinaryOperator::Or => write!(f, "||"), BinaryOperator::Subtract => write!(f, "-"), diff --git a/dust-lang/src/lex.rs b/dust-lang/src/lex.rs index 4509c79..089b47e 100644 --- a/dust-lang/src/lex.rs +++ b/dust-lang/src/lex.rs @@ -119,7 +119,7 @@ impl Lexer { (Token::Minus, (self.position - 1, self.position)) } } - 'a'..='z' | 'A'..='Z' => self.lex_alphabetical(source)?, + 'a'..='z' | 'A'..='Z' => self.lex_alphanumeric(source)?, '"' => self.lex_string('"', source)?, '\'' => self.lex_string('\'', source)?, '+' => { @@ -204,6 +204,11 @@ impl Lexer { (Token::Slash, (self.position - 1, self.position)) } + '%' => { + self.position += 1; + + (Token::Percent, (self.position - 1, self.position)) + } _ => { self.position += 1; @@ -320,14 +325,14 @@ impl Lexer { } /// Lex an identifier token. - fn lex_alphabetical<'src>( + fn lex_alphanumeric<'src>( &mut self, source: &'src str, ) -> Result<(Token<'src>, Span), LexError> { let start_pos = self.position; while let Some(c) = self.peek_char(source) { - if c.is_ascii_alphabetic() || c == '_' { + if c.is_ascii_alphanumeric() || c == '_' { self.next_char(source); } else { break; @@ -430,6 +435,21 @@ impl From for LexError { mod tests { use super::*; + #[test] + fn modulo() { + let input = "42 % 2"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Integer(42), (0, 2)), + (Token::Percent, (3, 4)), + (Token::Integer(2), (5, 6)), + (Token::Eof, (6, 6)), + ]) + ) + } + #[test] fn divide() { let input = "42 / 2"; diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 2622316..50e66e4 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -302,6 +302,23 @@ impl<'src> Parser<'src> { (left_start, right_end), )); } + (Token::Percent, _) => { + let operator = Node::new(BinaryOperator::Modulo, 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), + )); + } _ => {} } } @@ -524,6 +541,7 @@ impl<'src> Parser<'src> { match self.current.0 { Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5, Token::Dot => 4, + Token::Percent => 3, Token::Star => 2, Token::Slash => 2, Token::Plus => 1, @@ -595,6 +613,26 @@ mod tests { use super::*; + #[test] + fn modulo() { + 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::Modulo, (3, 4)), + right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))) + }, + (0, 6) + )] + .into() + }) + ); + } + #[test] fn divide() { let input = "42 / 2"; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index eb20175..eb1da3f 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -37,6 +37,7 @@ pub enum Token<'src> { Less, LessEqual, Minus, + Percent, Plus, RightCurlyBrace, RightParenthesis, @@ -69,6 +70,7 @@ impl<'src> Token<'src> { Token::Less => TokenOwned::Less, Token::LessEqual => TokenOwned::LessOrEqual, Token::Minus => TokenOwned::Minus, + Token::Percent => TokenOwned::Percent, Token::Plus => TokenOwned::Plus, Token::ReadLine => TokenOwned::ReadLine, Token::RightCurlyBrace => TokenOwned::RightCurlyBrace, @@ -104,6 +106,7 @@ impl<'src> Token<'src> { Token::Less => "<", Token::LessEqual => "<=", Token::Minus => "-", + Token::Percent => "%", Token::Plus => "+", Token::ReadLine => "read_line", Token::RightCurlyBrace => "}", @@ -148,6 +151,7 @@ impl<'src> PartialEq for Token<'src> { (Token::Less, Token::Less) => true, (Token::LessEqual, Token::LessEqual) => true, (Token::Minus, Token::Minus) => true, + (Token::Percent, Token::Percent) => true, (Token::Plus, Token::Plus) => true, (Token::ReadLine, Token::ReadLine) => true, (Token::RightCurlyBrace, Token::RightCurlyBrace) => true, @@ -198,6 +202,7 @@ pub enum TokenOwned { Less, LessOrEqual, Minus, + Percent, Plus, RightCurlyBrace, RightParenthesis, @@ -230,6 +235,7 @@ impl Display for TokenOwned { TokenOwned::Less => Token::Less.fmt(f), TokenOwned::LessOrEqual => Token::LessEqual.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f), + TokenOwned::Percent => Token::Percent.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::ReadLine => Token::ReadLine.fmt(f), TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 575fdaa..ea0d45b 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -178,6 +178,20 @@ impl Value { } } + pub fn modulo(&self, other: &Value) -> Result { + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Float(left), ValueInner::Float(right)) => 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::CannotModulo(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)), @@ -778,6 +792,7 @@ pub enum ValueError { CannotGreaterThanOrEqual(Value, Value), CannotLessThan(Value, Value), CannotLessThanOrEqual(Value, Value), + CannotModulo(Value, Value), CannotMultiply(Value, Value), CannotSubtract(Value, Value), CannotOr(Value, Value), @@ -800,6 +815,9 @@ impl Display for ValueError { ValueError::CannotDivide(left, right) => { write!(f, "Cannot divide {} by {}", left, right) } + ValueError::CannotModulo(left, right) => { + write!(f, "Cannot modulo {} by {}", left, right) + } ValueError::CannotMultiply(left, right) => { write!(f, "Cannot multiply {} and {}", left, right) } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 282fd8c..9d56baa 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -102,6 +102,7 @@ impl Vm { } BinaryOperator::Less => left_value.less_than(&right_value), BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value), + BinaryOperator::Modulo => left_value.modulo(&right_value), BinaryOperator::Multiply => left_value.multiply(&right_value), BinaryOperator::Or => left_value.or(&right_value), BinaryOperator::Subtract => left_value.subtract(&right_value), @@ -432,6 +433,13 @@ impl Display for VmError { mod tests { use super::*; + #[test] + fn modulo() { + let input = "42 % 2"; + + assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(0)))); + } + #[test] fn divide() { let input = "42 / 2"; diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index bf867e0..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly diff --git a/std/core.ds b/std/core.ds deleted file mode 100644 index bb1012b..0000000 --- a/std/core.ds +++ /dev/null @@ -1,3 +0,0 @@ -length = fn (list: [any]) -> int { - __LENGTH__(list) -} diff --git a/std/fs.ds b/std/fs.ds deleted file mode 100644 index 536cab4..0000000 --- a/std/fs.ds +++ /dev/null @@ -1,5 +0,0 @@ -fs = { - read_file = fn (path: str) -> str { - __READ_FILE__(path) - } -} diff --git a/std/io.ds b/std/io.ds deleted file mode 100644 index 5300897..0000000 --- a/std/io.ds +++ /dev/null @@ -1,9 +0,0 @@ -io = { - read_line = fn () -> str { - __READ_LINE__() - } - - write_line = fn (output: any) { - __WRITE_LINE__(output) - } -} diff --git a/std/json.ds b/std/json.ds deleted file mode 100644 index ab1454a..0000000 --- a/std/json.ds +++ /dev/null @@ -1,5 +0,0 @@ -json = { - parse = fn (input: str) -> T { - __JSON_PARSE__::(input) - } -} diff --git a/std/thread.ds b/std/thread.ds deleted file mode 100644 index a692227..0000000 --- a/std/thread.ds +++ /dev/null @@ -1,5 +0,0 @@ -thread = { - sleep = fn (milliseconds: int) { - __SLEEP__(milliseconds) - } -}