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)),
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, "-"),
}
}

View File

@ -78,8 +78,7 @@ impl<'a> Analyzer<'a> {
fn analyze_node(&self, node: &Node<Statement>) -> 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<Statement>,
position: Span,
},
ExpectedSameType {
left: Node<Statement>,
right: Node<Statement>,
position: Span,
},
ExpectedString {
actual: Node<Statement>,
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 {

View File

@ -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()
})

View File

@ -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}"),

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 {
@ -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)

View File

@ -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 {