Add && and || operators
This commit is contained in:
parent
8c8fde94ce
commit
d0dba35285
@ -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, "-"),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -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}"),
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user