rue_compiler/compile/expr/
binary.rs

1use std::collections::HashMap;
2
3use log::debug;
4use rue_ast::{AstBinaryExpr, AstNode};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{BinaryOp, Hir, Value, merge_mappings};
7use rue_parser::T;
8
9use crate::{Compiler, compile_expr};
10
11pub fn compile_binary_expr(ctx: &mut Compiler, binary: &AstBinaryExpr) -> Value {
12    let left = |ctx: &mut Compiler| {
13        if let Some(left) = binary.left() {
14            compile_expr(ctx, &left, None)
15        } else {
16            debug!("Unresolved lhs in binary expr");
17            ctx.builtins().unresolved.clone()
18        }
19    };
20
21    let right = |ctx: &mut Compiler| {
22        if let Some(right) = binary.right() {
23            compile_expr(ctx, &right, None)
24        } else {
25            debug!("Unresolved rhs in binary expr");
26            ctx.builtins().unresolved.clone()
27        }
28    };
29
30    let Some(op) = binary.op() else {
31        debug!("Unresolved op in binary expr");
32        return ctx.builtins().unresolved.clone();
33    };
34
35    let (left, right) = match op.kind() {
36        T![+] => {
37            let left = left(ctx);
38            let right = right(ctx);
39
40            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
41                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
42                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Add, left.hir, right.hir));
43                return Value::new(hir, ctx.builtins().types.int);
44            }
45
46            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
47                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
48                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Concat, left.hir, right.hir));
49                return Value::new(hir, ctx.builtins().types.bytes);
50            }
51
52            (left, right)
53        }
54        T![-] => {
55            let left = left(ctx);
56            let right = right(ctx);
57
58            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
59                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
60                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Sub, left.hir, right.hir));
61                return Value::new(hir, ctx.builtins().types.int);
62            }
63
64            (left, right)
65        }
66        T![*] => {
67            let left = left(ctx);
68            let right = right(ctx);
69
70            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
71                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
72                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mul, left.hir, right.hir));
73                return Value::new(hir, ctx.builtins().types.int);
74            }
75
76            (left, right)
77        }
78        T![/] => {
79            let left = left(ctx);
80            let right = right(ctx);
81
82            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
83                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
84                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Div, left.hir, right.hir));
85                return Value::new(hir, ctx.builtins().types.int);
86            }
87
88            (left, right)
89        }
90        T![%] => {
91            let left = left(ctx);
92            let right = right(ctx);
93
94            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
95                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
96                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mod, left.hir, right.hir));
97                return Value::new(hir, ctx.builtins().types.int);
98            }
99
100            (left, right)
101        }
102        T![<<] => {
103            let left = left(ctx);
104            let right = right(ctx);
105
106            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
107                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
108                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LeftShift, left.hir, right.hir));
109                return Value::new(hir, ctx.builtins().types.int);
110            }
111
112            (left, right)
113        }
114        T![>>] => {
115            let left = left(ctx);
116            let right = right(ctx);
117
118            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
119                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
120                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::RightShift, left.hir, right.hir));
121                return Value::new(hir, ctx.builtins().types.int);
122            }
123
124            (left, right)
125        }
126        T![>] => {
127            let left = left(ctx);
128            let right = right(ctx);
129
130            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
131                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
132                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gt, left.hir, right.hir));
133                return Value::new(hir, ctx.builtins().types.bool);
134            }
135
136            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
137                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
138                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GtBytes, left.hir, right.hir));
139                return Value::new(hir, ctx.builtins().types.bool);
140            }
141
142            (left, right)
143        }
144        T![<] => {
145            let left = left(ctx);
146            let right = right(ctx);
147
148            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
149                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
150                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lt, left.hir, right.hir));
151                return Value::new(hir, ctx.builtins().types.bool);
152            }
153
154            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
155                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
156                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LtBytes, left.hir, right.hir));
157                return Value::new(hir, ctx.builtins().types.bool);
158            }
159
160            (left, right)
161        }
162        T![>=] => {
163            let left = left(ctx);
164            let right = right(ctx);
165
166            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
167                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
168                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gte, left.hir, right.hir));
169                return Value::new(hir, ctx.builtins().types.bool);
170            }
171
172            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
173                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
174                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GteBytes, left.hir, right.hir));
175                return Value::new(hir, ctx.builtins().types.bool);
176            }
177
178            (left, right)
179        }
180        T![<=] => {
181            let left = left(ctx);
182            let right = right(ctx);
183
184            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
185                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
186                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lte, left.hir, right.hir));
187                return Value::new(hir, ctx.builtins().types.bool);
188            }
189
190            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
191                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
192                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LteBytes, left.hir, right.hir));
193                return Value::new(hir, ctx.builtins().types.bool);
194            }
195
196            (left, right)
197        }
198        T![&&] => {
199            let left = left(ctx);
200
201            let index = ctx.push_mappings(left.then_map.clone());
202            let right = right(ctx);
203            ctx.revert_mappings(index);
204
205            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
206                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
207                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::And, left.hir, right.hir));
208                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
209                    merge_mappings(&left.then_map, &right.then_map),
210                    HashMap::new(),
211                );
212            }
213
214            (left, right)
215        }
216        T![||] => {
217            let left = left(ctx);
218
219            let index = ctx.push_mappings(left.else_map.clone());
220            let right = right(ctx);
221            ctx.revert_mappings(index);
222
223            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
224                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
225                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Or, left.hir, right.hir));
226                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
227                    HashMap::new(),
228                    merge_mappings(&left.else_map, &right.else_map),
229                );
230            }
231
232            (left, right)
233        }
234        T![&] => {
235            let left = left(ctx);
236            let right = right(ctx);
237
238            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
239                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
240                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::All, left.hir, right.hir));
241                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
242                    merge_mappings(&left.then_map, &right.then_map),
243                    HashMap::new(),
244                );
245            }
246
247            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
248                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
249                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseAnd, left.hir, right.hir));
250                return Value::new(hir, ctx.builtins().types.int);
251            }
252
253            (left, right)
254        }
255        T![|] => {
256            let left = left(ctx);
257            let right = right(ctx);
258
259            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
260                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
261                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Any, left.hir, right.hir));
262                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
263                    HashMap::new(),
264                    merge_mappings(&left.then_map, &right.then_map),
265                );
266            }
267
268            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
269                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
270                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseOr, left.hir, right.hir));
271                return Value::new(hir, ctx.builtins().types.int);
272            }
273
274            (left, right)
275        }
276        T![^] => {
277            let left = left(ctx);
278            let right = right(ctx);
279
280            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
281                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
282                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseXor, left.hir, right.hir));
283                return Value::new(hir, ctx.builtins().types.int);
284            }
285
286            (left, right)
287        }
288        T![==] | T![!=] => {
289            // TODO: Guard?
290
291            let left = left(ctx);
292            let right = right(ctx);
293
294            if ctx.is_castable(left.ty, ctx.builtins().types.bytes)
295                && ctx.is_assignable(right.ty, left.ty)
296            {
297                let hir = if op.kind() == T![==] {
298                    ctx.alloc_hir(Hir::Binary(BinaryOp::Eq, left.hir, right.hir))
299                } else {
300                    ctx.alloc_hir(Hir::Binary(BinaryOp::Ne, left.hir, right.hir))
301                };
302
303                let mut value = Value::new(hir, ctx.builtins().types.bool);
304
305                if op.kind() == T![!=] {
306                    value = value.flip_mappings();
307                }
308
309                return value;
310            }
311
312            (left, right)
313        }
314        _ => {
315            let left = left(ctx);
316            let right = right(ctx);
317
318            (left, right)
319        }
320    };
321
322    let left_name = ctx.type_name(left.ty);
323    let right_name = ctx.type_name(right.ty);
324
325    debug!("Unresolved binary expr");
326
327    ctx.diagnostic(
328        binary.syntax(),
329        DiagnosticKind::IncompatibleBinaryOp(op.text().to_string(), left_name, right_name),
330    );
331    ctx.builtins().unresolved.clone()
332}