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 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}