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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
use cas_parser::parser::ast::range::RangeKind;
use crate::consts::PI;
use crate::primitive::{complex, float};
use rug::{Complex, Float, Integer};
use std::{cell::RefCell, fmt::{Display, Formatter}, rc::Rc};
use super::{fmt::{FormatOptions, ValueFormatter}, func::Function};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Represents any value that can be stored in a variable.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Value {
/// A floating-point value.
Float(Float),
/// An integer value.
Integer(Integer),
/// A complex number value.
Complex(Complex),
/// A boolean.
Boolean(bool),
/// The unit type, analogous to `()` in Rust.
Unit,
/// A list of values.
///
/// In `cas-rs`, a list is a reference to a vector of values. This is done to allow efficient
/// cloning of lists, as well as mutation of lists in-place. References are passed around
/// by default, which can result in somewhat confusing behavior, for example:
///
/// ```text
/// a = [1, 2, 3]
/// b = a
/// b[0] = 4
/// print(a) // prints [4, 2, 3]
/// ```
///
/// TODO: In the future, a `clone` method may be added to `cas-rs` to allow the user to
/// explicitly clone the list instead of copying the reference.
List(Rc<RefCell<Vec<Value>>>),
/// A range representing a sequence of values, either half-open or closed.
Range(Box<Value>, RangeKind, Box<Value>),
/// A function.
///
/// Functions are treated as values just like any other value in `cas-rs`; they can be stored
/// in variables, passed as arguments to other functions, and returned from functions.
Function(Function),
}
impl Value {
/// Returns the typename of this value.
pub fn typename(&self) -> &'static str {
match self {
Value::Float(_) => "Float",
Value::Integer(_) => "Integer",
Value::Complex(_) => "Complex",
Value::Boolean(_) => "Boolean",
Value::Unit => "Unit",
Value::List(_) => "List",
Value::Range(_, _, _) => "Range",
Value::Function(_) => "Function",
}
}
/// Consumes and attempts to coerce the value to a real number. **Note that this coercion can
/// be lossy** if converting an arbitrary-precision integer to a fixed-width float. To preserve
/// precision, see [`Value::coerce_integer`] and [`Value::coerce_number`].
///
/// This conversion only occurs if one of the following is true:
///
/// - The value is an integer.
/// - The value is a complex number with a zero imaginary part.
///
/// This is useful for when evaluation of an expression results in a [`Value::Complex`] with a
/// zero value for the imaginary part. Using a complex number for certain operators, such as
/// the bitwise operators, will result in an error, so we will need to coerce those values to
/// [`Value::Float`] instead.
pub fn coerce_float(self) -> Self {
match self {
Value::Integer(n) => Value::Float(float(n)),
Value::Complex(c) if c.imag().is_zero() => Value::Float(c.into_real_imag().0),
_ => self,
}
}
/// Consumes and attempts to coerce the value to an integer. **This coercion is lossless**.
///
/// This conversion only occurs if one of the following is true:
///
/// - The value is a float with a zero fractional part.
/// - The value is a complex number with a zero imaginary part, and a real part with a zero
/// fractional part.
pub fn coerce_integer(self) -> Self {
match self {
Value::Float(n) if n.is_integer() => Value::Integer(n.to_integer().unwrap()),
Value::Complex(c) if c.imag().is_zero() && c.real().is_integer() => {
Value::Integer(c.into_real_imag().0.to_integer().unwrap())
}
_ => self,
}
}
/// Consumes and attempts to coerce the value to a real number or an integer, preferring
/// integers if possible. **This coercion is lossless**.
///
/// This conversion follows these rules:
///
/// - If the value is an integer, it is returned as-is.
/// - If the value is a float with a zero fractional part, it is converted to an integer.
/// Otherwise, it is returned as-is.
/// - If the value is a complex number with a zero imaginary part, either an integer or float
/// is returned if the real part is an integer or float, respectively. Otherwise, it is
/// returned as-is.
pub fn coerce_number(self) -> Self {
match self {
Value::Float(n) if n.is_integer() => Value::Integer(n.to_integer().unwrap()),
Value::Complex(c) if c.imag().is_zero() => {
let (real, _) = c.into_real_imag();
if real.is_integer() {
Value::Integer(real.to_integer().unwrap())
} else {
Value::Float(real)
}
}
_ => self,
}
}
/// Consumes and attempts to coerce the value to a complex number. This coercion is lossless.
pub fn coerce_complex(self) -> Self {
match self {
Value::Float(n) => Value::Complex(complex(n)),
Value::Integer(n) => Value::Complex(complex(n)),
_ => self,
}
}
/// Converts this value from radians to degrees. If it is a real number, it is converted as
/// usual. If it is a complex number, the real and imaginary parts are converted separately.
pub fn into_degrees(self) -> Self {
let convert = |n: Float| n * 180.0 / &*PI;
match self {
Value::Float(n) => Value::Float(convert(n)),
Value::Integer(n) => Value::Float(convert(float(n))),
Value::Complex(c) => Value::Complex({
let (real, imag) = c.into_real_imag();
complex((convert(real), convert(imag)))
}),
_ => self,
}
}
/// Converts this value from degrees to radians. If it is a real number, it is converted as
/// usual. If it is a complex number, the real and imaginary parts are converted separately.
pub fn into_radians(self) -> Self {
let convert = |n: Float| n * &*PI / 180.0;
match self {
Value::Float(n) => Value::Float(convert(n)),
Value::Integer(n) => Value::Float(convert(float(n))),
Value::Complex(c) => Value::Complex({
let (real, imag) = c.into_real_imag();
complex((convert(real), convert(imag)))
}),
_ => self,
}
}
/// Returns true if this value is a real number, or can be coerced to one.
pub fn is_float(&self) -> bool {
match self {
Value::Float(_) => true,
Value::Integer(_) => true,
Value::Complex(c) => c.imag().is_zero(),
_ => false,
}
}
/// Returns true if this value is an integer, or can be coerced to one.
pub fn is_integer(&self) -> bool {
match self {
Value::Float(n) => n.is_integer(),
Value::Integer(_) => true,
Value::Complex(c) => c.imag().is_zero() && c.real().is_integer(),
_ => false,
}
}
/// Returns true if this value is a complex number, or can be coerced to one.
pub fn is_complex(&self) -> bool {
matches!(self, Value::Complex(_) | Value::Float(_) | Value::Integer(_))
}
/// Returns true if this value is a boolean.
pub fn is_boolean(&self) -> bool {
matches!(self, Value::Boolean(_))
}
/// Returns true if this value is a unit type.
pub fn is_unit(&self) -> bool {
matches!(self, Value::Unit)
}
/// Returns true if this value is a list.
pub fn is_list(&self) -> bool {
matches!(self, Value::List(_))
}
/// Returns true if this value is truthy.
///
/// For each type, the following values are considered "truthy":
///
/// - `Float`: any value except `0.0` and `NaN`
/// - `Integer`: any value except `0`
/// - `Complex`: any value except `0.0 + 0.0i` and `NaN + NaNi`
/// - `Bool`: `true`
/// - `Unit`: never true; always false
/// - `List`: lists with at least one element; element(s) does not have to be truthy
/// - `Range`: ranges with at least one element; element(s) does not have to be truthy
/// - `Function`: always true
pub fn is_truthy(&self) -> bool {
match self {
Value::Float(n) => !n.is_zero(),
Value::Integer(n) => !n.is_zero(),
Value::Complex(c) => !c.is_zero(),
Value::Boolean(b) => *b,
Value::Unit => false,
Value::List(l) => !l.borrow().is_empty(),
Value::Range(lhs, kind, rhs) => {
match kind {
RangeKind::HalfOpen => lhs != rhs,
RangeKind::Closed => true,
}
},
Value::Function(_) => true,
}
}
/// Returns a formatter for the value with the given options.
pub fn fmt(&self, options: FormatOptions) -> ValueFormatter {
ValueFormatter {
value: self,
options,
}
}
}
impl From<f64> for Value {
fn from(n: f64) -> Self {
Value::Float(float(n))
}
}
impl From<Float> for Value {
fn from(n: Float) -> Self {
Value::Float(n)
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Integer(Integer::from(n))
}
}
impl From<Integer> for Value {
fn from(n: Integer) -> Self {
Value::Integer(n)
}
}
impl From<Complex> for Value {
fn from(c: Complex) -> Self {
Value::Complex(c)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl From<()> for Value {
fn from(_: ()) -> Self {
Value::Unit
}
}
impl From<Vec<Value>> for Value {
fn from(values: Vec<Value>) -> Self {
Value::List(Rc::new(RefCell::new(values)))
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.fmt(Default::default()).fmt(f)
}
}