rue_compiler/compile/expr/
prefix.rs

1use std::borrow::Cow;
2
3use log::debug;
4use rue_ast::{AstNode, AstPrefixExpr};
5use rue_diagnostic::DiagnosticKind;
6use rue_hir::{Hir, UnaryOp, Value};
7use rue_lir::{atom_bigint, bigint_atom};
8use rue_parser::T;
9use rue_types::{Atom, AtomRestriction, AtomSemantic, Atoms, Type};
10
11use crate::{Compiler, compile_expr};
12
13pub fn compile_prefix_expr(ctx: &mut Compiler, prefix: &AstPrefixExpr) -> Value {
14    let Some(expr) = prefix.expr() else {
15        debug!("Unresolved prefix expr");
16        return ctx.builtins().unresolved.clone();
17    };
18
19    let value = compile_expr(ctx, &expr, None);
20
21    if ctx.is_unresolved(value.ty) {
22        debug!("Unresolved prefix expr");
23        return ctx.builtins().unresolved.clone();
24    }
25
26    let Some(op) = prefix.op() else {
27        return value;
28    };
29
30    match op.kind() {
31        T![!] => {
32            if ctx.is_assignable(value.ty, ctx.builtins().types.bool) {
33                let is_true = ctx.is_assignable(value.ty, ctx.builtins().types.bool_true);
34                let is_false = ctx.is_assignable(value.ty, ctx.builtins().types.bool_false);
35
36                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Not, value.hir));
37
38                return value.flip_mappings().with_hir(hir).with_type(if is_true {
39                    ctx.builtins().types.bool_false
40                } else if is_false {
41                    ctx.builtins().types.bool_true
42                } else {
43                    ctx.builtins().types.bool
44                });
45            }
46        }
47        T![+] => {
48            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
49                ctx.diagnostic(prefix.syntax(), DiagnosticKind::UnnecessaryPlus);
50                return Value::new(value.hir, ctx.builtins().types.int);
51            }
52        }
53        T![-] => {
54            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
55                let ty = if let Some(Atoms::Restricted(restrictions)) =
56                    rue_types::extract_atoms(ctx.types_mut(), value.ty, true)
57                    && restrictions.len() == 1
58                    && let Some(AtomRestriction::Value(value)) = restrictions.iter().next()
59                {
60                    ctx.alloc_type(Type::Atom(Atom::new(
61                        AtomSemantic::Int,
62                        Some(AtomRestriction::Value(Cow::Owned(bigint_atom(
63                            -atom_bigint(value),
64                        )))),
65                    )))
66                } else {
67                    ctx.builtins().types.int
68                };
69
70                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Neg, value.hir));
71                return Value::new(hir, ty);
72            }
73
74            if ctx.is_assignable(value.ty, ctx.builtins().types.public_key) {
75                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::G1Negate, value.hir));
76                return Value::new(hir, ctx.builtins().types.public_key);
77            }
78
79            if ctx.is_assignable(value.ty, ctx.builtins().types.signature) {
80                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::G2Negate, value.hir));
81                return Value::new(hir, ctx.builtins().types.signature);
82            }
83        }
84        T![~] => {
85            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
86                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::BitwiseNot, value.hir));
87                return Value::new(hir, ctx.builtins().types.int);
88            }
89        }
90        _ => {}
91    }
92
93    debug!("Unresolved prefix expr due to incompatible op");
94
95    let type_name = ctx.type_name(value.ty);
96    ctx.diagnostic(
97        prefix.syntax(),
98        DiagnosticKind::IncompatibleUnaryOp(op.text().to_string(), type_name),
99    );
100    ctx.builtins().unresolved.clone()
101}