1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Copyright (c) 2016-2021 Fabian Schuiki

//! This module implements constant value computation.

use crate::score::*;

// Calculate the constant value of an expression.
impl_make!(self, id: ExprRef => &Const {
    let hir = self.lazy_hir(id)?;
    Ok(match hir.data {
        // Integer literals.
        hir::ExprData::IntegerLiteral(ref c) => self.intern_const(c.clone()),

        // Float literals.
        hir::ExprData::FloatLiteral(ref c) => self.intern_const(c.clone()),

        // Unary operators.
        hir::ExprData::Unary(op, _, arg_id) => {
            let arg = self.const_value(arg_id)?;
            // TODO: Lookup the type of the current expression and perform
            // the operation accordingly.
            match op.value {
                UnaryOp::Pos => arg,
                UnaryOp::Neg => self.intern_const(arg.clone().negate()),
                _ => {
                    self.emit(
                        DiagBuilder2::bug(format!("constant unary operator {:?} not yet implemented", op.value))
                        .span(op.span)
                    );
                    return Err(());
                }
            }
        }

        // Ranges.
        hir::ExprData::Range(dir, lb_id, rb_id) => {
            // TODO: Determine the type of ourself, then make sure the const
            // values are all cast appropriately.
            let lb = self.const_value(lb_id)?;
            let rb = self.const_value(rb_id)?;
            match (lb, rb) {
                (&Const::Int(ref lb), &Const::Int(ref rb)) => {
                    self.intern_const(ConstIntRange::new(dir, lb.clone(), rb.clone()))
                }
                (&Const::Float(ref lb), &Const::Float(ref rb)) => {
                    self.intern_const(ConstFloatRange::new(dir, lb.clone(), rb.clone()))
                }
                _ => {
                    self.emit(
                        DiagBuilder2::error("left and right bound of range must both be integer or float")
                        .span(hir.span)
                    );
                    return Err(());
                }
            }
        }

        // Names.
        hir::ExprData::Name(Def::Enum(EnumRef(decl, index)), _) => self.intern_const(ConstEnum::new(decl, index)),

        // All other expressions cannot be turned into a constant value.
        _ => {
            self.emit(
                DiagBuilder2::error("expression does not have a constant value")
                .span(hir.span)
            );
            return Err(());
        }
    })
});