rslua_march1917/
consts.rs

1use crate::compiler::CompileError;
2use crate::success;
3use crate::types::{FloatType, IntType};
4use num_traits::Float;
5use std::hash::{Hash, Hasher};
6
7#[derive(Clone, PartialEq)]
8pub enum Const {
9    Int(IntType),
10    Float(FloatType),
11    Str(String),
12}
13
14impl Eq for Const {}
15
16impl Hash for Const {
17    fn hash<H: Hasher>(&self, state: &mut H) {
18        match self {
19            Const::Int(i) => i.hash(state),
20            Const::Float(f) => {
21                let (m, e, s) = Float::integer_decode(*f);
22                m.hash(state);
23                e.hash(state);
24                s.hash(state);
25            }
26            Const::Str(s) => s.hash(state),
27        }
28    }
29}
30
31fn float_to_int(f: FloatType) -> Option<IntType> {
32    if f.floor() == f {
33        Some(f as IntType)
34    } else {
35        None
36    }
37}
38
39fn ignore_unhashable_float(
40    input: Result<Option<Const>, CompileError>,
41) -> Result<Option<Const>, CompileError> {
42    // compitibale with lua
43    // won't constant folding Nan/Inf/0.0
44    match &input {
45        Ok(k) => match k {
46            Some(k) => match k {
47                Const::Float(f) if (*f).is_nan() || (*f).is_infinite() || *f == 0.0 => Ok(None),
48                _ => input,
49            },
50            _ => input,
51        },
52        _ => input,
53    }
54}
55
56macro_rules! bin_op {
57    ($name:ident, $int_int:expr, $int_float:expr, $float_int:expr, $float_float:expr) => {
58        pub fn $name(self, other: Const) -> Result<Option<Const>, CompileError> {
59            let result = match self {
60                Const::Int(a) => match other {
61                    Const::Int(b) => $int_int(a, b),
62                    Const::Float(b) => $int_float(a, b),
63                    _ => unreachable!(),
64                },
65                Const::Float(a) => match other {
66                    Const::Int(b) => $float_int(a, b),
67                    Const::Float(b) => $float_float(a, b),
68                    _ => unreachable!(),
69                },
70                _ => unreachable!(),
71            };
72
73            ignore_unhashable_float(result)
74        }
75    };
76}
77
78macro_rules! bin_op_normal {
79    ($name:ident, $op:tt) => {
80        bin_op! {
81            $name,
82            |a, b| success!(Const::Int(a $op b)),
83            |a, b| success!(Const::Float(a as FloatType $op b)),
84            |a, b| success!(Const::Float(a $op b as FloatType)),
85            |a, b| success!(Const::Float(a $op b))
86        }
87    };
88}
89
90macro_rules! bin_op_int {
91    ($name:ident, $op:tt) => {
92        bin_op! {
93            $name,
94            |a, b| success!(Const::Int(a $op b)),
95            |a, b| Ok(float_to_int(b).map(|b| Const::Int(a $op b))),
96            |a, b| Ok(float_to_int(a).map(|a| Const::Int(a $op b))),
97            |a, b| Ok(float_to_int(a).and_then(|a| float_to_int(b).and_then(|b| Some(Const::Int(a $op b)))))
98        }
99    };
100}
101
102impl Const {
103    bin_op_normal! {add, +}
104    bin_op_normal! {sub, -}
105    bin_op_normal! {mul, *}
106
107    bin_op! {
108        div,
109        |a, b| success!(Const::Float(a as FloatType / b as FloatType)),
110        |a, b| success!(Const::Float(a as FloatType / b)),
111        |a, b| success!(Const::Float(a / b as FloatType)),
112        |a, b| success!(Const::Float(a / b))
113    }
114
115    bin_op! {
116        idiv,
117        |a, b| if b == 0 { Err(CompileError::new("divide by zero")) } else { success!(Const::Int(a / b)) },
118        |_, _| Ok(None),
119        |_, _| Ok(None),
120        |_, _| Ok(None)
121    }
122
123    bin_op! {
124        mod_,
125        |a, b| success!(Const::Int(a % b)),
126        |a, b| success!(Const::Float(a as FloatType % b)),
127        |a, b| success!(Const::Float(a % b as FloatType)),
128        |a, b| success!(Const::Float(a % b))
129    }
130
131    bin_op! {
132        pow,
133        |a, b| success!(Const::Float((a as FloatType).powf(b as FloatType))),
134        |a, b| success!(Const::Float((a as FloatType).powf(b))),
135        |a:FloatType, b| success!(Const::Float(a.powf(b as FloatType))),
136        |a:FloatType, b| success!(Const::Float(a.powf(b)))
137    }
138
139    bin_op_int! {band, &}
140    bin_op_int! {bor, |}
141    bin_op_int! {bxor, ^}
142    bin_op_int! {shl, <<}
143    bin_op_int! {shr, >>}
144
145    pub fn minus(&self) -> Result<Option<Const>, CompileError> {
146        let result = match self {
147            Const::Int(i) => success!(Const::Int(-i)),
148            Const::Float(f) => success!(Const::Float(-f)),
149            _ => return Ok(None),
150        };
151        ignore_unhashable_float(result)
152    }
153
154    pub fn bnot(&self)-> Result<Option<Const>, CompileError> {
155        match self {
156            Const::Int(i) => success!(Const::Int(!i)),
157            _ => return Ok(None)
158        }
159    }
160}