Skip to main content

rl_lang/checker/operators/
binary.rs

1//! Binary operator type checking.
2//!
3//! # Rules
4//!
5//! | Operator          | Left / Right                    | Result  |
6//! |-------------------|---------------------------------|---------|
7//! | `+` `-` `*` `/`  | int + int                       | int     |
8//! | `+` `-` `*` `/`  | float + float                   | float   |
9//! | `+` `-` `*` `/`  | byte + byte                     | byte    |
10//! | `+` `-` `*` `/`  | byte + int (or int + byte)      | int     |
11//! | `<` `>` `<=` `>=`| int/byte pairs                  | bool    |
12//! | `<` `>` `<=` `>=`| float + float                   | bool    |
13//! | `==` `!=`         | matching primitive types        | bool    |
14//!
15//! Any side being `Unknown` short-circuits to `Unknown` to suppress cascading errors.
16
17use crate::{
18    ast::statements::TypeAnnotation,
19    checker::{
20        operators::op_str,
21        structs::{CheckType, TypeChecker},
22    },
23    lexer::tokentypes::TokenType,
24    utils::span::Span,
25};
26
27impl TypeChecker {
28    pub fn check_binary_operator(
29        &mut self,
30        left: CheckType,
31        right: CheckType,
32        op: &TokenType,
33        span: Span,
34    ) -> CheckType {
35        // if any of sides is unknown then it is unknown
36        if left.is_unknown() || right.is_unknown() {
37            return CheckType::Unknown;
38        }
39
40        match op {
41            // arithmetic check if both same type or not
42            TokenType::Plus | TokenType::Minus | TokenType::Star | TokenType::Slash => {
43                match (&left, &right) {
44                    (
45                        CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
46                        CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
47                    ) => CheckType::Known(TypeAnnotation::Int),
48                    (
49                        CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
50                        CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
51                    ) => CheckType::Known(TypeAnnotation::Float),
52                    (
53                        CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
54                        CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
55                    ) => CheckType::Known(TypeAnnotation::Byte),
56
57                    _ => {
58                        self.error(
59                            format!(
60                                "type mismatch on {}: got {} and {}",
61                                op_str(op),
62                                left.info(),
63                                right.info()
64                            ),
65                            span,
66                        );
67                        CheckType::Unknown
68                    }
69                }
70            }
71
72            // comparisons should be same type
73            TokenType::Less
74            | TokenType::Greater
75            | TokenType::LessEqual
76            | TokenType::GreaterEqual => match (&left, &right) {
77                (
78                    CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
79                    CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
80                )
81                | (
82                    CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
83                    CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
84                ) => CheckType::Known(TypeAnnotation::Bool),
85                (
86                    CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
87                    CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
88                ) => CheckType::Known(TypeAnnotation::Bool),
89
90                _ => {
91                    self.error(
92                        format!(
93                            "type mismatch on {}: got {} and {}",
94                            op_str(op),
95                            left.info(),
96                            right.info()
97                        ),
98                        span,
99                    );
100                    CheckType::Unknown
101                }
102            },
103
104            // equality should be between same types
105            TokenType::Compare | TokenType::BangEqual => {
106                let ok = matches!(
107                    (&left, &right),
108                    (
109                        CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
110                        CheckType::Known(TypeAnnotation::Int | TypeAnnotation::CInt),
111                    ) | (
112                        CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
113                        CheckType::Known(TypeAnnotation::Byte | TypeAnnotation::CByte),
114                    ) | (
115                        CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
116                        CheckType::Known(TypeAnnotation::Float | TypeAnnotation::CFloat),
117                    ) | (
118                        CheckType::Known(TypeAnnotation::String | TypeAnnotation::CString),
119                        CheckType::Known(TypeAnnotation::String | TypeAnnotation::CString),
120                    ) | (
121                        CheckType::Known(TypeAnnotation::Char | TypeAnnotation::CChar),
122                        CheckType::Known(TypeAnnotation::Char | TypeAnnotation::CChar),
123                    ) | (
124                        CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool),
125                        CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool),
126                    ) | (
127                        CheckType::Known(TypeAnnotation::Enum(_) | TypeAnnotation::CEnum(_)),
128                        CheckType::Known(TypeAnnotation::Enum(_) | TypeAnnotation::CEnum(_)),
129                    ) | (
130                        CheckType::Known(TypeAnnotation::Record(_) | TypeAnnotation::CRecord(_)),
131                        CheckType::Known(TypeAnnotation::Record(_) | TypeAnnotation::CRecord(_)),
132                    )
133                );
134                if !ok {
135                    self.error(
136                        format!(
137                            "type mismatch on {}: got {} and {}",
138                            op_str(op),
139                            left.info(),
140                            right.info()
141                        ),
142                        span,
143                    );
144                }
145                CheckType::Known(TypeAnnotation::Bool)
146            }
147
148            TokenType::And | TokenType::Or => {
149                if !matches!(
150                    left,
151                    CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool)
152                ) {
153                    self.error(
154                        format!("expected bool on the left side of {}", op_str(op)),
155                        span,
156                    );
157                }
158                if !matches!(
159                    right,
160                    CheckType::Known(TypeAnnotation::Bool | TypeAnnotation::CBool)
161                ) {
162                    self.error(
163                        format!("expected bool on the right side of {}", op_str(op)),
164                        span,
165                    );
166                }
167
168                CheckType::Known(TypeAnnotation::Bool)
169            }
170
171            // unknown operator
172            _ => {
173                self.error(format!("unknown binary operator {:?}", op), span);
174                CheckType::Unknown
175            }
176        }
177    }
178}