sphinx/runtime/types/
numeric.rs

1use core::str::FromStr;
2use core::fmt::{self, Write};
3use crate::language::{IntType, FloatType};
4use crate::runtime::Variant;
5use crate::runtime::strings::{StringValue, StrBuffer};
6use crate::runtime::types::{MetaObject, Type};
7use crate::runtime::errors::{ExecResult, RuntimeError};
8
9macro_rules! checked_int_math {
10    ( $method:tt, $lhs:expr, $rhs:expr ) => {
11        match $lhs.$method($rhs) {
12            Some(value) => Ok(Variant::Integer(value)),
13            None => Err(RuntimeError::overflow_error()),
14        }
15    };
16}
17
18pub fn int_from_str(s: &str, radix: IntType) -> ExecResult<IntType> {
19    if !(2..=36).contains(&radix) {
20        return Err(RuntimeError::invalid_value("invalid radix"));
21    }
22    
23    let value = IntType::from_str_radix(s, radix.try_into().unwrap())
24        .map_err(|_| RuntimeError::invalid_value(format!(
25            "could not parse \"{}\" as int with radix {}", s, radix)
26        ))?;
27    Ok(value)
28}
29
30impl MetaObject for IntType {
31    fn type_tag(&self) -> Type { Type::Integer }
32    
33    fn as_bits(&self) -> Option<ExecResult<IntType>> { Some(Ok(*self)) }
34    fn as_int(&self) -> Option<ExecResult<IntType>> { Some(Ok(*self)) }
35    fn as_float(&self) -> Option<ExecResult<FloatType>> { Some(Ok(*self as FloatType)) }
36    
37    fn op_neg(&self) -> Option<ExecResult<Variant>> { Some(Ok(Variant::from(-(*self)))) }
38    fn op_pos(&self) -> Option<ExecResult<Variant>> { Some(Ok(Variant::from(*self))) }
39    fn op_inv(&self) -> Option<ExecResult<Variant>> { Some(Ok(Variant::from(!(*self)))) }
40    
41    fn op_mul(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
42        match rhs {
43            Variant::Integer(rhs) => Some(checked_int_math!(checked_mul, *self, *rhs)),
44            _ => rhs.as_meta().as_int()
45                .map(|rhs| checked_int_math!(checked_mul, *self, rhs?))
46        }
47    }
48    
49    fn op_rmul(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
50        self.op_mul(lhs)
51    }
52    
53    fn op_div(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
54        rhs.as_meta().as_int().map(|rhs| {
55            let rhs = rhs?;
56            if rhs == 0 {
57                Err(RuntimeError::divide_by_zero())
58            } else {
59                checked_int_math!(checked_div, *self, rhs)
60            }
61        })
62    }
63    
64    fn op_rdiv(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
65        lhs.as_meta().as_int().map(|lhs| {
66            if *self == 0 {
67                Err(RuntimeError::divide_by_zero())
68            } else {
69                checked_int_math!(checked_div, lhs?, *self)
70            }
71        })
72    }
73    
74    fn op_mod(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
75        rhs.as_meta().as_int().map(|rhs| Ok(Variant::from(*self % rhs?)))
76    }
77    
78    fn op_rmod(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
79        lhs.as_meta().as_int().map(|lhs| Ok(Variant::from(lhs? % *self)))
80    }
81    
82    fn op_add(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
83        match rhs {
84            Variant::Integer(rhs) => Some(checked_int_math!(checked_add, *self, *rhs)),
85            _ => rhs.as_meta().as_int()
86                .map(|rhs| checked_int_math!(checked_add, *self, rhs?))
87        }
88    }
89    
90    fn op_radd(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
91        self.op_add(lhs)
92    }
93    
94    fn op_sub(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
95        match rhs {
96            Variant::Integer(rhs) => Some(checked_int_math!(checked_sub, *self, *rhs)),
97            _ => rhs.as_meta().as_int()
98                .map(|rhs| checked_int_math!(checked_sub, *self, rhs?))
99        }
100    }
101    
102    fn op_rsub(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
103        match lhs {
104            Variant::Integer(lhs) => Some(checked_int_math!(checked_sub, *lhs, *self)),
105            _ => lhs.as_meta().as_int()
106                .map(|lhs| checked_int_math!(checked_sub, lhs?, *self))
107        }
108    }
109    
110    fn op_and(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
111        let lhs = self.as_bits().unwrap().unwrap();
112        match rhs {
113            Variant::BoolFalse => Some(Ok(Variant::from(lhs & false.as_bits().unwrap().unwrap()))),
114            Variant::BoolTrue => Some(Ok(Variant::from(lhs & true.as_bits().unwrap().unwrap()))),
115            Variant::Integer(rhs) => Some(Ok(Variant::from(lhs & rhs.as_bits().unwrap().unwrap()))),
116            _ => rhs.as_meta().as_bits()
117                .map(|rhs| Ok(Variant::from(lhs & rhs?)))
118        }
119    }
120    
121    fn op_rand(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
122        self.op_and(lhs)
123    }
124
125    fn op_xor(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
126        let lhs = self.as_bits().unwrap().unwrap();
127        match rhs {
128            Variant::BoolFalse => Some(Ok(Variant::from(lhs ^ false.as_bits().unwrap().unwrap()))),
129            Variant::BoolTrue => Some(Ok(Variant::from(lhs ^ true.as_bits().unwrap().unwrap()))),
130            Variant::Integer(rhs) => Some(Ok(Variant::from(lhs ^ rhs.as_bits().unwrap().unwrap()))),
131            _ => rhs.as_meta().as_bits()
132                .map(|rhs| Ok(Variant::from(lhs ^ rhs?)))
133        }
134    }
135    
136    fn op_rxor(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
137        self.op_xor(lhs)
138    }
139
140    fn op_or(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
141        let lhs = self.as_bits().unwrap().unwrap();
142        match rhs {
143            Variant::BoolFalse => Some(Ok(Variant::from(lhs | false.as_bits().unwrap().unwrap()))),
144            Variant::BoolTrue => Some(Ok(Variant::from(lhs | true.as_bits().unwrap().unwrap()))),
145            Variant::Integer(rhs) => Some(Ok(Variant::from(lhs | rhs.as_bits().unwrap().unwrap()))),
146            _ => rhs.as_meta().as_bits()
147                .map(|rhs| Ok(Variant::from(lhs | rhs?)))
148        }
149    }
150    
151    fn op_ror(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
152        self.op_or(lhs)
153    }
154    
155    fn op_shl(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
156        let rhs = match rhs {
157            Variant::BoolFalse => Some(0),
158            Variant::BoolTrue => Some(1),
159            Variant::Integer(rhs) => Some(*rhs),
160            _ => match rhs.as_meta().as_int() {
161                Some(Ok(rhs)) => Some(rhs),
162                Some(Err(error)) => return Some(Err(error)),
163                None => None,
164            },
165        };
166        
167        rhs.map(|rhs| {
168            if rhs < 0 {
169                return Err(RuntimeError::negative_shift_count());
170            }
171            return checked_int_math!(checked_shl, *self, rhs.try_into().unwrap());
172        })
173    }
174    
175    fn op_rshl(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
176        lhs.as_meta().as_bits().map(|lhs| {
177            let lhs = lhs?;
178            if *self < 0 {
179                return Err(RuntimeError::negative_shift_count());
180            }
181            return checked_int_math!(checked_shl, lhs, (*self).try_into().unwrap());
182        })
183    }
184    
185    fn op_shr(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
186        let rhs = match rhs {
187            Variant::BoolFalse => Some(0),
188            Variant::BoolTrue => Some(1),
189            Variant::Integer(rhs) => Some(*rhs),
190            _ => match rhs.as_meta().as_int() {
191                Some(Ok(rhs)) => Some(rhs),
192                Some(Err(error)) => return Some(Err(error)),
193                None => None,
194            },
195        };
196        
197        rhs.map(|rhs| {
198            if rhs < 0 {
199                return Err(RuntimeError::negative_shift_count());
200            }
201            return checked_int_math!(checked_shr, *self, rhs.try_into().unwrap());
202        })
203    }
204    
205    fn op_rshr(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
206        lhs.as_meta().as_bits().map(|lhs| {
207            let lhs = lhs?;
208            if *self < 0 {
209                return Err(RuntimeError::negative_shift_count());
210            }
211            return checked_int_math!(checked_shr, lhs, (*self).try_into().unwrap());
212        })
213    }
214    
215    fn cmp_eq(&self, other: &Variant) -> Option<ExecResult<bool>> {
216        match other {
217            Variant::Integer(other) => Some(Ok(*self == *other)),
218            _ => other.as_meta().as_int()
219                .map(|other| Ok(*self == other?))
220        }
221    }
222    
223    fn cmp_lt(&self, other: &Variant) -> Option<ExecResult<bool>> {
224        match other {
225            Variant::Integer(other) => Some(Ok(*self < *other)),
226            _ => other.as_meta().as_int()
227                .map(|other| Ok(*self < other?))
228        }
229    }
230    
231    fn cmp_le(&self, other: &Variant) -> Option<ExecResult<bool>> {
232        match other {
233            Variant::Integer(other) => Some(Ok(*self <= *other)),
234            _ => other.as_meta().as_int()
235                .map(|other| Ok(*self <= other?))
236        }
237    }
238    
239    fn fmt_repr(&self) -> ExecResult<StringValue> {
240        let mut buf = StrBuffer::<32>::new();
241        if write!(buf, "{}", *self).is_ok() {
242            Ok(StringValue::new_maybe_interned(buf))
243        } else {
244            // resort to allocated buffer
245            Ok(StringValue::new_maybe_interned(format!("{}", *self)))
246        }
247    }
248}
249
250
251pub fn float_from_str(s: &str) -> ExecResult<FloatType> {
252    let value = FloatType::from_str(s)
253        .map_err(|_| RuntimeError::invalid_value(format!(
254            "could not parse \"{}\" as float", s
255        )))?;
256    Ok(value)
257}
258
259impl MetaObject for FloatType {
260    fn type_tag(&self) -> Type { Type::Float }
261    
262    fn as_float(&self) -> Option<ExecResult<FloatType>> { Some(Ok(*self)) }
263    
264    fn op_neg(&self) -> Option<ExecResult<Variant>> { Some(Ok(Variant::from(-(*self)))) }
265    fn op_pos(&self) -> Option<ExecResult<Variant>> { Some(Ok(Variant::from(*self))) }
266    
267    fn op_mul(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
268        rhs.as_meta().as_float().map(|rhs| Ok(Variant::from(*self * rhs?)))
269    }
270    
271    fn op_rmul(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
272        self.op_mul(lhs)
273    }
274    
275    fn op_div(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
276        rhs.as_meta().as_float().map(|rhs| Ok(Variant::from(*self / rhs?)))
277    }
278    
279    fn op_rdiv(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
280        lhs.as_meta().as_float().map(|lhs| Ok(Variant::from(lhs? / *self)))
281    }
282    
283    fn op_mod(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
284        rhs.as_meta().as_float().map(|rhs| Ok(Variant::from(*self % rhs?)))
285    }
286    
287    fn op_rmod(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
288        lhs.as_meta().as_float().map(|lhs| Ok(Variant::from(lhs? % *self)))
289    }
290    
291    fn op_add(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
292        rhs.as_meta().as_float().map(|rhs| Ok(Variant::from(*self + rhs?)))
293    }
294    
295    fn op_radd(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
296        self.op_add(lhs)
297    }
298    
299    fn op_sub(&self, rhs: &Variant) -> Option<ExecResult<Variant>> {
300        rhs.as_meta().as_float().map(|rhs| Ok(Variant::from(*self - rhs?)))
301    }
302    
303    fn op_rsub(&self, lhs: &Variant) -> Option<ExecResult<Variant>> {
304        lhs.as_meta().as_float().map(|lhs| Ok(Variant::from(lhs? - *self)))
305    }
306    
307    fn cmp_eq(&self, other: &Variant) -> Option<ExecResult<bool>> {
308        other.as_meta().as_float().map(|other| Ok(*self == other?))
309    }
310    
311    fn cmp_lt(&self, other: &Variant) -> Option<ExecResult<bool>> {
312        other.as_meta().as_float().map(|other| Ok(*self < other?))
313    }
314    
315    fn cmp_le(&self, other: &Variant) -> Option<ExecResult<bool>> {
316        other.as_meta().as_float().map(|other| Ok(*self <= other?))
317    }
318    
319    fn fmt_repr(&self) -> ExecResult<StringValue> {
320        fn write_float(value: FloatType, fmt: &mut impl fmt::Write) -> fmt::Result {
321            if !value.is_finite() || value.trunc() != value {
322                write!(fmt, "{}", value)
323            } else {
324                write!(fmt, "{}.0", value)
325            }
326        }
327        
328        let mut buf = StrBuffer::<32>::new();
329        if write_float(*self, &mut buf).is_ok() {
330            Ok(StringValue::new_maybe_interned(buf))
331        } else {
332            let mut buf = String::new();
333            write_float(*self, &mut buf)
334                .map_err(|error| RuntimeError::other(error.to_string()))?;
335            Ok(StringValue::new_maybe_interned(buf))
336        }
337    }
338}
339