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