swan_core/value.rs
1//! Runtime values of the Swan language.
2//!
3//! The central type is [`Value`], which represents every first-class Swan
4//! value: scalars (`Nothing`, `Bool`, `Numb`, `Text`), collections (`List`,
5//! `Namespace`, `Tuple`), callables (`Func`, `Builtin`), asynchronous results
6//! (`Promise`), and the non-throwing error value `Undefined`.
7//!
8//! # Tuples
9//!
10//! Every `Value` is conceptually a tuple. `Nothing` is the empty tuple, any
11//! non-`Tuple` value is a one-element tuple, and `Value::Tuple` is a tuple of
12//! two or more items. [`Value::items`], [`Value::pair`], [`Value::map`] and
13//! [`Value::normalize`] all work with this view. Tuples are always flat and
14//! never contain `Nothing` once normalized.
15//!
16//! # Submodules
17//!
18//! - [`Scope`]: lexical environment, also used as the storage of `Namespace`.
19//! - [`Undef`]: description of why an operation was undefined.
20//! - [`BuiltinFn`]: wrapper around a native Rust function.
21//! - [`Promise`]: single-assignment asynchronous value.
22//!
23//! # Comparison and conversion
24//!
25//! `Value` implements `PartialEq` and `PartialOrd` by hand (see the impl docs)
26//! and converts to `bool` through `From<Value> for bool`.
27
28use std::rc::Rc;
29use std::cell::RefCell;
30use std::collections::HashMap;
31use std::cmp::Ordering;
32use std::fmt;
33use crate::ast::Node;
34
35// Lexical scopes and namespaces
36mod scope;
37pub use scope::Scope;
38
39// Explanations of why an operation is undefined
40mod undef;
41pub use undef::Undef;
42
43// Native Rust functions callable from Swan
44mod func;
45pub use func::BuiltinFn;
46
47// Single-assignment asynchronous values
48mod promise;
49pub use promise::Promise;
50
51
52
53// ----------------------------------------------------------------------------
54// Value (every first-class Swan value)
55// ----------------------------------------------------------------------------
56
57/// Any first-class Swan value.
58///
59/// Cloning is cheap: heap-backed variants (`Text`, `List`, `Tuple`, `Name`)
60/// use `Rc`, and `Scope`, `Promise` and `BuiltinFn` are handles that share
61/// state when cloned.
62///
63/// The Swan type name of each variant (as returned by the `type` built-in) is
64/// the variant name, except that `Builtin` reports `Func`.
65///
66/// # Examples
67///
68/// ```
69/// use swan_core::value::Value;
70///
71/// let t = Value::tuple(vec![Value::Numb(1.0), Value::Nothing, Value::Numb(2.0)]);
72/// assert_eq!(t.items().count(), 2);
73/// assert!(bool::from(t));
74/// ```
75#[derive(Debug, Clone)]
76pub enum Value {
77
78 /// The empty tuple `()`. Also the result of failed lookups such as out-of-range list indexing.
79 Nothing,
80
81 /// A boolean, `TRUE` or `FALSE`.
82 Bool(bool),
83
84 /// A 64-bit floating-point number.
85 Numb(f64),
86
87 /// An immutable UTF-8 string.
88 Text(Rc<str>),
89
90 /// An ordered, integer-indexed sequence — `[a, b, c]`.
91 List(Rc<Vec<Value>>),
92
93 /// A first-class namespace — `{ x: 1, y: 2 }`.
94 ///
95 /// Backed by a [`Scope`]. Applying a namespace to a `Text` key performs an
96 /// own-only lookup (parent scopes are not consulted) and yields `Nothing`
97 /// if the key is absent. Equality compares bindings (and parents) by value.
98 Namespace(Scope),
99
100 /// A user-defined function.
101 Func {
102 /// Parameter pattern: a `Name` or a tuple of names.
103 params: Box<Value>,
104 /// Unevaluated body expression.
105 body: Box<Node>,
106 /// Scope captured at definition time.
107 closure: Scope,
108 },
109
110 /// A native Rust function callable from Swan.
111 Builtin(BuiltinFn),
112
113 /// A promise. It resolves at most once and never rejects; an `Undefined` result resolves to `Nothing`.
114 Promise(Promise),
115
116 /// The result of an invalid operation, such as `"a" - 1`.
117 ///
118 /// Swan expressions do not throw exceptions; they return `Undefined`
119 /// values instead. The [`Undef`] payload describes what went wrong.
120 Undefined(Undef),
121
122 /// The Swan product type — a heterogeneous sequence of items produced by
123 /// the comma operator `a, b, c`. A Tuple of zero items is `Nothing`.
124 /// A Tuple of exactly one item normalises to that item.
125 /// Construct with [`Value::tuple`] so the result is normalized.
126 Tuple(Rc<Vec<Value>>),
127
128 /// An identifier in a binding position: the left side of `=`, `:` or
129 /// `->`, or the operand of `$`. It is produced only while evaluating those
130 /// positions and has no meaning as a runtime result.
131 Name(Rc<str>),
132}
133
134
135impl Value {
136
137 /// Builds `Undefined` for unary operation `op` applied to `val`; `pos` is the source byte offset.
138 #[inline]
139 pub fn undef_unary(op: &'static str, val: Value, pos: usize) -> Value {
140 Value::Undefined(Undef::UnaryOperation {
141 op,
142 val: Box::new(val),
143 pos
144 })
145 }
146
147 /// Builds `Undefined` for binary operation `op` applied to `lhv` and `rhv`; `pos` is the source byte offset.
148 #[inline]
149 pub fn undef_binary(op: &'static str, lhv: Value, rhv: Value, pos: usize) -> Value {
150 Value::Undefined(Undef::BinaryOperation {
151 op,
152 lhv: Box::new(lhv),
153 rhv: Box::new(rhv),
154 pos
155 })
156 }
157
158 /// Builds a `List` from `items`.
159 #[inline]
160 pub fn list(items: Vec<Value>) -> Value {
161 Value::List(Rc::new(items))
162 }
163
164 /// Builds a normalized tuple from `items`.
165 ///
166 /// Zero items give `Nothing`. One item gives that item. Nested tuples are
167 /// flattened and `Nothing` items are dropped (see [`Value::normalize`]).
168 #[inline]
169 pub fn tuple(items: Vec<Value>) -> Value {
170 match items.len() {
171 0 => Value::Nothing,
172 _ => Value::Tuple(Rc::new(items)).normalize(),
173 }
174 }
175}
176
177
178
179// ----------------------------------------------------------------------------
180// Implement equality and order for Value
181// ----------------------------------------------------------------------------
182
183/// Structural equality.
184///
185/// Values of different variants are never equal. `Promise`s are equal only
186/// if they are the same promise (pointer identity). `Func`, `Builtin` and
187/// `Name` are never equal to anything, including themselves. `Undefined`
188/// values compare by operation and operands, ignoring source position.
189impl PartialEq for Value {
190 fn eq(&self, other: &Self) -> bool {
191 match (self, other) {
192 (Value::Bool(a), Value::Bool(b)) => a == b,
193 (Value::Numb(a), Value::Numb(b)) => a == b,
194 (Value::Text(a), Value::Text(b)) => a == b,
195 (Value::List(a), Value::List(b)) => a == b,
196 (Value::Tuple(a), Value::Tuple(b)) => a == b,
197 (Value::Namespace(a), Value::Namespace(b)) => a == b,
198 (Value::Undefined(a), Value::Undefined(b)) => a == b,
199 (Value::Promise(a), Value::Promise(b)) => Rc::ptr_eq(&a.0, &b.0),
200 (Value::Nothing, Value::Nothing) => true,
201 // Every other pair is never equal
202 _ => false,
203 }
204 }
205}
206
207/// Partial ordering between values.
208///
209/// Only values of the same variant are comparable (`Bool`, `Numb`, `Text`,
210/// `List`, `Tuple`); lists and tuples compare lexicographically, with the
211/// shorter sequence ordering first. `Nothing` equals `Nothing` and orders
212/// before every other value. Every other pair returns `None`
213/// (e.g. `NaN`, mixed types, `Func`, `Builtin`, `Namespace`, `Promise`, `Undefined`).
214impl PartialOrd for Value {
215 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216 match (self, other) {
217 (Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
218 (Value::Numb(a), Value::Numb(b)) => a.partial_cmp(b),
219 (Value::Text(a), Value::Text(b)) => a.partial_cmp(b),
220 (Value::List(a), Value::List(b)) => compare_iterators(a.iter(), b.iter()),
221 (Value::Tuple(a), Value::Tuple(b)) => compare_iterators(a.iter(), b.iter()),
222 // Nothing is equal to Nothing and less that anything else.
223 (Value::Nothing, Value::Nothing) => Some(Ordering::Equal),
224 (Value::Nothing, _) => Some(Ordering::Less),
225 (_, Value::Nothing) => Some(Ordering::Greater),
226
227 // All other pairs are unordered.
228 _ => None,
229 }
230 }
231}
232
233/// Helper function to perform lexicographical comparison between item iterators.
234/// Returns `None` as soon as two corresponding items are unordered.
235fn compare_iterators<'a>(
236 mut it1: impl Iterator<Item = &'a Value>,
237 mut it2: impl Iterator<Item = &'a Value>,
238) -> Option<Ordering> {
239 loop {
240 match (it1.next(), it2.next()) {
241 (None, None) => return Some(Ordering::Equal),
242 (None, Some(_)) => return Some(Ordering::Less), // Shorter tuple/list is Less
243 (Some(_), None) => return Some(Ordering::Greater), // Longer tuple/list is Greater
244 (Some(a), Some(b)) => match a.partial_cmp(b) {
245 Some(Ordering::Equal) => continue,
246 non_eq => return non_eq,
247 },
248 }
249 }
250}
251
252
253
254// ----------------------------------------------------------------------------
255// ValueIter (generic "tuple item" iterator over any Value)
256//
257// Every Value in Swan is conceptually a Tuple: `Nothing` is the empty tuple,
258// any non-Tuple Item behaves like a 1-element tuple, and `Value::Tuple`
259// behaves like the sequence of its elements. `ValueIter` implements this
260// view uniformly, mirroring `Term.items()` in the JS reference.
261//
262// Nested tuples are flattened (defensively — well-formed values should never
263// contain a Tuple element after normalisation, but the iterator handles it
264// anyway) and `Nothing` items are skipped, so `(1, (), 2)` yields `1, 2`,
265// not `1, Nothing, 2`.
266// ----------------------------------------------------------------------------
267
268/// An iterator over the items of a value seen as a tuple.
269///
270/// Created by [`Value::items`]. Nested tuples are flattened and `Nothing`
271/// items are skipped.
272#[derive(Debug, Clone)]
273pub struct ValueIter {
274 /// Stack of (tuple, next_index) frames; the last entry is the tuple
275 /// currently being consumed. Nested tuples push a new frame; exhausted
276 /// frames are popped.
277 stack: Vec<(Rc<Vec<Value>>, usize)>,
278}
279
280impl ValueIter {
281 /// Creates an iterator over `value` viewed as a tuple: `Nothing` is empty, a `Tuple` yields its items, and any other value yields itself once.
282 fn new(value: Value) -> Self {
283 match value {
284 Value::Nothing => ValueIter { stack: Vec::new() },
285 Value::Tuple(v) => ValueIter { stack: vec![(v, 0)] },
286 other => ValueIter { stack: vec![(Rc::new(vec![other]), 0)] },
287 }
288 }
289}
290
291impl Iterator for ValueIter {
292 type Item = Value;
293
294 fn next(&mut self) -> Option<Value> {
295 loop {
296 let (items, idx) = self.stack.last_mut()?;
297
298 if *idx >= items.len() {
299 self.stack.pop();
300 continue;
301 }
302
303 let item = items[*idx].clone();
304 *idx += 1;
305
306 match item {
307 Value::Nothing => continue, // skip Nothing items
308 Value::Tuple(v) => self.stack.push((v, 0)), // flatten
309 other => return Some(other),
310 }
311 }
312 }
313}
314
315impl std::iter::FusedIterator for ValueIter {}
316
317
318
319// ----------------------------------------------------------------------------
320// Pairs (generic "tuple item pair" iterator over two Values)
321//
322// Yields corresponding pairs of items from two values (each treated as a
323// tuple), mirroring `Term.iterPairs(other)` in the JS reference.
324//
325// When the two tuples have different lengths, the missing items from the
326// shorter tuple are replaced with `Value::Nothing`.
327// ----------------------------------------------------------------------------
328
329/// An iterator over the corresponding items of two values seen as tuples.
330///
331/// Created by [`Value::pair`] or [`Pairs::new`]. When one value has fewer
332/// items, the missing items are [`Value::Nothing`].
333#[derive(Debug, Clone)]
334pub struct Pairs {
335 iter1: ValueIter,
336 iter2: ValueIter,
337}
338
339impl Pairs {
340 /// Creates a new `Pairs` iterator from two values.
341 pub fn new(first: &Value, second: &Value) -> Self {
342 Self {
343 iter1: first.items(),
344 iter2: second.items(),
345 }
346 }
347}
348
349impl Iterator for Pairs {
350 type Item = (Value, Value);
351
352 fn next(&mut self) -> Option<Self::Item> {
353 let item1 = self.iter1.next();
354 let item2 = self.iter2.next();
355
356 match (item1, item2) {
357 (None, None) => None,
358 (Some(v1), Some(v2)) => Some((v1, v2)),
359 (Some(v1), None) => Some((v1, Value::Nothing)),
360 (None, Some(v2)) => Some((Value::Nothing, v2)),
361 }
362 }
363}
364
365impl std::iter::FusedIterator for Pairs {}
366
367
368
369// ----------------------------------------------------------------------------
370// Value tuple methods
371// ----------------------------------------------------------------------------
372
373impl Value {
374 /// Returns an iterator over this value's items, treating it as a tuple.
375 ///
376 /// - `Nothing` yields no items.
377 /// - `Tuple` yields its items (recursively flattened, `Nothing` skipped).
378 /// - Any other value yields itself as the single item.
379 pub fn items(&self) -> ValueIter {
380 ValueIter::new(self.clone())
381 }
382
383 /// Returns an iterator yielding corresponding pairs of items between
384 /// `self` and `other`, treating each value as a tuple.
385 ///
386 /// If the two values have different lengths, the missing items from the
387 /// shorter tuple are replaced with [`Value::Nothing`].
388 pub fn pair(&self, other: &Value) -> Pairs {
389 Pairs::new(self, other)
390 }
391
392 /// Returns the canonical form of this value.
393 ///
394 /// For a `Tuple`, nested tuples are flattened and `Nothing` items are
395 /// removed. The result is `Nothing` if no items remain, the sole item if
396 /// one remains, and a `Tuple` otherwise. Non-tuple values are returned
397 /// unchanged (cloned).
398 pub fn normalize(&self) -> Self {
399 if matches!(self, Value::Tuple(_)) {
400 let flat_items: Vec<_> = self.items().collect();
401 match flat_items.len() {
402 0 => Value::Nothing,
403 1 => flat_items[0].clone(),
404 _ => Value::Tuple(Rc::new(flat_items)),
405 }
406 } else {
407 self.clone()
408 }
409 }
410
411 /// Maps a function `f: Value -> Value` over this `Value`:
412 /// - `Value::Nothing` -> `Value::Nothing`
413 /// - `Value::Tuple` -> `Value::Tuple` containing `f(v1), f(v2), ..., f(vn)`
414 /// - Any other value -> `f(self)`
415 /// The result of mapping over a tuple is normalized, so items that map to `Nothing` are dropped.
416 pub fn map(&self, f: impl Fn(Value) -> Value + 'static) -> Value {
417 match self {
418 Value::Nothing => Value::Nothing,
419 Value::Tuple(items) => {
420 let mapped_items: Vec<Value> = items
421 .iter()
422 .map(|item| f(item.clone()))
423 .collect();
424 Value::tuple(mapped_items)
425 }
426 _ => f(self.clone()),
427 }
428 }
429}
430
431
432
433// ----------------------------------------------------------------------------
434// Value type casting
435// ----------------------------------------------------------------------------
436
437/// Truthiness of a value.
438///
439/// - `Nothing` and `Undefined` are false.
440/// - `Bool` is itself; `Numb` is false only for `0`; `Text` and `List` are
441/// true when non-empty; `Namespace` is true when it has at least one binding.
442/// - `Func` and `Builtin` are always true.
443/// - A `Promise` is true once it is resolved (whatever its value) and false
444/// while it is pending.
445/// - A `Name` has no boolean meaning and is false.
446/// - A `Tuple` is true if at least one item is true.
447impl From<Value> for bool {
448 fn from(val: Value) -> Self {
449 match val {
450 Value::Nothing => false,
451 Value::Bool(b) => b,
452 Value::Numb(n) => n != 0.0,
453 Value::Text(t) => !t.is_empty(),
454 Value::List(v) => !v.is_empty(),
455 Value::Func{..} => true,
456 Value::Builtin(_) => true,
457 Value::Undefined(_) => false,
458 Value::Namespace(s) => s.0.borrow().bindings.len() > 0,
459 Value::Tuple(_) => {
460 // Returns true if AT LEAST ONE item in the tuple converts to true.
461 // If ALL items map to false, `.any()` returns false.
462 val.items().any(|item| bool::from(item))
463 }
464 Value::Promise(p) => p.0.borrow().value.is_some(),
465 Value::Name(_) => false,
466 }
467 }
468}
469
470
471
472// ============================================================================
473// Tests
474// ============================================================================
475
476#[cfg(test)]
477mod tuple_tests {
478 use super::*;
479 // Tests for tuple iteration, pairing and normalization.
480
481 #[test]
482 fn test_pairs_same_length() {
483 let t1 = Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0), Value::Numb(3.0)]);
484 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0), Value::Numb(30.0)]);
485
486 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
487 assert_eq!(pairs.len(), 3);
488 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Numb(10.0)));
489 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Numb(20.0)));
490 assert_eq!(pairs[2], (Value::Numb(3.0), Value::Numb(30.0)));
491 }
492
493 #[test]
494 fn test_pairs_first_longer() {
495 let t1 = Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0), Value::Numb(3.0)]);
496 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0)]);
497
498 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
499 assert_eq!(pairs.len(), 3);
500 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Numb(10.0)));
501 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Numb(20.0)));
502 assert_eq!(pairs[2], (Value::Numb(3.0), Value::Nothing));
503 }
504
505 #[test]
506 fn test_pairs_second_longer() {
507 let t1 = Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0)]);
508 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0), Value::Numb(30.0)]);
509
510 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
511 assert_eq!(pairs.len(), 3);
512 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Numb(10.0)));
513 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Numb(20.0)));
514 assert_eq!(pairs[2], (Value::Nothing, Value::Numb(30.0)));
515 }
516
517 #[test]
518 fn test_pairs_both_empty() {
519 let t1 = Value::Nothing;
520 let t2 = Value::Nothing;
521
522 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
523 assert!(pairs.is_empty());
524 }
525
526 #[test]
527 fn test_pairs_first_empty() {
528 let t1 = Value::Nothing;
529 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0)]);
530
531 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
532 assert_eq!(pairs.len(), 2);
533 assert_eq!(pairs[0], (Value::Nothing, Value::Numb(10.0)));
534 assert_eq!(pairs[1], (Value::Nothing, Value::Numb(20.0)));
535 }
536
537 #[test]
538 fn test_pairs_second_empty() {
539 let t1 = Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0)]);
540 let t2 = Value::Nothing;
541
542 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
543 assert_eq!(pairs.len(), 2);
544 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Nothing));
545 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Nothing));
546 }
547
548 #[test]
549 fn test_pairs_scalars() {
550 let v1 = Value::Numb(42.0);
551 let v2 = Value::Text("swan".into());
552
553 let pairs: Vec<(Value, Value)> = v1.pair(&v2).collect();
554 assert_eq!(pairs.len(), 1);
555 assert_eq!(pairs[0], (Value::Numb(42.0), Value::Text("swan".into())));
556 }
557
558 #[test]
559 fn test_pairs_nested_tuples_flattened() {
560 let inner = Value::tuple(vec![Value::Numb(2.0), Value::Numb(3.0)]);
561 let t1 = Value::tuple(vec![Value::Numb(1.0), inner]);
562 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0), Value::Numb(30.0)]);
563
564 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
565 assert_eq!(pairs.len(), 3);
566 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Numb(10.0)));
567 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Numb(20.0)));
568 assert_eq!(pairs[2], (Value::Numb(3.0), Value::Numb(30.0)));
569 }
570
571 #[test]
572 fn test_pairs_skips_nothing_in_tuples() {
573 let t1 = Value::tuple(vec![Value::Numb(1.0), Value::Nothing, Value::Numb(2.0)]);
574 let t2 = Value::tuple(vec![Value::Numb(10.0), Value::Numb(20.0)]);
575
576 let pairs: Vec<(Value, Value)> = t1.pair(&t2).collect();
577 assert_eq!(pairs.len(), 2);
578 assert_eq!(pairs[0], (Value::Numb(1.0), Value::Numb(10.0)));
579 assert_eq!(pairs[1], (Value::Numb(2.0), Value::Numb(20.0)));
580 }
581
582 #[test]
583 fn test_pairs_fused_iterator() {
584 let t1 = Value::Nothing;
585 let t2 = Value::Nothing;
586
587 let mut pairs = t1.pair(&t2);
588 assert_eq!(pairs.next(), None);
589 assert_eq!(pairs.next(), None);
590 assert_eq!(pairs.next(), None);
591 }
592}
593
594
595
596#[cfg(test)]
597mod boolean_conversion_tests {
598 use super::*;
599 // Tests for `From<Value> for bool`.
600
601 #[test]
602 fn test_primitives_to_bool() {
603 assert!(!bool::from(Value::Nothing));
604 assert!(!bool::from(Value::Bool(false)));
605 assert!(bool::from(Value::Bool(true)));
606 assert!(!bool::from(Value::Numb(0.0)));
607 assert!(bool::from(Value::Numb(1.5)));
608 assert!(!bool::from(Value::Text("".into())));
609 assert!(bool::from(Value::Text("hello".into())));
610 assert!(!bool::from(Value::list(vec![])));
611 assert!(bool::from(Value::list(vec![Value::Numb(1.0)])));
612 }
613
614 #[test]
615 fn test_tuple_all_false_maps_to_false() {
616 // A tuple where all items map to false
617 let all_false_tuple = Value::tuple(vec![
618 Value::Bool(false),
619 Value::Numb(0.0),
620 Value::Text("".into()),
621 Value::Nothing,
622 ]);
623
624 assert!(!bool::from(all_false_tuple));
625 }
626
627 #[test]
628 fn test_tuple_at_least_one_true_maps_to_true() {
629 // A tuple where at least one item maps to true
630 let one_true_tuple = Value::tuple(vec![
631 Value::Bool(false),
632 Value::Numb(0.0),
633 Value::Text("truthy".into()),
634 Value::Nothing,
635 ]);
636
637 assert!(bool::from(one_true_tuple));
638 }
639
640 #[test]
641 fn test_nested_tuple_all_false_maps_to_false() {
642 // Nested tuples evaluate elements recursively via items()
643 let inner_false = Value::tuple(vec![Value::Numb(0.0), Value::Bool(false)]);
644 let nested_all_false = Value::tuple(vec![Value::Nothing, inner_false]);
645
646 assert!(!bool::from(nested_all_false));
647 }
648
649 #[test]
650 fn test_promise_is_true_once_resolved() {
651 let p = crate::value::Promise::new();
652 assert!(!bool::from(Value::Promise(p.clone())));
653
654 // Resolved: true, even when the value itself is false
655 p.resolve(Value::Bool(false));
656 assert!(bool::from(Value::Promise(p)));
657
658 let q = crate::value::Promise::new();
659 q.resolve(Value::Nothing);
660 assert!(bool::from(Value::Promise(q)));
661 }
662
663 #[test]
664 fn test_name_maps_to_false() {
665 assert!(!bool::from(Value::Name("x".into())));
666 }
667}