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    let Some(op) = prefix.op() else {
22        return value;
23    };
24
25    match op.kind() {
26        T![!] => {
27            if ctx.is_assignable(value.ty, ctx.builtins().types.bool) {
28                let is_true = ctx.is_assignable(value.ty, ctx.builtins().types.bool_true);
29                let is_false = ctx.is_assignable(value.ty, ctx.builtins().types.bool_false);
30
31                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Not, value.hir));
32
33                return value.flip_mappings().with_hir(hir).with_type(if is_true {
34                    ctx.builtins().types.bool_false
35                } else if is_false {
36                    ctx.builtins().types.bool_true
37                } else {
38                    ctx.builtins().types.bool
39                });
40            }
41        }
42        T![+] => {
43            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
44                ctx.diagnostic(prefix.syntax(), DiagnosticKind::UnnecessaryPlus);
45                return Value::new(value.hir, ctx.builtins().types.int);
46            }
47        }
48        T![-] => {
49            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
50                let ty = if let Some(Atoms::Restricted(restrictions)) =
51                    rue_types::extract_atoms(ctx.types_mut(), value.ty, true)
52                    && restrictions.len() == 1
53                    && let Some(AtomRestriction::Value(value)) = restrictions.iter().next()
54                {
55                    ctx.alloc_type(Type::Atom(Atom::new(
56                        AtomSemantic::Int,
57                        Some(AtomRestriction::Value(Cow::Owned(bigint_atom(
58                            -atom_bigint(value),
59                        )))),
60                    )))
61                } else {
62                    ctx.builtins().types.int
63                };
64
65                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::Neg, value.hir));
66                return Value::new(hir, ty);
67            }
68
69            if ctx.is_assignable(value.ty, ctx.builtins().types.public_key) {
70                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::G1Negate, value.hir));
71                return Value::new(hir, ctx.builtins().types.public_key);
72            }
73
74            if ctx.is_assignable(value.ty, ctx.builtins().types.signature) {
75                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::G2Negate, value.hir));
76                return Value::new(hir, ctx.builtins().types.signature);
77            }
78        }
79        T![~] => {
80            if ctx.is_assignable(value.ty, ctx.builtins().types.int) {
81                let hir = ctx.alloc_hir(Hir::Unary(UnaryOp::BitwiseNot, value.hir));
82                return Value::new(hir, ctx.builtins().types.int);
83            }
84        }
85        _ => {}
86    }
87
88    debug!("Unresolved prefix expr due to incompatible op");
89
90    let type_name = ctx.type_name(value.ty);
91    ctx.diagnostic(
92        prefix.syntax(),
93        DiagnosticKind::IncompatibleUnaryOp(op.text().to_string(), type_name),
94    );
95    ctx.builtins().unresolved.clone()
96}