endbasic_core/
value.rs

1// EndBASIC
2// Copyright 2022 Julio Merino
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License.  You may obtain a copy
6// of the License at:
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13// License for the specific language governing permissions and limitations
14// under the License.
15
16//! Operations on EndBASIC values.
17
18use crate::ast::*;
19use std::convert::TryFrom;
20
21/// Evaluation errors.
22#[derive(Debug, thiserror::Error)]
23#[error("{message}")]
24pub struct Error {
25    pub(crate) message: String,
26}
27
28impl Error {
29    /// Constructs a new evaluation error from a textual `message`.
30    pub(crate) fn new<S: Into<String>>(message: S) -> Self {
31        Self { message: message.into() }
32    }
33}
34
35/// Result for value computation return values.
36pub type Result<T> = std::result::Result<T, Error>;
37
38impl Value {
39    /// Given a `target` variable type, tries to convert the value to that type if they are
40    /// compatible or otherwise returns self.
41    ///
42    /// Can return an error when the conversion is feasible but it is not possible, such as trying
43    /// to cast a NaN to an integer.
44    pub(crate) fn maybe_cast(self, target: Option<ExprType>) -> Result<Value> {
45        match (target, self) {
46            (Some(ExprType::Integer), Value::Double(d)) => {
47                Ok(Value::Integer(double_to_integer(d)?))
48            }
49            (Some(ExprType::Double), Value::Integer(i)) => Ok(Value::Double(integer_to_double(i))),
50            (_, v) => Ok(v),
51        }
52    }
53}
54
55/// Converts the double `d` to an integer and fails if the conversion is not possible.
56pub fn double_to_integer(d: f64) -> Result<i32> {
57    let d = d.round();
58    if d.is_finite() && d >= (i32::MIN as f64) && (d <= i32::MAX as f64) {
59        Ok(d as i32)
60    } else {
61        Err(Error::new(format!("Cannot cast {} to integer due to overflow", d)))
62    }
63}
64
65/// Converts the integer `i` to a double.
66pub(crate) fn integer_to_double(i: i32) -> f64 {
67    i as f64
68}
69
70/// Performs a left shift.
71pub(crate) fn bitwise_shl(lhs: i32, rhs: i32) -> Result<i32> {
72    let bits = match u32::try_from(rhs) {
73        Ok(n) => n,
74        Err(_) => {
75            return Err(Error::new(format!("Number of bits to << ({}) must be positive", rhs)))
76        }
77    };
78
79    match lhs.checked_shl(bits) {
80        Some(i) => Ok(i),
81        None => Ok(0),
82    }
83}
84
85/// Performs a right shift.
86pub(crate) fn bitwise_shr(lhs: i32, rhs: i32) -> Result<i32> {
87    let bits = match u32::try_from(rhs) {
88        Ok(n) => n,
89        Err(_) => {
90            return Err(Error::new(format!("Number of bits to >> ({}) must be positive", rhs)))
91        }
92    };
93
94    match lhs.checked_shr(bits) {
95        Some(i) => Ok(i),
96        None if lhs < 0 => Ok(-1),
97        None => Ok(0),
98    }
99}
100
101/// Performs an arithmetic addition of integers.
102pub fn add_integer(lhs: i32, rhs: i32) -> Result<i32> {
103    match lhs.checked_add(rhs) {
104        Some(i) => Ok(i),
105        None => Err(Error::new("Integer overflow".to_owned())),
106    }
107}
108
109/// Performs an arithmetic subtraction of integers.
110pub fn sub_integer(lhs: i32, rhs: i32) -> Result<i32> {
111    match lhs.checked_sub(rhs) {
112        Some(i) => Ok(i),
113        None => Err(Error::new("Integer underflow".to_owned())),
114    }
115}
116
117/// Performs a multiplication of integers.
118pub fn mul_integer(lhs: i32, rhs: i32) -> Result<i32> {
119    match lhs.checked_mul(rhs) {
120        Some(i) => Ok(i),
121        None => Err(Error::new("Integer overflow".to_owned())),
122    }
123}
124
125/// Performs an arithmetic division of integers.
126pub fn div_integer(lhs: i32, rhs: i32) -> Result<i32> {
127    if rhs == 0 {
128        return Err(Error::new("Division by zero"));
129    }
130    match lhs.checked_div(rhs) {
131        Some(i) => Ok(i),
132        None => Err(Error::new("Integer underflow".to_owned())),
133    }
134}
135
136/// Performs a modulo operation of integers.
137pub fn modulo_integer(lhs: i32, rhs: i32) -> Result<i32> {
138    if rhs == 0 {
139        return Err(Error::new("Modulo by zero"));
140    }
141    match lhs.checked_rem(rhs) {
142        Some(i) => Ok(i),
143        None => Err(Error::new("Integer underflow".to_owned())),
144    }
145}
146
147/// Performs a power operation of integers.
148pub fn pow_integer(lhs: i32, rhs: i32) -> Result<i32> {
149    let exp = match u32::try_from(rhs) {
150        Ok(exp) => exp,
151        Err(_) => {
152            return Err(Error::new(format!("Exponent {} cannot be negative", rhs)));
153        }
154    };
155    match lhs.checked_pow(exp) {
156        Some(i) => Ok(i),
157        None => Err(Error::new("Integer overflow".to_owned())),
158    }
159}
160
161/// Performs an arithmetic negation of an integer.
162pub fn neg_integer(i: i32) -> Result<i32> {
163    match i.checked_neg() {
164        Some(i) => Ok(i),
165        None => Err(Error::new("Integer underflow".to_owned())),
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::Value::*;
172    use super::*;
173
174    #[test]
175    fn test_double_to_integer() {
176        assert_eq!(8, double_to_integer(8.4).unwrap());
177        assert_eq!(9, double_to_integer(8.5).unwrap());
178        assert_eq!(9, double_to_integer(8.6).unwrap());
179
180        double_to_integer(f64::NAN).unwrap_err();
181        double_to_integer(i32::MAX as f64 + 1.0).unwrap_err();
182        double_to_integer(i32::MIN as f64 - 1.0).unwrap_err();
183    }
184
185    #[test]
186    fn test_integer_to_double() {
187        assert_eq!(7.0, integer_to_double(7));
188    }
189
190    #[test]
191    fn test_value_maybe_cast() {
192        let all_types = [ExprType::Boolean, ExprType::Double, ExprType::Integer, ExprType::Text];
193        for target in all_types {
194            assert_eq!(Boolean(true), Boolean(true).maybe_cast(Some(target)).unwrap());
195            if target != ExprType::Integer {
196                assert_eq!(Double(3.8), Double(3.8).maybe_cast(Some(target)).unwrap());
197                match Double(f64::NAN).maybe_cast(Some(target)).unwrap() {
198                    Double(d) => assert!(d.is_nan()),
199                    _ => panic!(),
200                }
201            }
202            if target != ExprType::Double {
203                assert_eq!(Integer(3), Integer(3).maybe_cast(Some(target)).unwrap());
204            }
205            assert_eq!(
206                Text("a".to_owned()),
207                Text("a".to_owned()).maybe_cast(Some(target)).unwrap()
208            );
209        }
210
211        assert_eq!(Integer(8), Double(8.4).maybe_cast(Some(ExprType::Integer)).unwrap());
212        assert_eq!(Integer(9), Double(8.5).maybe_cast(Some(ExprType::Integer)).unwrap());
213        assert_eq!(Integer(9), Double(8.6).maybe_cast(Some(ExprType::Integer)).unwrap());
214        assert_eq!(Double(7.0), Integer(7).maybe_cast(Some(ExprType::Double)).unwrap());
215
216        Double(f64::NAN).maybe_cast(Some(ExprType::Integer)).unwrap_err();
217        assert_eq!(
218            Double(i32::MAX as f64),
219            Integer(i32::MAX).maybe_cast(Some(ExprType::Double)).unwrap()
220        );
221        Double(i32::MAX as f64 + 1.0).maybe_cast(Some(ExprType::Integer)).unwrap_err();
222        assert_eq!(
223            Double(i32::MIN as f64),
224            Integer(i32::MIN).maybe_cast(Some(ExprType::Double)).unwrap()
225        );
226        Double(i32::MIN as f64 - 1.0).maybe_cast(Some(ExprType::Integer)).unwrap_err();
227    }
228
229    #[test]
230    fn test_value_shl() {
231        assert_eq!(12, bitwise_shl(3, 2).unwrap());
232        assert_eq!(0xf0000000u32 as i32, bitwise_shl(0xf0000000u32 as i32, 0).unwrap());
233        assert_eq!(0x80000000u32 as i32, bitwise_shl(1, 31).unwrap());
234        assert_eq!(0, bitwise_shl(0xf0000000u32 as i32, 31).unwrap());
235
236        assert_eq!(0xe0000000u32 as i32, bitwise_shl(0xf0000000u32 as i32, 1).unwrap());
237        assert_eq!(0, bitwise_shl(0x80000000u32 as i32, 1).unwrap());
238        assert_eq!(0, bitwise_shl(1, 32).unwrap());
239        assert_eq!(0, bitwise_shl(1, 64).unwrap());
240
241        assert_eq!(
242            "Number of bits to << (-1) must be positive",
243            format!("{}", bitwise_shl(3, -1).unwrap_err())
244        );
245    }
246
247    #[test]
248    fn test_value_shr() {
249        assert_eq!(3, bitwise_shr(12, 2).unwrap());
250        assert_eq!(0xf0000000u32 as i32, bitwise_shr(0xf0000000u32 as i32, 0).unwrap());
251        assert_eq!(-1, bitwise_shr(0xf0000000u32 as i32, 31).unwrap());
252        assert_eq!(1, bitwise_shr(0x70000000u32 as i32, 30).unwrap());
253        assert_eq!(-2, bitwise_shr(-8, 2).unwrap());
254
255        assert_eq!(0xf0000000u32 as i32, bitwise_shr(0xe0000000u32 as i32, 1).unwrap());
256        assert_eq!(0xc0000000u32 as i32, bitwise_shr(0x80000000u32 as i32, 1).unwrap());
257        assert_eq!(0x38000000, bitwise_shr(0x70000000, 1).unwrap());
258        assert_eq!(0, bitwise_shr(0x70000000u32 as i32, 32).unwrap());
259        assert_eq!(0, bitwise_shr(0x70000000u32 as i32, 32).unwrap());
260        assert_eq!(-1, bitwise_shr(0x80000000u32 as i32, 32).unwrap());
261        assert_eq!(-1, bitwise_shr(0x80000000u32 as i32, 64).unwrap());
262
263        assert_eq!(
264            "Number of bits to >> (-1) must be positive",
265            format!("{}", bitwise_shr(3, -1).unwrap_err())
266        );
267    }
268
269    #[test]
270    fn test_value_add_integer() {
271        assert_eq!(5, add_integer(2, 3).unwrap());
272        assert_eq!(i32::MAX, add_integer(i32::MAX, 0).unwrap());
273        assert_eq!("Integer overflow", format!("{}", add_integer(i32::MAX, 1).unwrap_err()));
274    }
275
276    #[test]
277    fn test_value_sub_integer() {
278        assert_eq!(-1, sub_integer(2, 3).unwrap());
279        assert_eq!(i32::MIN, sub_integer(i32::MIN, 0).unwrap());
280        assert_eq!("Integer underflow", format!("{}", sub_integer(i32::MIN, 1).unwrap_err()));
281    }
282
283    #[test]
284    fn test_value_mul_integer() {
285        assert_eq!(6, mul_integer(2, 3).unwrap());
286        assert_eq!(i32::MAX, mul_integer(i32::MAX, 1).unwrap());
287        assert_eq!("Integer overflow", format!("{}", mul_integer(i32::MAX, 2).unwrap_err()));
288    }
289
290    #[test]
291    fn test_value_div_integer() {
292        assert_eq!(2, div_integer(10, 5).unwrap());
293        assert_eq!(6, div_integer(20, 3).unwrap());
294        assert_eq!(i32::MIN, div_integer(i32::MIN, 1).unwrap());
295        assert_eq!("Division by zero", format!("{}", div_integer(4, 0).unwrap_err()));
296        assert_eq!("Integer underflow", format!("{}", div_integer(i32::MIN, -1).unwrap_err()));
297    }
298
299    #[test]
300    fn test_value_modulo_integer() {
301        assert_eq!(0, modulo_integer(10, 5).unwrap());
302        assert_eq!(2, modulo_integer(20, 3).unwrap());
303        assert_eq!("Modulo by zero", format!("{}", modulo_integer(4, 0).unwrap_err()));
304        assert_eq!("Integer underflow", format!("{}", modulo_integer(i32::MIN, -1).unwrap_err()));
305    }
306
307    #[test]
308    fn test_value_pow_integer() {
309        assert_eq!(1, pow_integer(0, 0).unwrap());
310        assert_eq!(9, pow_integer(3, 2).unwrap());
311        assert_eq!(i32::MAX, pow_integer(i32::MAX, 1).unwrap());
312        assert_eq!("Integer overflow", format!("{}", pow_integer(i32::MAX, 2).unwrap_err()));
313        assert_eq!(
314            "Exponent -3 cannot be negative",
315            format!("{}", pow_integer(1, -3).unwrap_err())
316        );
317    }
318
319    #[test]
320    fn test_value_neg_integer() {
321        assert_eq!(-6, neg_integer(6).unwrap());
322        assert_eq!(5, neg_integer(-5).unwrap());
323    }
324}