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