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 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}