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 {
56                            expected: left_type,
57                            found: right_type,
58                        },
59                        &ast_op.node,
60                    ));
61                }
62                Ok((
63                    BinaryOperator {
64                        left: Box::new(left),
65                        right: Box::new(right),
66                        kind,
67                        node,
68                    },
69                    Type::Bool,
70                ))
71            }
72
73            // All other operators require exact type matches
74            _ => {
75                if !left_type.compatible_with(&right_type) {
76                    debug!(?left_type, ?right_type, "type mismatch in operation");
77                    return Err(self.create_err_resolved(
78                        ErrorKind::IncompatibleTypes {
79                            expected: left_type,
80                            found: right_type,
81                        },
82                        &node,
83                    ));
84                }
85                Ok((
86                    BinaryOperator {
87                        left: Box::new(left),
88                        right: Box::new(right),
89                        kind,
90                        node,
91                    },
92                    left_type,
93                ))
94            }
95        }
96    }
97
98    pub(crate) fn analyze_unary_op(
99        &mut self,
100        ast_op: &swamp_script_ast::UnaryOperator,
101        ast_left: &swamp_script_ast::Expression,
102    ) -> Result<(UnaryOperator, Type), Error> {
103        let (node, kind, require_type) = match ast_op {
104            swamp_script_ast::UnaryOperator::Not(node) => {
105                (node, UnaryOperatorKind::Not, Some(&Type::Bool))
106            }
107            swamp_script_ast::UnaryOperator::Negate(node) => {
108                (node, UnaryOperatorKind::Negate, None)
109            }
110        };
111        let context = TypeContext::new_unsure_argument(require_type);
112        let left = self.analyze_expression(ast_left, &context)?;
113        let resolved_type = left.ty.clone();
114        Ok((
115            UnaryOperator {
116                left: Box::new(left),
117                kind,
118                node: self.to_node(node),
119            },
120            resolved_type,
121        ))
122    }
123
124    const fn convert_binary_operator_kind(
125        &self,
126        binary_operator: &swamp_script_ast::BinaryOperator,
127    ) -> BinaryOperatorKind {
128        match binary_operator.kind {
129            swamp_script_ast::BinaryOperatorKind::Add => BinaryOperatorKind::Add,
130            swamp_script_ast::BinaryOperatorKind::Subtract => BinaryOperatorKind::Subtract,
131            swamp_script_ast::BinaryOperatorKind::Multiply => BinaryOperatorKind::Multiply,
132            swamp_script_ast::BinaryOperatorKind::Divide => BinaryOperatorKind::Divide,
133            swamp_script_ast::BinaryOperatorKind::Modulo => BinaryOperatorKind::Modulo,
134            swamp_script_ast::BinaryOperatorKind::LogicalOr => BinaryOperatorKind::LogicalOr,
135            swamp_script_ast::BinaryOperatorKind::LogicalAnd => BinaryOperatorKind::LogicalAnd,
136            swamp_script_ast::BinaryOperatorKind::Equal => BinaryOperatorKind::Equal,
137            swamp_script_ast::BinaryOperatorKind::NotEqual => BinaryOperatorKind::NotEqual,
138            swamp_script_ast::BinaryOperatorKind::LessThan => BinaryOperatorKind::LessThan,
139            swamp_script_ast::BinaryOperatorKind::LessEqual => BinaryOperatorKind::LessEqual,
140            swamp_script_ast::BinaryOperatorKind::GreaterThan => BinaryOperatorKind::GreaterThan,
141            swamp_script_ast::BinaryOperatorKind::GreaterEqual => BinaryOperatorKind::GreaterEqual,
142            swamp_script_ast::BinaryOperatorKind::RangeExclusive => {
143                BinaryOperatorKind::RangeExclusive
144            }
145        }
146    }
147}