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            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
53                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
54                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Add, left.hir, right.hir));
55                return Value::new(hir, ctx.builtins().types.public_key);
56            }
57
58            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
59                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
60                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Add, left.hir, right.hir));
61                return Value::new(hir, ctx.builtins().types.signature);
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::Sub, left.hir, right.hir));
73                return Value::new(hir, ctx.builtins().types.int);
74            }
75
76            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
77                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
78                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Subtract, left.hir, right.hir));
79                return Value::new(hir, ctx.builtins().types.public_key);
80            }
81
82            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
83                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
84                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Subtract, left.hir, right.hir));
85                return Value::new(hir, ctx.builtins().types.signature);
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::Mul, left.hir, right.hir));
97                return Value::new(hir, ctx.builtins().types.int);
98            }
99
100            if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
101                ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
102                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Multiply, left.hir, right.hir));
103                return Value::new(hir, ctx.builtins().types.public_key);
104            }
105
106            if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
107                ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
108                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Multiply, left.hir, right.hir));
109                return Value::new(hir, ctx.builtins().types.signature);
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::Div, 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::Mod, left.hir, right.hir));
133                return Value::new(hir, ctx.builtins().types.int);
134            }
135
136            (left, right)
137        }
138        T![<<] => {
139            let left = left(ctx);
140            let right = right(ctx);
141
142            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
143                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
144                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LeftShift, left.hir, right.hir));
145                return Value::new(hir, ctx.builtins().types.int);
146            }
147
148            (left, right)
149        }
150        T![>>] => {
151            let left = left(ctx);
152            let right = right(ctx);
153
154            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
155                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
156                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::RightShift, left.hir, right.hir));
157                return Value::new(hir, ctx.builtins().types.int);
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::Gt, 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::GtBytes, 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::Lt, 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::LtBytes, 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            let right = right(ctx);
201
202            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
203                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
204                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gte, left.hir, right.hir));
205                return Value::new(hir, ctx.builtins().types.bool);
206            }
207
208            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
209                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
210                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GteBytes, left.hir, right.hir));
211                return Value::new(hir, ctx.builtins().types.bool);
212            }
213
214            (left, right)
215        }
216        T![<=] => {
217            let left = left(ctx);
218            let right = right(ctx);
219
220            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
221                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
222                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lte, left.hir, right.hir));
223                return Value::new(hir, ctx.builtins().types.bool);
224            }
225
226            if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
227                ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
228                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LteBytes, left.hir, right.hir));
229                return Value::new(hir, ctx.builtins().types.bool);
230            }
231
232            (left, right)
233        }
234        T![&&] => {
235            let left = left(ctx);
236
237            let index = ctx.push_mappings(left.then_map.clone());
238            let right = right(ctx);
239            ctx.revert_mappings(index);
240
241            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
242                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
243                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::And, left.hir, right.hir));
244                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
245                    merge_mappings(&left.then_map, &right.then_map),
246                    HashMap::new(),
247                );
248            }
249
250            (left, right)
251        }
252        T![||] => {
253            let left = left(ctx);
254
255            let index = ctx.push_mappings(left.else_map.clone());
256            let right = right(ctx);
257            ctx.revert_mappings(index);
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::Or, left.hir, right.hir));
262                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
263                    HashMap::new(),
264                    merge_mappings(&left.else_map, &right.else_map),
265                );
266            }
267
268            (left, right)
269        }
270        T![&] => {
271            let left = left(ctx);
272            let right = right(ctx);
273
274            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
275                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
276                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::All, left.hir, right.hir));
277                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
278                    merge_mappings(&left.then_map, &right.then_map),
279                    HashMap::new(),
280                );
281            }
282
283            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
284                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
285                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseAnd, left.hir, right.hir));
286                return Value::new(hir, ctx.builtins().types.int);
287            }
288
289            (left, right)
290        }
291        T![|] => {
292            let left = left(ctx);
293            let right = right(ctx);
294
295            if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
296                ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
297                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Any, left.hir, right.hir));
298                return Value::new(hir, ctx.builtins().types.bool).with_mappings(
299                    HashMap::new(),
300                    merge_mappings(&left.then_map, &right.then_map),
301                );
302            }
303
304            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
305                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
306                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseOr, left.hir, right.hir));
307                return Value::new(hir, ctx.builtins().types.int);
308            }
309
310            (left, right)
311        }
312        T![^] => {
313            let left = left(ctx);
314            let right = right(ctx);
315
316            if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
317                ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
318                let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseXor, left.hir, right.hir));
319                return Value::new(hir, ctx.builtins().types.int);
320            }
321
322            (left, right)
323        }
324        T![==] | T![!=] => {
325            // TODO: Guard?
326
327            let left = left(ctx);
328            let right = right(ctx);
329
330            let types = [
331                ctx.builtins().types.bool,
332                ctx.builtins().types.int,
333                ctx.builtins().types.bytes,
334                ctx.builtins().types.public_key,
335                ctx.builtins().types.signature,
336            ];
337
338            if types
339                .iter()
340                .any(|ty| ctx.is_assignable(left.ty, *ty) && ctx.is_assignable(right.ty, *ty))
341            {
342                let hir = if op.kind() == T![==] {
343                    ctx.alloc_hir(Hir::Binary(BinaryOp::Eq, left.hir, right.hir))
344                } else {
345                    ctx.alloc_hir(Hir::Binary(BinaryOp::Ne, left.hir, right.hir))
346                };
347
348                let mut value = Value::new(hir, ctx.builtins().types.bool);
349
350                if op.kind() == T![!=] {
351                    value = value.flip_mappings();
352                }
353
354                return value;
355            }
356
357            (left, right)
358        }
359        _ => {
360            let left = left(ctx);
361            let right = right(ctx);
362
363            (left, right)
364        }
365    };
366
367    let left_name = ctx.type_name(left.ty);
368    let right_name = ctx.type_name(right.ty);
369
370    debug!("Unresolved binary expr");
371
372    ctx.diagnostic(
373        binary.syntax(),
374        DiagnosticKind::IncompatibleBinaryOp(op.text().to_string(), left_name, right_name),
375    );
376    ctx.builtins().unresolved.clone()
377}