use crate::err::{Error, ErrorKind};
use crate::{Analyzer, TypeContext};
use swamp_script_semantic::{BinaryOperator, BinaryOperatorKind, UnaryOperator, UnaryOperatorKind};
use swamp_script_types::prelude::*;
use tracing::debug;
impl Analyzer<'_> {
pub(crate) fn analyze_binary_op(
&mut self,
ast_left: &swamp_script_ast::Expression,
ast_op: &swamp_script_ast::BinaryOperator,
ast_right: &swamp_script_ast::Expression,
) -> Result<(BinaryOperator, Type), Error> {
let anything_context = TypeContext::new_anything_argument();
let left = self.analyze_expression(ast_left, &anything_context)?;
let left_type = left.ty.clone();
let right = self.analyze_expression(ast_right, &anything_context)?;
let right_type = right.ty.clone();
let kind = self.convert_binary_operator_kind(ast_op);
let node = self.to_node(&ast_op.node);
match (&kind, &left_type, &right_type) {
(&BinaryOperatorKind::Add, Type::String, _) => Ok((
BinaryOperator {
left: Box::new(left),
right: Box::new(right),
kind,
node,
},
Type::String,
)),
(
BinaryOperatorKind::Equal
| BinaryOperatorKind::NotEqual
| BinaryOperatorKind::GreaterThan
| BinaryOperatorKind::GreaterEqual
| BinaryOperatorKind::LessThan
| BinaryOperatorKind::LessEqual,
_,
_,
) => {
if !left_type.compatible_with(&right_type) {
debug!(?left_type, ?right_type, "type mismatch in comparison");
return Err(self.create_err(
ErrorKind::IncompatibleTypes {
expected: left_type,
found: right_type,
},
&ast_op.node,
));
}
Ok((
BinaryOperator {
left: Box::new(left),
right: Box::new(right),
kind,
node,
},
Type::Bool,
))
}
_ => {
if !left_type.compatible_with(&right_type) {
debug!(?left_type, ?right_type, "type mismatch in operation");
return Err(self.create_err_resolved(
ErrorKind::IncompatibleTypes {
expected: left_type,
found: right_type,
},
&node,
));
}
Ok((
BinaryOperator {
left: Box::new(left),
right: Box::new(right),
kind,
node,
},
left_type,
))
}
}
}
pub(crate) fn analyze_unary_op(
&mut self,
ast_op: &swamp_script_ast::UnaryOperator,
ast_left: &swamp_script_ast::Expression,
) -> Result<(UnaryOperator, Type), Error> {
let (node, kind, require_type) = match ast_op {
swamp_script_ast::UnaryOperator::Not(node) => {
(node, UnaryOperatorKind::Not, Some(&Type::Bool))
}
swamp_script_ast::UnaryOperator::Negate(node) => {
(node, UnaryOperatorKind::Negate, None)
}
};
let context = TypeContext::new_unsure_argument(require_type);
let left = self.analyze_expression(ast_left, &context)?;
let resolved_type = left.ty.clone();
Ok((
UnaryOperator {
left: Box::new(left),
kind,
node: self.to_node(node),
},
resolved_type,
))
}
const fn convert_binary_operator_kind(
&self,
binary_operator: &swamp_script_ast::BinaryOperator,
) -> BinaryOperatorKind {
match binary_operator.kind {
swamp_script_ast::BinaryOperatorKind::Add => BinaryOperatorKind::Add,
swamp_script_ast::BinaryOperatorKind::Subtract => BinaryOperatorKind::Subtract,
swamp_script_ast::BinaryOperatorKind::Multiply => BinaryOperatorKind::Multiply,
swamp_script_ast::BinaryOperatorKind::Divide => BinaryOperatorKind::Divide,
swamp_script_ast::BinaryOperatorKind::Modulo => BinaryOperatorKind::Modulo,
swamp_script_ast::BinaryOperatorKind::LogicalOr => BinaryOperatorKind::LogicalOr,
swamp_script_ast::BinaryOperatorKind::LogicalAnd => BinaryOperatorKind::LogicalAnd,
swamp_script_ast::BinaryOperatorKind::Equal => BinaryOperatorKind::Equal,
swamp_script_ast::BinaryOperatorKind::NotEqual => BinaryOperatorKind::NotEqual,
swamp_script_ast::BinaryOperatorKind::LessThan => BinaryOperatorKind::LessThan,
swamp_script_ast::BinaryOperatorKind::LessEqual => BinaryOperatorKind::LessEqual,
swamp_script_ast::BinaryOperatorKind::GreaterThan => BinaryOperatorKind::GreaterThan,
swamp_script_ast::BinaryOperatorKind::GreaterEqual => BinaryOperatorKind::GreaterEqual,
swamp_script_ast::BinaryOperatorKind::RangeExclusive => {
BinaryOperatorKind::RangeExclusive
}
}
}
}