From d0dba3528591765bbb50480cb652a28a3b21a6f4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 9 Aug 2024 04:56:24 -0400 Subject: [PATCH] Add && and || operators --- dust-lang/src/abstract_tree.rs | 16 +++- dust-lang/src/analyzer.rs | 129 ++++++++++++++++----------------- dust-lang/src/parse.rs | 16 ++-- dust-lang/src/token.rs | 10 +++ dust-lang/src/value.rs | 42 +++++++++-- dust-lang/src/vm.rs | 2 + 6 files changed, 131 insertions(+), 84 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index 269d9ab..14b9bfd 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -85,8 +85,7 @@ impl Statement { .map(|value| value.r#type(variables)), Statement::List(nodes) => nodes .first() - .map(|node| node.inner.expected_type(variables)) - .flatten(), + .and_then(|node| node.inner.expected_type(variables)), Statement::PropertyAccess(_, _) => None, } } @@ -197,26 +196,35 @@ impl Display for Statement { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum BinaryOperator { + // Math Add, Divide, + Multiply, + Subtract, + + // Comparison Greater, GreaterOrEqual, Less, LessOrEqual, - Multiply, - Subtract, + + // Logic + And, + Or, } impl Display for BinaryOperator { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { BinaryOperator::Add => write!(f, "+"), + BinaryOperator::And => write!(f, "&&"), BinaryOperator::Divide => write!(f, "/"), BinaryOperator::Greater => write!(f, ">"), BinaryOperator::GreaterOrEqual => write!(f, ">="), BinaryOperator::Less => write!(f, "<"), BinaryOperator::LessOrEqual => write!(f, "<="), BinaryOperator::Multiply => write!(f, "*"), + BinaryOperator::Or => write!(f, "||"), BinaryOperator::Subtract => write!(f, "-"), } } diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 4ec723f..27ba33c 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -78,8 +78,7 @@ impl<'a> Analyzer<'a> { fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> { match &node.inner { Statement::Assignment { - identifier, - value_node: value, + value_node: value, .. } => { if let None = value.inner.expected_type(self.variables) { return Err(AnalyzerError::ExpectedValue { @@ -95,6 +94,49 @@ impl<'a> Analyzer<'a> { } => { self.analyze_node(left)?; self.analyze_node(right)?; + + let left_type = left.inner.expected_type(self.variables); + let right_type = right.inner.expected_type(self.variables); + + if let BinaryOperator::Add + | BinaryOperator::Subtract + | BinaryOperator::Multiply + | BinaryOperator::Divide + | BinaryOperator::Greater + | BinaryOperator::GreaterOrEqual + | BinaryOperator::Less + | BinaryOperator::LessOrEqual = operator.inner + { + match (left_type, right_type) { + (Some(Type::Integer), Some(Type::Integer)) => {} + (Some(Type::Float), Some(Type::Float)) => {} + (Some(Type::String), Some(Type::String)) => {} + (Some(Type::Integer), _) => { + return Err(AnalyzerError::ExpectedInteger { + actual: right.as_ref().clone(), + position: right.position, + }); + } + (Some(Type::Float), _) => { + return Err(AnalyzerError::ExpectedFloat { + actual: right.as_ref().clone(), + position: right.position, + }); + } + (Some(Type::String), _) => { + return Err(AnalyzerError::ExpectedString { + actual: right.as_ref().clone(), + position: right.position, + }); + } + (_, _) => { + return Err(AnalyzerError::ExpectedIntegerFloatOrString { + actual: right.as_ref().clone(), + position: right.position, + }) + } + } + } } Statement::BuiltInFunctionCall { .. } => {} Statement::Constant(_) => {} @@ -181,6 +223,11 @@ pub enum AnalyzerError { actual: Node, position: Span, }, + ExpectedSameType { + left: Node, + right: Node, + position: Span, + }, ExpectedString { actual: Node, position: (usize, usize), @@ -210,6 +257,7 @@ impl AnalyzerError { AnalyzerError::ExpectedInteger { position, .. } => *position, AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position, AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position, + AnalyzerError::ExpectedSameType { position, .. } => *position, AnalyzerError::ExpectedString { position, .. } => *position, AnalyzerError::UnexpectedIdentifier { position, .. } => *position, AnalyzerError::UnexectedString { position, .. } => *position, @@ -243,6 +291,9 @@ impl Display for AnalyzerError { AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => { write!(f, "Expected integer, float, or string, found {}", actual) } + AnalyzerError::ExpectedSameType { left, right, .. } => { + write!(f, "Expected same type, found {} and {}", left, right) + } AnalyzerError::ExpectedString { actual, .. } => { write!(f, "Expected string, found {}", actual) } @@ -266,15 +317,15 @@ mod tests { use super::*; #[test] - fn add_expects_same_types() { + fn float_plus_integer() { let abstract_tree = AbstractSyntaxTree { nodes: [Node::new( Statement::BinaryOperation { - left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), + left: Box::new(Node::new(Statement::Constant(Value::float(1.0)), (0, 1))), operator: Node::new(BinaryOperator::Add, (1, 2)), - right: Box::new(Node::new(Statement::Constant(Value::float(1.0)), (3, 4))), + right: Box::new(Node::new(Statement::Constant(Value::integer(1)), (3, 4))), }, - (0, 4), + (0, 2), )] .into(), }; @@ -283,15 +334,15 @@ mod tests { assert_eq!( analyzer.analyze(), - Err(AnalyzerError::ExpectedIntegerFloatOrString { - actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2)), - position: (1, 2) + Err(AnalyzerError::ExpectedFloat { + actual: Node::new(Statement::Constant(Value::integer(1)), (3, 4)), + position: (3, 4) }) ) } #[test] - fn add_expects_integer_float_or_string() { + fn integer_plus_boolean() { let abstract_tree = AbstractSyntaxTree { nodes: [Node::new( Statement::BinaryOperation { @@ -308,9 +359,9 @@ mod tests { assert_eq!( analyzer.analyze(), - Err(AnalyzerError::ExpectedIntegerFloatOrString { - actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)), - position: (0, 1) + Err(AnalyzerError::ExpectedInteger { + actual: Node::new(Statement::Constant(Value::boolean(true)), (3, 4)), + position: (3, 4) }) ) } @@ -376,58 +427,6 @@ mod tests { ) } - #[test] - fn multiply_expect_integer_or_float() { - let abstract_tree = AbstractSyntaxTree { - nodes: [Node::new( - Statement::BinaryOperation { - left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), - operator: Node::new(BinaryOperator::Multiply, (1, 2)), - right: Box::new(Node::new( - Statement::Constant(Value::boolean(false)), - (3, 4), - )), - }, - (0, 2), - )] - .into(), - }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); - - assert_eq!( - analyzer.analyze(), - Err(AnalyzerError::ExpectedIntegerOrFloat { - actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2)), - position: (1, 2) - }) - ) - } - - #[test] - fn assignment_expect_identifier() { - let abstract_tree = AbstractSyntaxTree { - nodes: [Node::new( - Statement::Assignment { - identifier: Identifier::new("x"), - value_node: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), - }, - (0, 2), - )] - .into(), - }; - let variables = HashMap::new(); - let analyzer = Analyzer::new(&abstract_tree, &variables); - - assert_eq!( - analyzer.analyze(), - Err(AnalyzerError::ExpectedIdentifier { - actual: Node::new(Statement::Constant(Value::integer(1)), (0, 1)), - position: (0, 1) - }) - ) - } - #[test] fn unexpected_identifier() { let abstract_tree = AbstractSyntaxTree { diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 73de1b2..544143b 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -582,7 +582,7 @@ mod tests { nodes: [Node::new( Statement::BinaryOperation { left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), - operator: Node::new(BinaryOperator::Less, (2, 4)), + operator: Node::new(BinaryOperator::LessOrEqual, (2, 4)), right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))), }, (0, 6) @@ -602,7 +602,7 @@ mod tests { nodes: [Node::new( Statement::BinaryOperation { left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), - operator: Node::new(BinaryOperator::Greater, (2, 4)), + operator: Node::new(BinaryOperator::GreaterOrEqual, (2, 4)), right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))), }, (0, 6) @@ -641,9 +641,9 @@ mod tests { Ok(AbstractSyntaxTree { nodes: [Node::new( Statement::BinaryOperation { - left: Node::new(Statement::Constant(Value::integer(1)), (1, 2)).into(), + left: Node::new(Statement::Constant(Value::integer(-1)), (0, 2)).into(), operator: Node::new(BinaryOperator::Subtract, (3, 4)), - right: Node::new(Statement::Constant(Value::integer(2)), (6, 7)).into() + right: Node::new(Statement::Constant(Value::integer(-2)), (5, 7)).into() }, (0, 7) )] @@ -663,15 +663,15 @@ mod tests { Statement::BinaryOperation { left: Box::new(Node::new( Statement::Constant(Value::string("Hello, ")), - (0, 8) + (0, 9) )), - operator: Node::new(BinaryOperator::Add, (9, 10)), + operator: Node::new(BinaryOperator::Add, (10, 11)), right: Box::new(Node::new( Statement::Constant(Value::string("World!")), - (11, 19) + (12, 20) )) }, - (0, 19) + (0, 20) )] .into() }) diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index a7708a6..cd55aca 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -26,6 +26,8 @@ pub enum Token<'src> { // Symbols Comma, Dot, + DoubleAmpersand, + DoublePipe, Equal, Greater, GreaterEqual, @@ -46,6 +48,8 @@ impl<'src> Token<'src> { Token::Boolean(boolean) => TokenOwned::Boolean(*boolean), Token::Comma => TokenOwned::Comma, Token::Dot => TokenOwned::Dot, + Token::DoubleAmpersand => TokenOwned::DoubleAmpersand, + Token::DoublePipe => TokenOwned::DoublePipe, Token::Eof => TokenOwned::Eof, Token::Equal => TokenOwned::Equal, Token::Float(float) => TokenOwned::Float(*float), @@ -76,6 +80,8 @@ impl<'src> Token<'src> { Token::Boolean(_) => "boolean", Token::Comma => ",", Token::Dot => ".", + Token::DoubleAmpersand => "&&", + Token::DoublePipe => "||", Token::Eof => "EOF", Token::Equal => "=", Token::Float(_) => "float", @@ -169,6 +175,8 @@ pub enum TokenOwned { // Symbols Comma, Dot, + DoubleAmpersand, + DoublePipe, Equal, Greater, GreaterOrEqual, @@ -189,6 +197,8 @@ impl Display for TokenOwned { TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Comma => Token::Comma.fmt(f), TokenOwned::Dot => Token::Dot.fmt(f), + TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f), + TokenOwned::DoublePipe => Token::DoublePipe.fmt(f), TokenOwned::Eof => Token::Eof.fmt(f), TokenOwned::Equal => Token::Equal.fmt(f), TokenOwned::Float(float) => write!(f, "{float}"), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 7aaf1ab..8090824 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -207,6 +207,24 @@ impl Value { )), } } + + pub fn and(&self, other: &Value) -> Result { + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Boolean(left), ValueInner::Boolean(right)) => { + Ok(Value::boolean(*left && *right)) + } + _ => Err(ValueError::CannotAnd(self.clone(), other.clone())), + } + } + + pub fn or(&self, other: &Value) -> Result { + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Boolean(left), ValueInner::Boolean(right)) => { + Ok(Value::boolean(*left || *right)) + } + _ => Err(ValueError::CannotOr(self.clone(), other.clone())), + } + } } impl Display for Value { @@ -734,15 +752,16 @@ impl Display for Function { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ValueError { CannotAdd(Value, Value), - CannotMultiply(Value, Value), - CannotSubtract(Value, Value), - CannotLessThan(Value, Value), - CannotLessThanOrEqual(Value, Value), + CannotAnd(Value, Value), CannotGreaterThan(Value, Value), CannotGreaterThanOrEqual(Value, Value), + CannotLessThan(Value, Value), + CannotLessThanOrEqual(Value, Value), + CannotMultiply(Value, Value), + CannotSubtract(Value, Value), + CannotOr(Value, Value), ExpectedList(Value), IndexOutOfBounds { value: Value, index: i64 }, - PropertyNotFound { value: Value, property: Identifier }, } impl Error for ValueError {} @@ -751,6 +770,11 @@ impl Display for ValueError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right), + ValueError::CannotAnd(left, right) => write!( + f, + "Cannot use logical and operation on {} and {}", + left, right + ), ValueError::CannotMultiply(left, right) => { write!(f, "Cannot multiply {} and {}", left, right) } @@ -763,8 +787,12 @@ impl Display for ValueError { | ValueError::CannotGreaterThanOrEqual(left, right) => { write!(f, "Cannot compare {} and {}", left, right) } - ValueError::PropertyNotFound { value, property } => { - write!(f, "{} does not have a property named {}", value, property) + ValueError::CannotOr(left, right) => { + write!( + f, + "Cannot use logical or operation on {} and {}", + left, right + ) } 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 5a13278..11dc723 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -94,6 +94,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::Greater => left_value.greater_than(&right_value), BinaryOperator::GreaterOrEqual => { @@ -102,6 +103,7 @@ impl Vm { BinaryOperator::Less => left_value.less_than(&right_value), BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value), BinaryOperator::Multiply => left_value.multiply(&right_value), + BinaryOperator::Or => left_value.or(&right_value), BinaryOperator::Subtract => left_value.subtract(&right_value), } .map_err(|value_error| VmError::ValueError {