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
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use num_traits::Float;
use std::hash::{Hash, Hasher};
use crate::types::{FloatType, IntType};
#[derive(Clone, PartialEq)]
pub enum Const {
    Int(IntType),
    Float(FloatType),
    Str(String),
}

impl Eq for Const {}

impl Hash for Const {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            Const::Int(i) => i.hash(state),
            Const::Float(f) => {
                let (m, e, s) = Float::integer_decode(*f);
                m.hash(state);
                e.hash(state);
                s.hash(state);
            }
            Const::Str(s) => s.hash(state),
        }
    }
}

macro_rules! bin_op {
    ($name:ident, $int_int:expr, $int_float:expr, $float_int:expr, $float_float:expr) => {
        pub fn $name(self, other: Const) -> Option<Const> {
            match self {
                Const::Int(a) => match other {
                    Const::Int(b) => $int_int(a, b),
                    Const::Float(b) => $int_float(a, b),
                    _ => unreachable!()
                },
                Const::Float(a) => match other {
                    Const::Int(b) => $float_int(a, b),
                    Const::Float(b) => $float_float(a, b),
                    _ => unreachable!()
                },
                _ => unreachable!()
            }
        }    
    };
}

macro_rules! bin_op_normal {
    ($name:ident, $op:tt) => {
        bin_op! {
            $name, 
            |a, b| Some(Const::Int(a $op b)), 
            |a, b| Some(Const::Float(a as FloatType $op b)), 
            |a, b| Some(Const::Float(a $op b as FloatType)), 
            |a, b| Some(Const::Float(a $op b))
        } 
    };
}

macro_rules! bin_op_int {
    ($name:ident, $op:tt) => {
        bin_op! {
            $name, 
            |a, b| Some(Const::Int(a $op b)), 
            |_, _| None, 
            |_, _| None, 
            |_, _| None
        } 
    };
}

impl Const {
    bin_op_normal! {add, +}
    bin_op_normal! {sub, -}
    bin_op_normal! {mul, *}
    
    bin_op! {
        div,
        |a, b| Some(Const::Float(a as FloatType / b as FloatType)),
        |a, b| Some(Const::Float(a as FloatType / b)),
        |a, b| Some(Const::Float(a / b as FloatType)),
        |a, b| Some(Const::Float(a / b as FloatType))
    }

    bin_op! {
        idiv,
        |a, b| Some(Const::Int(a / b)),
        |a, b| Some(Const::Float((a / b as IntType) as FloatType)),
        |a, b| Some(Const::Float((a as IntType / b) as FloatType)),
        |a, b| Some(Const::Float((a as IntType / b as IntType) as FloatType))
    }

    bin_op! {
        mod_,
        |a, b| Some(Const::Int(a % b)),
        |a, b| Some(Const::Float(a as FloatType % b)),
        |a, b| Some(Const::Float(a % b as FloatType)),
        |a, b| Some(Const::Float(a % b))
    }

    bin_op! {
        pow,
        |a, b| Some(Const::Float((a as FloatType).powf(b as FloatType))),
        |a, b| Some(Const::Float((a as FloatType).powf(b))),
        |a:FloatType, b| Some(Const::Float(a.powf(b as FloatType))),
        |a:FloatType, b| Some(Const::Float(a.powf(b)))
    }

    bin_op_int! {band, &}
    bin_op_int! {bor, |}
    bin_op_int! {bxor, ^}
    bin_op_int! {shl, <<}
    bin_op_int! {shr, >>}
}