1use crate::ast::{Expr, Stmt};
5use crate::environment::Environment;
6use num_bigint::BigInt;
7use num_rational::Ratio;
8use num_traits::Zero;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::fmt;
12use std::rc::Rc;
13
14#[derive(Debug, Clone)]
16pub enum Value {
17 Number(f64),
19
20 Fraction(Ratio<BigInt>),
22
23 String(String),
25
26 Boolean(bool),
28
29 Null,
31
32 Array(Vec<Value>),
34
35 Dict(HashMap<String, Value>),
37
38 Function {
40 name: Option<String>,
41 params: Vec<String>,
42 body: Vec<Stmt>,
43 env: Rc<RefCell<Environment>>,
44 },
45
46 Generator {
48 params: Vec<String>,
49 body: Vec<Stmt>,
50 env: Rc<RefCell<Environment>>,
51 state: GeneratorState,
52 },
53
54 Lazy {
56 expr: Expr,
57 env: Rc<RefCell<Environment>>,
58 cached: Option<Box<Value>>,
59 },
60
61 BuiltIn { name: String, arity: usize },
63}
64
65#[derive(Debug, Clone)]
67pub enum GeneratorState {
68 NotStarted,
70
71 Running { position: usize },
73
74 Done,
76}
77
78impl Value {
79 pub fn is_truthy(&self) -> bool {
81 match self {
82 Value::Boolean(b) => *b,
83 Value::Null => false,
84 Value::Number(n) => *n != 0.0,
85 Value::Fraction(f) => !f.is_zero(),
86 Value::String(s) => !s.is_empty(),
87 Value::Array(arr) => !arr.is_empty(),
88 Value::Dict(dict) => !dict.is_empty(),
89 _ => true,
90 }
91 }
92
93 pub fn type_name(&self) -> &'static str {
95 match self {
96 Value::Number(_) => "Number",
97 Value::Fraction(_) => "Fraction",
98 Value::String(_) => "String",
99 Value::Boolean(_) => "Boolean",
100 Value::Null => "Null",
101 Value::Array(_) => "Array",
102 Value::Dict(_) => "Dict",
103 Value::Function { .. } => "Function",
104 Value::Generator { .. } => "Generator",
105 Value::Lazy { .. } => "Lazy",
106 Value::BuiltIn { .. } => "BuiltIn",
107 }
108 }
109
110 pub fn to_number(&self) -> Option<f64> {
112 match self {
113 Value::Number(n) => Some(*n),
114 Value::Fraction(f) => Some(
115 f.numer().to_string().parse::<f64>().ok()?
116 / f.denom().to_string().parse::<f64>().ok()?,
117 ),
118 Value::Boolean(true) => Some(1.0),
119 Value::Boolean(false) => Some(0.0),
120 Value::String(s) => s.parse().ok(),
121 _ => None,
122 }
123 }
124
125 #[allow(clippy::inherent_to_string_shadow_display)]
127 pub fn to_string(&self) -> String {
128 match self {
129 Value::Number(n) => {
130 if n.fract() == 0.0 {
132 format!("{:.0}", n)
133 } else {
134 format!("{}", n)
135 }
136 }
137 Value::Fraction(f) => {
138 if f.is_integer() {
139 format!("{}", f.numer())
140 } else {
141 format!("{}/{}", f.numer(), f.denom())
142 }
143 }
144 Value::String(s) => s.clone(),
145 Value::Boolean(b) => b.to_string(),
146 Value::Null => "Null".to_string(),
147 Value::Array(arr) => {
148 let elements: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
149 format!("[{}]", elements.join(", "))
150 }
151 Value::Dict(dict) => {
152 let pairs: Vec<String> = dict
153 .iter()
154 .map(|(k, v)| format!("{}: {}", k, v.to_string()))
155 .collect();
156 format!("{{{}}}", pairs.join(", "))
157 }
158 Value::Function { name, params, .. } => {
159 if let Some(n) = name {
160 format!("<Function {} ({})>", n, params.join(", "))
161 } else {
162 format!("<Function ({})>", params.join(", "))
163 }
164 }
165 Value::Generator { params, .. } => {
166 format!("<Generator ({})>", params.join(", "))
167 }
168 Value::Lazy { .. } => "<Lazy>".to_string(),
169 Value::BuiltIn { name, arity } => {
170 format!("<BuiltIn {} ({} args)>", name, arity)
171 }
172 }
173 }
174
175 pub fn equals(&self, other: &Value) -> bool {
177 match (self, other) {
178 (Value::Number(a), Value::Number(b)) => (a - b).abs() < f64::EPSILON,
179 (Value::Fraction(a), Value::Fraction(b)) => a == b,
180 (Value::String(a), Value::String(b)) => a == b,
181 (Value::Boolean(a), Value::Boolean(b)) => a == b,
182 (Value::Null, Value::Null) => true,
183 (Value::Array(a), Value::Array(b)) => {
184 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.equals(y))
185 }
186 _ => false,
187 }
188 }
189
190 pub fn compare(&self, other: &Value) -> Option<std::cmp::Ordering> {
192 match (self, other) {
193 (Value::Number(a), Value::Number(b)) => a.partial_cmp(b),
194 (Value::Fraction(a), Value::Fraction(b)) => Some(a.cmp(b)),
195 (Value::String(a), Value::String(b)) => Some(a.cmp(b)),
196 (Value::Boolean(a), Value::Boolean(b)) => Some(a.cmp(b)),
197 _ => None,
198 }
199 }
200}
201
202impl fmt::Display for Value {
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 write!(f, "{}", self.to_string())
205 }
206}
207
208impl PartialEq for Value {
209 fn eq(&self, other: &Self) -> bool {
210 self.equals(other)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_value_truthy() {
220 assert!(Value::Boolean(true).is_truthy());
221 assert!(!Value::Boolean(false).is_truthy());
222 assert!(!Value::Null.is_truthy());
223 assert!(Value::Number(1.0).is_truthy());
224 assert!(!Value::Number(0.0).is_truthy());
225 assert!(Value::String("hello".to_string()).is_truthy());
226 assert!(!Value::String("".to_string()).is_truthy());
227 }
228
229 #[test]
230 fn test_value_type_name() {
231 assert_eq!(Value::Number(42.0).type_name(), "Number");
232 assert_eq!(Value::String("test".to_string()).type_name(), "String");
233 assert_eq!(Value::Boolean(true).type_name(), "Boolean");
234 assert_eq!(Value::Null.type_name(), "Null");
235 assert_eq!(Value::Array(vec![]).type_name(), "Array");
236 }
237
238 #[test]
239 fn test_value_to_number() {
240 assert_eq!(Value::Number(42.0).to_number(), Some(42.0));
241 assert_eq!(Value::Boolean(true).to_number(), Some(1.0));
242 assert_eq!(Value::Boolean(false).to_number(), Some(0.0));
243 assert_eq!(Value::String("123".to_string()).to_number(), Some(123.0));
244 assert_eq!(Value::String("abc".to_string()).to_number(), None);
245 assert_eq!(Value::Null.to_number(), None);
246 }
247
248 #[test]
249 fn test_value_to_string() {
250 assert_eq!(Value::Number(42.0).to_string(), "42");
251 #[allow(clippy::approx_constant)]
252 {
253 assert_eq!(Value::Number(3.14).to_string(), "3.14");
254 }
255 assert_eq!(Value::String("hello".to_string()).to_string(), "hello");
256 assert_eq!(Value::Boolean(true).to_string(), "true");
257 assert_eq!(Value::Null.to_string(), "Null");
258 assert_eq!(
259 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]).to_string(),
260 "[1, 2]"
261 );
262 }
263
264 #[test]
265 fn test_value_equals() {
266 assert!(Value::Number(42.0).equals(&Value::Number(42.0)));
267 assert!(!Value::Number(42.0).equals(&Value::Number(43.0)));
268 assert!(Value::String("test".to_string()).equals(&Value::String("test".to_string())));
269 assert!(Value::Boolean(true).equals(&Value::Boolean(true)));
270 assert!(Value::Null.equals(&Value::Null));
271 }
272
273 #[test]
274 fn test_value_compare() {
275 use std::cmp::Ordering;
276
277 assert_eq!(
278 Value::Number(42.0).compare(&Value::Number(43.0)),
279 Some(Ordering::Less)
280 );
281 assert_eq!(
282 Value::String("a".to_string()).compare(&Value::String("b".to_string())),
283 Some(Ordering::Less)
284 );
285 assert_eq!(
286 Value::Boolean(false).compare(&Value::Boolean(true)),
287 Some(Ordering::Less)
288 );
289 }
290
291 #[test]
292 fn test_array_equality() {
293 let arr1 = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
294 let arr2 = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
295 let arr3 = Value::Array(vec![Value::Number(1.0), Value::Number(3.0)]);
296
297 assert!(arr1.equals(&arr2));
298 assert!(!arr1.equals(&arr3));
299 }
300}