use std::collections::HashMap;
use log::debug;
use rue_ast::{AstBinaryExpr, AstNode};
use rue_diagnostic::DiagnosticKind;
use rue_hir::{BinaryOp, Hir, Mappings, Value, merge_mappings};
use rue_parser::T;
use crate::{Compiler, compile_expr};
pub fn compile_binary_expr(ctx: &mut Compiler, binary: &AstBinaryExpr) -> Value {
let left = |ctx: &mut Compiler| {
if let Some(left) = binary.left() {
compile_expr(ctx, &left, None)
} else {
debug!("Unresolved lhs in binary expr");
ctx.builtins().unresolved.clone()
}
};
let right = |ctx: &mut Compiler, mappings: Option<Mappings>| {
if let Some(right) = binary.right() {
if let Some(mappings) = mappings {
let range = right.syntax().text_range();
let index = ctx.push_mappings(mappings, range.start());
let value = compile_expr(ctx, &right, None);
ctx.revert_mappings(index, range.end());
value
} else {
compile_expr(ctx, &right, None)
}
} else {
debug!("Unresolved rhs in binary expr");
ctx.builtins().unresolved.clone()
}
};
let Some(op) = binary.op() else {
debug!("Unresolved op in binary expr");
return ctx.builtins().unresolved.clone();
};
let (left, right) = match op.kind() {
T![+] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Add, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Concat, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bytes);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Add, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.public_key);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Add, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.signature);
}
(left, right)
}
T![-] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Sub, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Subtract, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.public_key);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Subtract, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.signature);
}
(left, right)
}
T![*] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mul, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.public_key) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.public_key);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G1Multiply, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.public_key);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.signature) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.signature);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::G2Multiply, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.signature);
}
(left, right)
}
T![/] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Div, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![%] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Mod, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![<<] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(
BinaryOp::LeftArithmeticShift,
left.hir,
right.hir,
));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![>>] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(
BinaryOp::RightArithmeticShift,
left.hir,
right.hir,
));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![>>>] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(
BinaryOp::RightLogicalShift,
left.hir,
right.hir,
));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![>] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gt, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GtBytes, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
(left, right)
}
T![<] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lt, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LtBytes, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
(left, right)
}
T![>=] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Gte, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::GteBytes, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
(left, right)
}
T![<=] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Lte, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bytes) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bytes);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::LteBytes, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool);
}
(left, right)
}
T![&&] => {
let left = left(ctx);
let right = right(ctx, Some(left.then_map.clone()));
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::And, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool).with_mappings(
merge_mappings(&left.then_map, &right.then_map),
HashMap::new(),
);
}
(left, right)
}
T![||] => {
let left = left(ctx);
let right = right(ctx, Some(left.else_map.clone()));
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Or, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool).with_mappings(
HashMap::new(),
merge_mappings(&left.else_map, &right.else_map),
);
}
(left, right)
}
T![&] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::All, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool).with_mappings(
merge_mappings(&left.then_map, &right.then_map),
HashMap::new(),
);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseAnd, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![|] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.bool) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.bool);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::Any, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.bool).with_mappings(
HashMap::new(),
merge_mappings(&left.then_map, &right.then_map),
);
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseOr, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![^] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
if ctx.is_assignable(left.ty, ctx.builtins().types.int) {
ctx.assign_type(&op, right.ty, ctx.builtins().types.int);
let hir = ctx.alloc_hir(Hir::Binary(BinaryOp::BitwiseXor, left.hir, right.hir));
return Value::new(hir, ctx.builtins().types.int);
}
(left, right)
}
T![==] | T![!=] => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
let types = [
ctx.builtins().types.bool,
ctx.builtins().types.int,
ctx.builtins().types.bytes,
ctx.builtins().types.public_key,
ctx.builtins().types.signature,
];
if types
.iter()
.any(|ty| ctx.is_assignable(left.ty, *ty) && ctx.is_assignable(right.ty, *ty))
{
let hir = if op.kind() == T![==] {
ctx.alloc_hir(Hir::Binary(BinaryOp::Eq, left.hir, right.hir))
} else {
ctx.alloc_hir(Hir::Binary(BinaryOp::Ne, left.hir, right.hir))
};
let mut value = Value::new(hir, ctx.builtins().types.bool);
if op.kind() == T![!=] {
value = value.flip_mappings();
}
return value;
}
(left, right)
}
_ => {
let left = left(ctx);
let right = right(ctx, None);
if ctx.is_unresolved(left.ty) || ctx.is_unresolved(right.ty) {
debug!("Unresolved binary expr operands");
return ctx.builtins().unresolved.clone();
}
(left, right)
}
};
let left_name = ctx.type_name(left.ty);
let right_name = ctx.type_name(right.ty);
debug!("Unresolved binary expr");
ctx.diagnostic(
binary.syntax(),
DiagnosticKind::IncompatibleBinaryOp(op.text().to_string(), left_name, right_name),
);
ctx.builtins().unresolved.clone()
}