1
0

Add && and || operators

This commit is contained in:
Jeff 2024-08-09 04:56:24 -04:00
parent 8c8fde94ce
commit d0dba35285
6 changed files with 131 additions and 84 deletions

View File

@ -85,8 +85,7 @@ impl Statement {
.map(|value| value.r#type(variables)), .map(|value| value.r#type(variables)),
Statement::List(nodes) => nodes Statement::List(nodes) => nodes
.first() .first()
.map(|node| node.inner.expected_type(variables)) .and_then(|node| node.inner.expected_type(variables)),
.flatten(),
Statement::PropertyAccess(_, _) => None, Statement::PropertyAccess(_, _) => None,
} }
} }
@ -197,26 +196,35 @@ impl Display for Statement {
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BinaryOperator { pub enum BinaryOperator {
// Math
Add, Add,
Divide, Divide,
Multiply,
Subtract,
// Comparison
Greater, Greater,
GreaterOrEqual, GreaterOrEqual,
Less, Less,
LessOrEqual, LessOrEqual,
Multiply,
Subtract, // Logic
And,
Or,
} }
impl Display for BinaryOperator { impl Display for BinaryOperator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
BinaryOperator::Add => write!(f, "+"), BinaryOperator::Add => write!(f, "+"),
BinaryOperator::And => write!(f, "&&"),
BinaryOperator::Divide => write!(f, "/"), BinaryOperator::Divide => write!(f, "/"),
BinaryOperator::Greater => write!(f, ">"), BinaryOperator::Greater => write!(f, ">"),
BinaryOperator::GreaterOrEqual => write!(f, ">="), BinaryOperator::GreaterOrEqual => write!(f, ">="),
BinaryOperator::Less => write!(f, "<"), BinaryOperator::Less => write!(f, "<"),
BinaryOperator::LessOrEqual => write!(f, "<="), BinaryOperator::LessOrEqual => write!(f, "<="),
BinaryOperator::Multiply => write!(f, "*"), BinaryOperator::Multiply => write!(f, "*"),
BinaryOperator::Or => write!(f, "||"),
BinaryOperator::Subtract => write!(f, "-"), BinaryOperator::Subtract => write!(f, "-"),
} }
} }

View File

@ -78,8 +78,7 @@ impl<'a> Analyzer<'a> {
fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> { fn analyze_node(&self, node: &Node<Statement>) -> Result<(), AnalyzerError> {
match &node.inner { match &node.inner {
Statement::Assignment { Statement::Assignment {
identifier, value_node: value, ..
value_node: value,
} => { } => {
if let None = value.inner.expected_type(self.variables) { if let None = value.inner.expected_type(self.variables) {
return Err(AnalyzerError::ExpectedValue { return Err(AnalyzerError::ExpectedValue {
@ -95,6 +94,49 @@ impl<'a> Analyzer<'a> {
} => { } => {
self.analyze_node(left)?; self.analyze_node(left)?;
self.analyze_node(right)?; 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::BuiltInFunctionCall { .. } => {}
Statement::Constant(_) => {} Statement::Constant(_) => {}
@ -181,6 +223,11 @@ pub enum AnalyzerError {
actual: Node<Statement>, actual: Node<Statement>,
position: Span, position: Span,
}, },
ExpectedSameType {
left: Node<Statement>,
right: Node<Statement>,
position: Span,
},
ExpectedString { ExpectedString {
actual: Node<Statement>, actual: Node<Statement>,
position: (usize, usize), position: (usize, usize),
@ -210,6 +257,7 @@ impl AnalyzerError {
AnalyzerError::ExpectedInteger { position, .. } => *position, AnalyzerError::ExpectedInteger { position, .. } => *position,
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position, AnalyzerError::ExpectedIntegerOrFloat { position, .. } => *position,
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position, AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => *position,
AnalyzerError::ExpectedSameType { position, .. } => *position,
AnalyzerError::ExpectedString { position, .. } => *position, AnalyzerError::ExpectedString { position, .. } => *position,
AnalyzerError::UnexpectedIdentifier { position, .. } => *position, AnalyzerError::UnexpectedIdentifier { position, .. } => *position,
AnalyzerError::UnexectedString { position, .. } => *position, AnalyzerError::UnexectedString { position, .. } => *position,
@ -243,6 +291,9 @@ impl Display for AnalyzerError {
AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => { AnalyzerError::ExpectedIntegerFloatOrString { actual, .. } => {
write!(f, "Expected integer, float, or string, found {}", 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, .. } => { AnalyzerError::ExpectedString { actual, .. } => {
write!(f, "Expected string, found {}", actual) write!(f, "Expected string, found {}", actual)
} }
@ -266,15 +317,15 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn add_expects_same_types() { fn float_plus_integer() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::BinaryOperation { 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)), 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(), .into(),
}; };
@ -283,15 +334,15 @@ mod tests {
assert_eq!( assert_eq!(
analyzer.analyze(), analyzer.analyze(),
Err(AnalyzerError::ExpectedIntegerFloatOrString { Err(AnalyzerError::ExpectedFloat {
actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2)), actual: Node::new(Statement::Constant(Value::integer(1)), (3, 4)),
position: (1, 2) position: (3, 4)
}) })
) )
} }
#[test] #[test]
fn add_expects_integer_float_or_string() { fn integer_plus_boolean() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::BinaryOperation { Statement::BinaryOperation {
@ -308,9 +359,9 @@ mod tests {
assert_eq!( assert_eq!(
analyzer.analyze(), analyzer.analyze(),
Err(AnalyzerError::ExpectedIntegerFloatOrString { Err(AnalyzerError::ExpectedInteger {
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)), actual: Node::new(Statement::Constant(Value::boolean(true)), (3, 4)),
position: (0, 1) 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] #[test]
fn unexpected_identifier() { fn unexpected_identifier() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {

View File

@ -582,7 +582,7 @@ mod tests {
nodes: [Node::new( nodes: [Node::new(
Statement::BinaryOperation { Statement::BinaryOperation {
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), 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))), right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))),
}, },
(0, 6) (0, 6)
@ -602,7 +602,7 @@ mod tests {
nodes: [Node::new( nodes: [Node::new(
Statement::BinaryOperation { Statement::BinaryOperation {
left: Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))), 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))), right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6))),
}, },
(0, 6) (0, 6)
@ -641,9 +641,9 @@ mod tests {
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::BinaryOperation { 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)), 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) (0, 7)
)] )]
@ -663,15 +663,15 @@ mod tests {
Statement::BinaryOperation { Statement::BinaryOperation {
left: Box::new(Node::new( left: Box::new(Node::new(
Statement::Constant(Value::string("Hello, ")), 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( right: Box::new(Node::new(
Statement::Constant(Value::string("World!")), Statement::Constant(Value::string("World!")),
(11, 19) (12, 20)
)) ))
}, },
(0, 19) (0, 20)
)] )]
.into() .into()
}) })

View File

@ -26,6 +26,8 @@ pub enum Token<'src> {
// Symbols // Symbols
Comma, Comma,
Dot, Dot,
DoubleAmpersand,
DoublePipe,
Equal, Equal,
Greater, Greater,
GreaterEqual, GreaterEqual,
@ -46,6 +48,8 @@ impl<'src> Token<'src> {
Token::Boolean(boolean) => TokenOwned::Boolean(*boolean), Token::Boolean(boolean) => TokenOwned::Boolean(*boolean),
Token::Comma => TokenOwned::Comma, Token::Comma => TokenOwned::Comma,
Token::Dot => TokenOwned::Dot, Token::Dot => TokenOwned::Dot,
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
Token::DoublePipe => TokenOwned::DoublePipe,
Token::Eof => TokenOwned::Eof, Token::Eof => TokenOwned::Eof,
Token::Equal => TokenOwned::Equal, Token::Equal => TokenOwned::Equal,
Token::Float(float) => TokenOwned::Float(*float), Token::Float(float) => TokenOwned::Float(*float),
@ -76,6 +80,8 @@ impl<'src> Token<'src> {
Token::Boolean(_) => "boolean", Token::Boolean(_) => "boolean",
Token::Comma => ",", Token::Comma => ",",
Token::Dot => ".", Token::Dot => ".",
Token::DoubleAmpersand => "&&",
Token::DoublePipe => "||",
Token::Eof => "EOF", Token::Eof => "EOF",
Token::Equal => "=", Token::Equal => "=",
Token::Float(_) => "float", Token::Float(_) => "float",
@ -169,6 +175,8 @@ pub enum TokenOwned {
// Symbols // Symbols
Comma, Comma,
Dot, Dot,
DoubleAmpersand,
DoublePipe,
Equal, Equal,
Greater, Greater,
GreaterOrEqual, GreaterOrEqual,
@ -189,6 +197,8 @@ impl Display for TokenOwned {
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"), TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Comma => Token::Comma.fmt(f), TokenOwned::Comma => Token::Comma.fmt(f),
TokenOwned::Dot => Token::Dot.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::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f), TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Float(float) => write!(f, "{float}"), TokenOwned::Float(float) => write!(f, "{float}"),

View File

@ -207,6 +207,24 @@ impl Value {
)), )),
} }
} }
pub fn and(&self, other: &Value) -> Result<Value, ValueError> {
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<Value, ValueError> {
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 { impl Display for Value {
@ -734,15 +752,16 @@ impl Display for Function {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ValueError { pub enum ValueError {
CannotAdd(Value, Value), CannotAdd(Value, Value),
CannotMultiply(Value, Value), CannotAnd(Value, Value),
CannotSubtract(Value, Value),
CannotLessThan(Value, Value),
CannotLessThanOrEqual(Value, Value),
CannotGreaterThan(Value, Value), CannotGreaterThan(Value, Value),
CannotGreaterThanOrEqual(Value, Value), CannotGreaterThanOrEqual(Value, Value),
CannotLessThan(Value, Value),
CannotLessThanOrEqual(Value, Value),
CannotMultiply(Value, Value),
CannotSubtract(Value, Value),
CannotOr(Value, Value),
ExpectedList(Value), ExpectedList(Value),
IndexOutOfBounds { value: Value, index: i64 }, IndexOutOfBounds { value: Value, index: i64 },
PropertyNotFound { value: Value, property: Identifier },
} }
impl Error for ValueError {} impl Error for ValueError {}
@ -751,6 +770,11 @@ impl Display for ValueError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right), 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) => { ValueError::CannotMultiply(left, right) => {
write!(f, "Cannot multiply {} and {}", left, right) write!(f, "Cannot multiply {} and {}", left, right)
} }
@ -763,8 +787,12 @@ impl Display for ValueError {
| ValueError::CannotGreaterThanOrEqual(left, right) => { | ValueError::CannotGreaterThanOrEqual(left, right) => {
write!(f, "Cannot compare {} and {}", left, right) write!(f, "Cannot compare {} and {}", left, right)
} }
ValueError::PropertyNotFound { value, property } => { ValueError::CannotOr(left, right) => {
write!(f, "{} does not have a property named {}", value, property) write!(
f,
"Cannot use logical or operation on {} and {}",
left, right
)
} }
ValueError::IndexOutOfBounds { value, index } => { ValueError::IndexOutOfBounds { value, index } => {
write!(f, "{} does not have an index of {}", value, index) write!(f, "{} does not have an index of {}", value, index)

View File

@ -94,6 +94,7 @@ impl Vm {
let result = match operator.inner { let result = match operator.inner {
BinaryOperator::Add => left_value.add(&right_value), BinaryOperator::Add => left_value.add(&right_value),
BinaryOperator::And => left_value.and(&right_value),
BinaryOperator::Divide => todo!(), BinaryOperator::Divide => todo!(),
BinaryOperator::Greater => left_value.greater_than(&right_value), BinaryOperator::Greater => left_value.greater_than(&right_value),
BinaryOperator::GreaterOrEqual => { BinaryOperator::GreaterOrEqual => {
@ -102,6 +103,7 @@ impl Vm {
BinaryOperator::Less => left_value.less_than(&right_value), BinaryOperator::Less => left_value.less_than(&right_value),
BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value), BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value),
BinaryOperator::Multiply => left_value.multiply(&right_value), BinaryOperator::Multiply => left_value.multiply(&right_value),
BinaryOperator::Or => left_value.or(&right_value),
BinaryOperator::Subtract => left_value.subtract(&right_value), BinaryOperator::Subtract => left_value.subtract(&right_value),
} }
.map_err(|value_error| VmError::ValueError { .map_err(|value_error| VmError::ValueError {