Skip to main content

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, Mappings, 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, mappings: Option<Mappings>| {
22        if let Some(right) = binary.right() {
23            if let Some(mappings) = mappings {
24                let range = right.syntax().text_range();
25                let index = ctx.push_mappings(mappings, range.start());
26                let value = compile_expr(ctx, &right, None);
27                ctx.revert_mappings(index, range.end());
28                value
29            } else {
30                compile_expr(ctx, &right, None)
31            }
32        } else {
33            debug!("Unresolved rhs in binary expr");
34            ctx.builtins().unresolved.clone()
35        }
36    };
37
38    let Some(op) = binary.op() else {
39        debug!("Unresolved op in binary expr");
40        return ctx.builtins().unresolved.clone();
41    };
42
43    let (left, right) = match op.kind() {
44        T![+] => {
45            let left = left(ctx);
46            let right = right(ctx, None);
47
48            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
49                debug!("Unresolved binary expr operands");
50                return ctx.builtins().unresolved.clone();
51            }
52
53            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
54                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
55                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Add, left.hir, right.hir));
56                return Value::new(hir, ctx.builtins().types.int);
57            }
58
59            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
60                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
61                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Concat, left.hir, right.hir));
62                return Value::new(hir, ctx.builtins().types.bytes);
63            }
64
65            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
66                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
67                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Add, left.hir, right.hir));
68                return Value::new(hir, ctx.builtins().types.public_key);
69            }
70
71            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
72                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
73                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Add, left.hir, right.hir));
74                return Value::new(hir, ctx.builtins().types.signature);
75            }
76
77            (left, right)
78        }
79        T![-] => {
80            let left = left(ctx);
81            let right = right(ctx, None);
82
83            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
84                debug!("Unresolved binary expr operands");
85                return ctx.builtins().unresolved.clone();
86            }
87
88            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
89                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
90                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Sub, left.hir, right.hir));
91                return Value::new(hir, ctx.builtins().types.int);
92            }
93
94            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
95                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
96                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Subtract, left.hir, right.hir));
97                return Value::new(hir, ctx.builtins().types.public_key);
98            }
99
100            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
101                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
102                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Subtract, left.hir, right.hir));
103                return Value::new(hir, ctx.builtins().types.signature);
104            }
105
106            (left, right)
107        }
108        T![*] => {
109            let left = left(ctx);
110            let right = right(ctx, None);
111
112            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
113                debug!("Unresolved binary expr operands");
114                return ctx.builtins().unresolved.clone();
115            }
116
117            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
118                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
119                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mul, left.hir, right.hir));
120                return Value::new(hir, ctx.builtins().types.int);
121            }
122
123            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
124                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
125                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Multiply, left.hir, right.hir));
126                return Value::new(hir, ctx.builtins().types.public_key);
127            }
128
129            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
130                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
131                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Multiply, left.hir, right.hir));
132                return Value::new(hir, ctx.builtins().types.signature);
133            }
134
135            (left, right)
136        }
137        T![/] => {
138            let left = left(ctx);
139            let right = right(ctx, None);
140
141            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
142                debug!("Unresolved binary expr operands");
143                return ctx.builtins().unresolved.clone();
144            }
145
146            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
147                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
148                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Div, left.hir, right.hir));
149                return Value::new(hir, ctx.builtins().types.int);
150            }
151
152            (left, right)
153        }
154        T![%] => {
155            let left = left(ctx);
156            let right = right(ctx, None);
157
158            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
159                debug!("Unresolved binary expr operands");
160                return ctx.builtins().unresolved.clone();
161            }
162
163            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
164                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
165                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mod, left.hir, right.hir));
166                return Value::new(hir, ctx.builtins().types.int);
167            }
168
169            (left, right)
170        }
171        T![<<] => {
172            let left = left(ctx);
173            let right = right(ctx, None);
174
175            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
176                debug!("Unresolved binary expr operands");
177                return ctx.builtins().unresolved.clone();
178            }
179
180            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
181                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
182                let hir = ctx.alloc_hir(Hir::Binary(
183                    BinaryOp::LeftArithmeticShift,
184                    left.hir,
185                    right.hir,
186                ));
187                return Value::new(hir, ctx.builtins().types.int);
188            }
189
190            (left, right)
191        }
192        T![>>] => {
193            let left = left(ctx);
194            let right = right(ctx, None);
195
196            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
197                debug!("Unresolved binary expr operands");
198                return ctx.builtins().unresolved.clone();
199            }
200
201            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
202                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
203                let hir = ctx.alloc_hir(Hir::Binary(
204                    BinaryOp::RightArithmeticShift,
205                    left.hir,
206                    right.hir,
207                ));
208                return Value::new(hir, ctx.builtins().types.int);
209            }
210
211            (left, right)
212        }
213        T![>>>] => {
214            let left = left(ctx);
215            let right = right(ctx, None);
216
217            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
218                debug!("Unresolved binary expr operands");
219                return ctx.builtins().unresolved.clone();
220            }
221
222            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
223                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
224                let hir = ctx.alloc_hir(Hir::Binary(
225                    BinaryOp::RightLogicalShift,
226                    left.hir,
227                    right.hir,
228                ));
229                return Value::new(hir, ctx.builtins().types.int);
230            }
231
232            (left, right)
233        }
234        T![>] => {
235            let left = left(ctx);
236            let right = right(ctx, None);
237
238            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
239                debug!("Unresolved binary expr operands");
240                return ctx.builtins().unresolved.clone();
241            }
242
243            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
244                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
245                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gt, left.hir, right.hir));
246                return Value::new(hir, ctx.builtins().types.bool);
247            }
248
249            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
250                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
251                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GtBytes, left.hir, right.hir));
252                return Value::new(hir, ctx.builtins().types.bool);
253            }
254
255            (left, right)
256        }
257        T![<] => {
258            let left = left(ctx);
259            let right = right(ctx, None);
260
261            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
262                debug!("Unresolved binary expr operands");
263                return ctx.builtins().unresolved.clone();
264            }
265
266            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
267                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
268                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lt, left.hir, right.hir));
269                return Value::new(hir, ctx.builtins().types.bool);
270            }
271
272            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
273                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
274                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LtBytes, left.hir, right.hir));
275                return Value::new(hir, ctx.builtins().types.bool);
276            }
277
278            (left, right)
279        }
280        T![>=] => {
281            let left = left(ctx);
282            let right = right(ctx, None);
283
284            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
285                debug!("Unresolved binary expr operands");
286                return ctx.builtins().unresolved.clone();
287            }
288
289            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
290                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
291                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gte, left.hir, right.hir));
292                return Value::new(hir, ctx.builtins().types.bool);
293            }
294
295            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
296                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
297                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GteBytes, left.hir, right.hir));
298                return Value::new(hir, ctx.builtins().types.bool);
299            }
300
301            (left, right)
302        }
303        T![<=] => {
304            let left = left(ctx);
305            let right = right(ctx, None);
306
307            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
308                debug!("Unresolved binary expr operands");
309                return ctx.builtins().unresolved.clone();
310            }
311
312            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
313                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
314                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lte, left.hir, right.hir));
315                return Value::new(hir, ctx.builtins().types.bool);
316            }
317
318            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
319                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
320                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LteBytes, left.hir, right.hir));
321                return Value::new(hir, ctx.builtins().types.bool);
322            }
323
324            (left, right)
325        }
326        T![&&] => {
327            let left = left(ctx);
328            let right = right(ctx, Some(left.then_map.clone()));
329
330            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
331                debug!("Unresolved binary expr operands");
332                return ctx.builtins().unresolved.clone();
333            }
334
335            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
336                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
337                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::And, left.hir, right.hir));
338                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
339                    merge_mappings(&left.then_map, &right.then_map),
340                    HashMap::new(),
341                );
342            }
343
344            (left, right)
345        }
346        T![||] => {
347            let left = left(ctx);
348            let right = right(ctx, Some(left.else_map.clone()));
349
350            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
351                debug!("Unresolved binary expr operands");
352                return ctx.builtins().unresolved.clone();
353            }
354
355            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
356                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
357                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Or, left.hir, right.hir));
358                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
359                    HashMap::new(),
360                    merge_mappings(&left.else_map, &right.else_map),
361                );
362            }
363
364            (left, right)
365        }
366        T![&] => {
367            let left = left(ctx);
368            let right = right(ctx, None);
369
370            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
371                debug!("Unresolved binary expr operands");
372                return ctx.builtins().unresolved.clone();
373            }
374
375            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
376                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
377                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::All, left.hir, right.hir));
378                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
379                    merge_mappings(&left.then_map, &right.then_map),
380                    HashMap::new(),
381                );
382            }
383
384            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
385                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
386                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseAnd, left.hir, right.hir));
387                return Value::new(hir, ctx.builtins().types.int);
388            }
389
390            (left, right)
391        }
392        T![|] => {
393            let left = left(ctx);
394            let right = right(ctx, None);
395
396            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
397                debug!("Unresolved binary expr operands");
398                return ctx.builtins().unresolved.clone();
399            }
400
401            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
402                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
403                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Any, left.hir, right.hir));
404                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
405                    HashMap::new(),
406                    merge_mappings(&left.then_map, &right.then_map),
407                );
408            }
409
410            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
411                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
412                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseOr, left.hir, right.hir));
413                return Value::new(hir, ctx.builtins().types.int);
414            }
415
416            (left, right)
417        }
418        T![^] => {
419            let left = left(ctx);
420            let right = right(ctx, None);
421
422            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
423                debug!("Unresolved binary expr operands");
424                return ctx.builtins().unresolved.clone();
425            }
426
427            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
428                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
429                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseXor, left.hir, right.hir));
430                return Value::new(hir, ctx.builtins().types.int);
431            }
432
433            (left, right)
434        }
435        T![==] | T![!=] => {
436            // TODO: Guard?
437
438            let left = left(ctx);
439            let right = right(ctx, None);
440
441            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
442                debug!("Unresolved binary expr operands");
443                return ctx.builtins().unresolved.clone();
444            }
445
446            let types = [
447                ctx.builtins().types.bool,
448                ctx.builtins().types.int,
449                ctx.builtins().types.bytes,
450                ctx.builtins().types.public_key,
451                ctx.builtins().types.signature,
452            ];
453
454            if types
455                .iter()
456                .any(|ty| ctx.is_assignable(left.ty, *ty) && ctx.is_assignable(right.ty, *ty))
457            {
458                let hir = if op.kind() == T![==] {
459                    ctx.alloc_hir(Hir::Binary(BinaryOp::Eq, left.hir, right.hir))
460                } else {
461                    ctx.alloc_hir(Hir::Binary(BinaryOp::Ne, left.hir, right.hir))
462                };
463
464                let mut value = Value::new(hir, ctx.builtins().types.bool);
465
466                if op.kind() == T![!=] {
467                    value = value.flip_mappings();
468                }
469
470                return value;
471            }
472
473            (left, right)
474        }
475        _ => {
476            let left = left(ctx);
477            let right = right(ctx, None);
478
479            if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
480                debug!("Unresolved binary expr operands");
481                return ctx.builtins().unresolved.clone();
482            }
483
484            (left, right)
485        }
486    };
487
488    let left_name = ctx.type_name(left.ty);
489    let right_name = ctx.type_name(right.ty);
490
491    debug!("Unresolved binary expr");
492
493    ctx.diagnostic(
494        binary.syntax(),
495        DiagnosticKind::IncompatibleBinaryOp(op.text().to_string(), left_name, right_name),
496    );
497    ctx.builtins().unresolved.clone()
498}