swamp_script_analyzer/
operator.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/script
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5
6use crate::err::{Error, ErrorKind};
7use crate::{Analyzer, TypeContext};
8use swamp_script_semantic::{BinaryOperator, BinaryOperatorKind, UnaryOperator, UnaryOperatorKind};
9use swamp_script_types::prelude::*;
10use tracing::debug;
11
12impl Analyzer<'_> {
13    pub(crate) fn analyze_binary_op(
14        &mut self,
15        ast_left: &swamp_script_ast::Expression,
16        ast_op: &swamp_script_ast::BinaryOperator,
17        ast_right: &swamp_script_ast::Expression,
18    ) -> Result<(BinaryOperator, Type), Error> {
19        let anything_context = TypeContext::new_anything_argument();
20        let left = self.analyze_expression(ast_left, &anything_context)?;
21        let left_type = left.ty.clone();
22
23        let right = self.analyze_expression(ast_right, &anything_context)?;
24        let right_type = right.ty.clone();
25
26        let kind = self.convert_binary_operator_kind(ast_op);
27        let node = self.to_node(&ast_op.node);
28
29        match (&kind, &left_type, &right_type) {
30            // String concatenation - allow any type on the right
31            (&BinaryOperatorKind::Add, Type::String, _) => Ok((
32                BinaryOperator {
33                    left: Box::new(left),
34                    right: Box::new(right),
35                    kind,
36                    node,
37                },
38                Type::String,
39            )),
40
41            // Comparison operators
42            (
43                BinaryOperatorKind::Equal
44                | BinaryOperatorKind::NotEqual
45                | BinaryOperatorKind::GreaterThan
46                | BinaryOperatorKind::GreaterEqual
47                | BinaryOperatorKind::LessThan
48                | BinaryOperatorKind::LessEqual,
49                _,
50                _,
51            ) => {
52                if !left_type.compatible_with(&right_type) {
53                    debug!(?left_type, ?right_type, "type mismatch in comparison");
54                    return Err(self.create_err(
55                        ErrorKind::IncompatibleTypes(left_type, right_type),
56                        &ast_op.node,
57                    ));
58                }
59                Ok((
60                    BinaryOperator {
61                        left: Box::new(left),
62                        right: Box::new(right),
63                        kind,
64                        node,
65                    },
66                    Type::Bool,
67                ))
68            }
69
70            // All other operators require exact type matches
71            _ => {
72                if !left_type.compatible_with(&right_type) {
73                    debug!(?left_type, ?right_type, "type mismatch in operation");
74                    return Err(self.create_err_resolved(
75                        ErrorKind::IncompatibleTypes(left_type, right_type),
76                        &node,
77                    ));
78                }
79                Ok((
80                    BinaryOperator {
81                        left: Box::new(left),
82                        right: Box::new(right),
83                        kind,
84                        node,
85                    },
86                    left_type,
87                ))
88            }
89        }
90    }
91
92    pub(crate) fn analyze_unary_op(
93        &mut self,
94        ast_op: &swamp_script_ast::UnaryOperator,
95        ast_left: &swamp_script_ast::Expression,
96    ) -> Result<(UnaryOperator, Type), Error> {
97        let (node, kind, require_type) = match ast_op {
98            swamp_script_ast::UnaryOperator::Not(node) => {
99                (node, UnaryOperatorKind::Not, Some(&Type::Bool))
100            }
101            swamp_script_ast::UnaryOperator::Negate(node) => {
102                (node, UnaryOperatorKind::Negate, None)
103            }
104        };
105        let context = TypeContext::new_unsure_argument(require_type);
106        let left = self.analyze_expression(ast_left, &context)?;
107        let resolved_type = left.ty.clone();
108        Ok((
109            UnaryOperator {
110                left: Box::new(left),
111                kind,
112                node: self.to_node(node),
113            },
114            resolved_type,
115        ))
116    }
117
118    const fn convert_binary_operator_kind(
119        &self,
120        binary_operator: &swamp_script_ast::BinaryOperator,
121    ) -> BinaryOperatorKind {
122        match binary_operator.kind {
123            swamp_script_ast::BinaryOperatorKind::Add => BinaryOperatorKind::Add,
124            swamp_script_ast::BinaryOperatorKind::Subtract => BinaryOperatorKind::Subtract,
125            swamp_script_ast::BinaryOperatorKind::Multiply => BinaryOperatorKind::Multiply,
126            swamp_script_ast::BinaryOperatorKind::Divide => BinaryOperatorKind::Divide,
127            swamp_script_ast::BinaryOperatorKind::Modulo => BinaryOperatorKind::Modulo,
128            swamp_script_ast::BinaryOperatorKind::LogicalOr => BinaryOperatorKind::LogicalOr,
129            swamp_script_ast::BinaryOperatorKind::LogicalAnd => BinaryOperatorKind::LogicalAnd,
130            swamp_script_ast::BinaryOperatorKind::Equal => BinaryOperatorKind::Equal,
131            swamp_script_ast::BinaryOperatorKind::NotEqual => BinaryOperatorKind::NotEqual,
132            swamp_script_ast::BinaryOperatorKind::LessThan => BinaryOperatorKind::LessThan,
133            swamp_script_ast::BinaryOperatorKind::LessEqual => BinaryOperatorKind::LessEqual,
134            swamp_script_ast::BinaryOperatorKind::GreaterThan => BinaryOperatorKind::GreaterThan,
135            swamp_script_ast::BinaryOperatorKind::GreaterEqual => BinaryOperatorKind::GreaterEqual,
136            swamp_script_ast::BinaryOperatorKind::RangeExclusive => {
137                BinaryOperatorKind::RangeExclusive
138            }
139        }
140    }
141}