Skip to main content

bock_interp/
value.rs

1//! Runtime value representation for the Bock interpreter.
2//!
3//! Every Bock type maps to a [`Value`] variant. Collections that require
4//! ordering ([`BTreeMap`], [`BTreeSet`]) are supported via [`OrdF64`], a
5//! total-order wrapper for `f64`.
6
7use std::collections::{BTreeMap, BTreeSet};
8use std::fmt;
9use std::hash::{Hash, Hasher};
10use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
11use std::sync::{Arc, Mutex};
12
13use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
14use tokio::sync::Mutex as AsyncMutex;
15use tokio::task::JoinHandle;
16
17use crate::error::RuntimeError;
18
19// ─── ChannelHandle ───────────────────────────────────────────────────────────
20
21/// Shared state for an unbounded MPSC channel of [`Value`]s.
22///
23/// The `Channel[T]` runtime type holds both a sender and a receiver. Both
24/// ends of the channel share a single `Arc<ChannelHandle>`, so cloning or
25/// passing a channel around does not duplicate the underlying queue.
26///
27/// `Channel.new()` returns a pair of clones of the same handle so that
28/// `send` and `recv` both operate on the same underlying mpsc.
29#[derive(Debug)]
30pub struct ChannelHandle {
31    /// The sending end; clones share the same underlying producer.
32    pub sender: UnboundedSender<Value>,
33    /// The receiving end behind an async mutex so a single consumer at a time
34    /// can `.recv().await`.
35    pub receiver: AsyncMutex<UnboundedReceiver<Value>>,
36}
37
38impl ChannelHandle {
39    /// Create a new unbounded channel handle.
40    #[must_use]
41    pub fn new() -> Arc<Self> {
42        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
43        Arc::new(ChannelHandle {
44            sender: tx,
45            receiver: AsyncMutex::new(rx),
46        })
47    }
48}
49
50// ─── OrdF64 ───────────────────────────────────────────────────────────────────
51
52/// A total-order wrapper for `f64`.
53///
54/// Uses [`f64::total_cmp`] (stable since Rust 1.62) which defines:
55/// `-NaN < -Inf < … < -0.0 < +0.0 < … < +Inf < +NaN`.
56///
57/// This is necessary because [`BTreeMap`] and [`BTreeSet`] require [`Ord`],
58/// but raw `f64` only implements [`PartialOrd`].
59#[derive(Debug, Clone, Copy)]
60pub struct OrdF64(pub f64);
61
62impl PartialEq for OrdF64 {
63    fn eq(&self, other: &Self) -> bool {
64        self.0.total_cmp(&other.0) == std::cmp::Ordering::Equal
65    }
66}
67
68impl Eq for OrdF64 {}
69
70impl PartialOrd for OrdF64 {
71    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
72        Some(self.cmp(other))
73    }
74}
75
76impl Ord for OrdF64 {
77    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
78        self.0.total_cmp(&other.0)
79    }
80}
81
82impl Hash for OrdF64 {
83    fn hash<H: Hasher>(&self, state: &mut H) {
84        // Consistent with total_cmp equality: equal values share the same bit pattern.
85        self.0.to_bits().hash(state);
86    }
87}
88
89impl fmt::Display for OrdF64 {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "{}", self.0)
92    }
93}
94
95impl From<f64> for OrdF64 {
96    fn from(v: f64) -> Self {
97        OrdF64(v)
98    }
99}
100
101// ─── BockString ──────────────────────────────────────────────────────────────
102
103/// A Bock string value.
104#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
105pub struct BockString(String);
106
107impl BockString {
108    /// Create an [`BockString`] from any string-like value.
109    #[must_use]
110    pub fn new(s: impl Into<String>) -> Self {
111        BockString(s.into())
112    }
113
114    /// View as a `str` slice.
115    #[must_use]
116    pub fn as_str(&self) -> &str {
117        &self.0
118    }
119}
120
121impl fmt::Display for BockString {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(f, "{}", self.0)
124    }
125}
126
127impl From<String> for BockString {
128    fn from(s: String) -> Self {
129        BockString(s)
130    }
131}
132
133impl From<&str> for BockString {
134    fn from(s: &str) -> Self {
135        BockString(s.to_owned())
136    }
137}
138
139// ─── RecordValue ─────────────────────────────────────────────────────────────
140
141/// A record (struct) value: a named type with named fields.
142#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
143pub struct RecordValue {
144    /// The record's type name.
145    pub type_name: String,
146    /// Fields stored in sorted key order for deterministic comparison.
147    pub fields: BTreeMap<String, Value>,
148}
149
150// ─── EnumValue ───────────────────────────────────────────────────────────────
151
152/// An enum (sum type) value: a named variant with an optional payload.
153#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
154pub struct EnumValue {
155    /// The enum type name.
156    pub type_name: String,
157    /// The variant name.
158    pub variant: String,
159    /// Optional payload for variants that carry data.
160    pub payload: Option<Box<Value>>,
161}
162
163// ─── FnValue ─────────────────────────────────────────────────────────────────
164
165/// Global unique-ID counter for function instances.
166static NEXT_FN_ID: AtomicU64 = AtomicU64::new(1);
167
168/// A function value.
169///
170/// Functions are equal only to themselves (compared by [`id`](FnValue::id)).
171/// Attempting to **order** function values — e.g. by using one as a map key or
172/// set element — is a **runtime error** and will panic with a clear message.
173#[derive(Debug, Clone)]
174pub struct FnValue {
175    /// Unique identifier assigned at creation.
176    pub id: u64,
177    /// Optional human-readable name, for display.
178    pub name: Option<String>,
179}
180
181impl FnValue {
182    /// Create an anonymous function value with a fresh unique identity.
183    #[must_use]
184    pub fn new_anonymous() -> Self {
185        FnValue {
186            id: NEXT_FN_ID.fetch_add(1, AtomicOrdering::Relaxed),
187            name: None,
188        }
189    }
190
191    /// Create a named function value with a fresh unique identity.
192    #[must_use]
193    pub fn new_named(name: impl Into<String>) -> Self {
194        FnValue {
195            id: NEXT_FN_ID.fetch_add(1, AtomicOrdering::Relaxed),
196            name: Some(name.into()),
197        }
198    }
199}
200
201impl PartialEq for FnValue {
202    /// Functions are equal iff they share the same identity.
203    fn eq(&self, other: &Self) -> bool {
204        self.id == other.id
205    }
206}
207
208impl Eq for FnValue {}
209
210impl Hash for FnValue {
211    fn hash<H: Hasher>(&self, state: &mut H) {
212        self.id.hash(state);
213    }
214}
215
216impl fmt::Display for FnValue {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        match &self.name {
219            Some(n) => write!(f, "<fn {n}>"),
220            None => write!(f, "<fn #{}>", self.id),
221        }
222    }
223}
224
225// ─── IteratorValue ────────────────────────────────────────────────────────────
226
227/// Global unique-ID counter for iterator instances.
228static NEXT_ITER_ID: AtomicU64 = AtomicU64::new(1);
229
230/// The internal state of a lazy iterator.
231///
232/// Each variant wraps a source iterator (or raw data) and computes values
233/// on demand via [`IteratorKind::next`]. Combinators like `map` and `filter`
234/// require interpreter support to invoke Bock closures — these store the
235/// function value but their `next()` returns a special
236/// `IteratorNext::NeedsCallback` signal.
237#[derive(Debug)]
238pub enum IteratorKind {
239    /// Iterate over a list of values.
240    List { items: Vec<Value>, pos: usize },
241    /// Iterate over an integer range.
242    Range {
243        current: i64,
244        end: i64,
245        inclusive: bool,
246        step: i64,
247    },
248    /// Iterate over set elements.
249    Set { items: Vec<Value>, pos: usize },
250    /// Iterate over map entries as (key, value) tuples.
251    MapEntries {
252        items: Vec<(Value, Value)>,
253        pos: usize,
254    },
255    /// Lazy map combinator — requires interpreter callback.
256    Map {
257        source: Arc<Mutex<IteratorKind>>,
258        func: FnValue,
259    },
260    /// Lazy filter combinator — requires interpreter callback.
261    Filter {
262        source: Arc<Mutex<IteratorKind>>,
263        pred: FnValue,
264    },
265    /// Take at most N elements.
266    Take {
267        source: Arc<Mutex<IteratorKind>>,
268        remaining: usize,
269    },
270    /// Skip the first N elements.
271    Skip {
272        source: Arc<Mutex<IteratorKind>>,
273        to_skip: usize,
274        skipped: bool,
275    },
276    /// Attach an index to each element.
277    Enumerate {
278        source: Arc<Mutex<IteratorKind>>,
279        index: usize,
280    },
281    /// Zip two iterators together.
282    Zip {
283        a: Arc<Mutex<IteratorKind>>,
284        b: Arc<Mutex<IteratorKind>>,
285    },
286    /// Chain two iterators sequentially.
287    Chain {
288        a: Arc<Mutex<IteratorKind>>,
289        b: Arc<Mutex<IteratorKind>>,
290        first_done: bool,
291    },
292}
293
294/// Result of calling [`IteratorKind::next`].
295///
296/// Most combinators can compute the next value directly, but `map` and `filter`
297/// need the interpreter to invoke a Bock closure. They return `NeedsCallback`
298/// with the source value and the function to call.
299#[derive(Debug)]
300pub enum IteratorNext {
301    /// A value was produced.
302    Some(Value),
303    /// The iterator is exhausted.
304    Done,
305    /// The combinator needs the interpreter to call `func(value)` and feed the
306    /// result back. Used by `Map`.
307    NeedsMapCallback { value: Value, func: FnValue },
308    /// The combinator needs the interpreter to call `pred(value)` and feed
309    /// back whether to keep this element. Used by `Filter`.
310    NeedsFilterCallback { value: Value, func: FnValue },
311}
312
313impl IteratorKind {
314    /// Advance the iterator and return the next value.
315    ///
316    /// For combinators that need Bock function calls (map, filter), this
317    /// returns [`IteratorNext::NeedsMapCallback`] or
318    /// [`IteratorNext::NeedsFilterCallback`] instead of a plain value.
319    #[allow(clippy::should_implement_trait)]
320    pub fn next(&mut self) -> IteratorNext {
321        match self {
322            IteratorKind::List { items, pos } => {
323                if *pos < items.len() {
324                    let val = items[*pos].clone();
325                    *pos += 1;
326                    IteratorNext::Some(val)
327                } else {
328                    IteratorNext::Done
329                }
330            }
331            IteratorKind::Range {
332                current,
333                end,
334                inclusive,
335                step,
336            } => {
337                let in_bounds = if *step > 0 {
338                    if *inclusive {
339                        *current <= *end
340                    } else {
341                        *current < *end
342                    }
343                } else if *step < 0 {
344                    if *inclusive {
345                        *current >= *end
346                    } else {
347                        *current > *end
348                    }
349                } else {
350                    false // zero step = no progress
351                };
352                if in_bounds {
353                    let val = *current;
354                    *current += *step;
355                    IteratorNext::Some(Value::Int(val))
356                } else {
357                    IteratorNext::Done
358                }
359            }
360            IteratorKind::Set { items, pos } => {
361                if *pos < items.len() {
362                    let val = items[*pos].clone();
363                    *pos += 1;
364                    IteratorNext::Some(val)
365                } else {
366                    IteratorNext::Done
367                }
368            }
369            IteratorKind::MapEntries { items, pos } => {
370                if *pos < items.len() {
371                    let (k, v) = items[*pos].clone();
372                    *pos += 1;
373                    IteratorNext::Some(Value::Tuple(vec![k, v]))
374                } else {
375                    IteratorNext::Done
376                }
377            }
378            IteratorKind::Map { source, func } => {
379                let mut src = source.lock().unwrap();
380                match src.next() {
381                    IteratorNext::Some(val) => IteratorNext::NeedsMapCallback {
382                        value: val,
383                        func: func.clone(),
384                    },
385                    IteratorNext::Done => IteratorNext::Done,
386                    // Propagate upstream callback requests
387                    other => other,
388                }
389            }
390            IteratorKind::Filter { source, pred } => {
391                let mut src = source.lock().unwrap();
392                match src.next() {
393                    IteratorNext::Some(val) => IteratorNext::NeedsFilterCallback {
394                        value: val,
395                        func: pred.clone(),
396                    },
397                    IteratorNext::Done => IteratorNext::Done,
398                    other => other,
399                }
400            }
401            IteratorKind::Take { source, remaining } => {
402                if *remaining == 0 {
403                    return IteratorNext::Done;
404                }
405                *remaining -= 1;
406                source.lock().unwrap().next()
407            }
408            IteratorKind::Skip {
409                source,
410                to_skip,
411                skipped,
412            } => {
413                if !*skipped {
414                    *skipped = true;
415                    let mut src = source.lock().unwrap();
416                    for _ in 0..*to_skip {
417                        match src.next() {
418                            IteratorNext::Done => return IteratorNext::Done,
419                            IteratorNext::Some(_) => {}
420                            other => return other,
421                        }
422                    }
423                }
424                source.lock().unwrap().next()
425            }
426            IteratorKind::Enumerate { source, index } => {
427                let mut src = source.lock().unwrap();
428                match src.next() {
429                    IteratorNext::Some(val) => {
430                        let idx = *index;
431                        *index += 1;
432                        IteratorNext::Some(Value::Tuple(vec![Value::Int(idx as i64), val]))
433                    }
434                    other => other,
435                }
436            }
437            IteratorKind::Zip { a, b } => {
438                let next_a = a.lock().unwrap().next();
439                match next_a {
440                    IteratorNext::Some(va) => {
441                        let next_b = b.lock().unwrap().next();
442                        match next_b {
443                            IteratorNext::Some(vb) => {
444                                IteratorNext::Some(Value::Tuple(vec![va, vb]))
445                            }
446                            IteratorNext::Done => IteratorNext::Done,
447                            other => other,
448                        }
449                    }
450                    IteratorNext::Done => IteratorNext::Done,
451                    other => other,
452                }
453            }
454            IteratorKind::Chain { a, b, first_done } => {
455                if !*first_done {
456                    let result = a.lock().unwrap().next();
457                    match result {
458                        IteratorNext::Done => {
459                            *first_done = true;
460                            b.lock().unwrap().next()
461                        }
462                        other => other,
463                    }
464                } else {
465                    b.lock().unwrap().next()
466                }
467            }
468        }
469    }
470}
471
472/// A lazy iterator value.
473///
474/// Iterators are identity-compared (like functions). Ordering is a runtime error.
475/// Interior mutability via [`Mutex`] allows `next()` to advance the state
476/// through shared references and across tasks.
477#[derive(Debug, Clone)]
478pub struct IteratorValue {
479    /// Unique identity for equality comparison.
480    pub id: u64,
481    /// The iterator state.
482    pub kind: Arc<Mutex<IteratorKind>>,
483}
484
485impl IteratorValue {
486    /// Create a new iterator value wrapping the given kind.
487    #[must_use]
488    pub fn new(kind: IteratorKind) -> Self {
489        IteratorValue {
490            id: NEXT_ITER_ID.fetch_add(1, AtomicOrdering::Relaxed),
491            kind: Arc::new(Mutex::new(kind)),
492        }
493    }
494}
495
496impl PartialEq for IteratorValue {
497    fn eq(&self, other: &Self) -> bool {
498        self.id == other.id
499    }
500}
501
502impl Eq for IteratorValue {}
503
504impl Hash for IteratorValue {
505    fn hash<H: Hasher>(&self, state: &mut H) {
506        self.id.hash(state);
507    }
508}
509
510impl fmt::Display for IteratorValue {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        write!(f, "<iterator #{}>", self.id)
513    }
514}
515
516// ─── Value ───────────────────────────────────────────────────────────────────
517
518/// A runtime value in the Bock interpreter.
519///
520/// Every Bock type maps to a variant. Collections requiring total order
521/// ([`Map`](Value::Map), [`Set`](Value::Set)) work because [`Value`] implements
522/// [`Ord`] — with the single exception that ordering [`Function`](Value::Function)
523/// values is a runtime panic.
524#[derive(Debug, Clone)]
525pub enum Value {
526    /// 64-bit signed integer.
527    Int(i64),
528    /// 64-bit float with total ordering.
529    Float(OrdF64),
530    /// Boolean.
531    Bool(bool),
532    /// Unicode string.
533    String(BockString),
534    /// Unicode scalar character.
535    Char(char),
536    /// Unit / void.
537    Void,
538    /// Homogeneous list.
539    List(Vec<Value>),
540    /// Ordered key-value map (keys must not be functions).
541    Map(BTreeMap<Value, Value>),
542    /// Ordered set (elements must not be functions).
543    Set(BTreeSet<Value>),
544    /// Fixed-size heterogeneous tuple.
545    Tuple(Vec<Value>),
546    /// Record (struct) value.
547    Record(RecordValue),
548    /// Enum (sum type) value.
549    Enum(EnumValue),
550    /// Function value — equal only to itself; ordering is a runtime error.
551    Function(FnValue),
552    /// Optional value (`Some(v)` or `None`).
553    Optional(Option<Box<Value>>),
554    /// Result value (`Ok(v)` or `Err(e)`).
555    Result(std::result::Result<Box<Value>, Box<Value>>),
556    /// An integer range with a step (produced by `lo..hi` / `lo..=hi` / `.step()`).
557    Range {
558        start: i64,
559        end: i64,
560        inclusive: bool,
561        step: i64,
562    },
563    /// A lazy iterator value.
564    Iterator(IteratorValue),
565    /// Mutable string builder for efficient concatenation.
566    StringBuilder(Arc<Mutex<String>>),
567    /// A pending async computation.
568    ///
569    /// Created when an `async fn` is called; resolved by `await`.
570    /// The `JoinHandle` lives behind an `Arc<Mutex<Option<...>>>` so the
571    /// Future value can be cloned (handles share the same task) and the
572    /// handle can be `take()`-n on first await.
573    Future(FutureHandle),
574    /// A time duration as signed nanoseconds (±292 year range).
575    Duration(i64),
576    /// A monotonic point in time (per-process).
577    Instant(std::time::Instant),
578    /// An unbounded async channel. `Channel.new()` returns a tuple of two
579    /// clones of the same handle; `send` and `recv` both operate on the
580    /// shared mpsc queue.
581    Channel(Arc<ChannelHandle>),
582}
583
584/// Shared handle to a spawned async task. See [`Value::Future`].
585pub type FutureHandle = Arc<Mutex<Option<JoinHandle<Result<Value, RuntimeError>>>>>;
586
587impl Value {
588    /// Build the prelude `Ordering` enum value (`Less`/`Equal`/`Greater`)
589    /// from a [`std::cmp::Ordering`].
590    ///
591    /// `Comparable.compare` returns `Ordering`, and the five codegen targets
592    /// lower a primitive `.compare()` to a value that `match`es against the
593    /// `Less`/`Equal`/`Greater` variants. The interpreter — the cross-target
594    /// equivalence oracle — must produce the same enum value so a
595    /// `match (a).compare(b) { Less => … }` dispatches identically under
596    /// `bock run`/`bock test` (Q-interp-compare-ordering).
597    #[must_use]
598    pub fn ordering(ord: std::cmp::Ordering) -> Self {
599        let variant = match ord {
600            std::cmp::Ordering::Less => "Less",
601            std::cmp::Ordering::Equal => "Equal",
602            std::cmp::Ordering::Greater => "Greater",
603        };
604        Value::Enum(EnumValue {
605            type_name: "Ordering".to_string(),
606            variant: variant.to_string(),
607            payload: None,
608        })
609    }
610}
611
612impl PartialEq for Value {
613    fn eq(&self, other: &Self) -> bool {
614        match (self, other) {
615            (Value::Int(a), Value::Int(b)) => a == b,
616            // IEEE 754 partial equality at the `==` boundary
617            // (Q-interp-float-ieee-equality, DV24): `NaN == NaN` is `false` on
618            // all five codegen targets, so the interpreter — the equivalence
619            // oracle — compares the raw `f64`s rather than the total-order
620            // `OrdF64` wrapper (whose `PartialEq` treats `NaN` as equal to
621            // itself). This propagates structurally: a record/list/tuple holding
622            // a `NaN` `Float` field is `!=` an identically-built one, matching
623            // the targets' field-wise IEEE equality (see the `eq_record_float_nan`
624            // fixture). The `OrdF64` total order is retained for `Value`'s `Ord`
625            // impl, which is what `BTreeMap`/`BTreeSet` keys use internally — the
626            // boundary between IEEE `==` and total-order containers is split here.
627            (Value::Float(a), Value::Float(b)) => a.0 == b.0,
628            (Value::Bool(a), Value::Bool(b)) => a == b,
629            (Value::String(a), Value::String(b)) => a == b,
630            (Value::Char(a), Value::Char(b)) => a == b,
631            (Value::Void, Value::Void) => true,
632            (Value::List(a), Value::List(b)) => a == b,
633            (Value::Map(a), Value::Map(b)) => a == b,
634            (Value::Set(a), Value::Set(b)) => a == b,
635            (Value::Tuple(a), Value::Tuple(b)) => a == b,
636            (Value::Record(a), Value::Record(b)) => a == b,
637            (Value::Enum(a), Value::Enum(b)) => a == b,
638            (Value::Function(a), Value::Function(b)) => a == b,
639            (Value::Optional(a), Value::Optional(b)) => a == b,
640            (Value::Result(a), Value::Result(b)) => match (a, b) {
641                (Ok(av), Ok(bv)) => av == bv,
642                (Err(ae), Err(be)) => ae == be,
643                _ => false,
644            },
645            (
646                Value::Range {
647                    start: s1,
648                    end: e1,
649                    inclusive: i1,
650                    step: st1,
651                },
652                Value::Range {
653                    start: s2,
654                    end: e2,
655                    inclusive: i2,
656                    step: st2,
657                },
658            ) => s1 == s2 && e1 == e2 && i1 == i2 && st1 == st2,
659            (Value::Iterator(a), Value::Iterator(b)) => a == b,
660            (Value::StringBuilder(a), Value::StringBuilder(b)) => Arc::ptr_eq(a, b),
661            (Value::Future(a), Value::Future(b)) => Arc::ptr_eq(a, b),
662            (Value::Duration(a), Value::Duration(b)) => a == b,
663            (Value::Instant(a), Value::Instant(b)) => a == b,
664            (Value::Channel(a), Value::Channel(b)) => Arc::ptr_eq(a, b),
665            _ => false,
666        }
667    }
668}
669
670impl Eq for Value {}
671
672/// Numeric discriminant used for cross-variant ordering.
673fn variant_ord(v: &Value) -> u8 {
674    match v {
675        Value::Void => 0,
676        Value::Bool(_) => 1,
677        Value::Int(_) => 2,
678        Value::Float(_) => 3,
679        Value::Char(_) => 4,
680        Value::String(_) => 5,
681        Value::Tuple(_) => 6,
682        Value::List(_) => 7,
683        Value::Set(_) => 8,
684        Value::Map(_) => 9,
685        Value::Record(_) => 10,
686        Value::Enum(_) => 11,
687        Value::Optional(_) => 12,
688        Value::Result(_) => 13,
689        Value::Function(_) => 14,
690        Value::Range { .. } => 15,
691        Value::Iterator(_) => 16,
692        Value::StringBuilder(_) => 17,
693        Value::Future(_) => 18,
694        Value::Duration(_) => 19,
695        Value::Instant(_) => 20,
696        Value::Channel(_) => 21,
697    }
698}
699
700impl Ord for Value {
701    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
702        use std::cmp::Ordering;
703
704        match (self, other) {
705            (Value::Void, Value::Void) => Ordering::Equal,
706            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
707            (Value::Int(a), Value::Int(b)) => a.cmp(b),
708            (Value::Float(a), Value::Float(b)) => a.cmp(b),
709            (Value::Char(a), Value::Char(b)) => a.cmp(b),
710            (Value::String(a), Value::String(b)) => a.cmp(b),
711            (Value::Tuple(a), Value::Tuple(b)) => a.cmp(b),
712            (Value::List(a), Value::List(b)) => a.cmp(b),
713            (Value::Set(a), Value::Set(b)) => a.cmp(b),
714            (Value::Map(a), Value::Map(b)) => a.cmp(b),
715            (Value::Record(a), Value::Record(b)) => a.cmp(b),
716            (Value::Enum(a), Value::Enum(b)) => a.cmp(b),
717            (Value::Optional(a), Value::Optional(b)) => a.cmp(b),
718            (Value::Result(a), Value::Result(b)) => match (a, b) {
719                (Ok(av), Ok(bv)) => av.cmp(bv),
720                (Err(ae), Err(be)) => ae.cmp(be),
721                (Ok(_), Err(_)) => Ordering::Less,
722                (Err(_), Ok(_)) => Ordering::Greater,
723            },
724            (
725                Value::Range {
726                    start: s1,
727                    end: e1,
728                    inclusive: i1,
729                    step: st1,
730                },
731                Value::Range {
732                    start: s2,
733                    end: e2,
734                    inclusive: i2,
735                    step: st2,
736                },
737            ) => (s1, e1, i1, st1).cmp(&(s2, e2, i2, st2)),
738            // SAFETY: Ord requires a total ordering but these types have no meaningful
739            // order. Reaching these arms indicates a program logic error (e.g. using
740            // functions as map keys). We use unreachable!() to signal the invariant.
741            (Value::Function(_), Value::Function(_)) => {
742                unreachable!("function values are not orderable and cannot be used as map keys or set elements")
743            }
744            (Value::Iterator(_), Value::Iterator(_)) => {
745                unreachable!("iterator values are not orderable and cannot be used as map keys or set elements")
746            }
747            (Value::StringBuilder(_), Value::StringBuilder(_)) => {
748                unreachable!("StringBuilder values are not orderable")
749            }
750            (Value::Future(_), Value::Future(_)) => {
751                unreachable!("Future values are not orderable")
752            }
753            (Value::Channel(_), Value::Channel(_)) => {
754                unreachable!("Channel values are not orderable")
755            }
756            (Value::Duration(a), Value::Duration(b)) => a.cmp(b),
757            (Value::Instant(a), Value::Instant(b)) => a.cmp(b),
758            // Different variants: order by discriminant.
759            _ => variant_ord(self).cmp(&variant_ord(other)),
760        }
761    }
762}
763
764impl PartialOrd for Value {
765    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
766        Some(self.cmp(other))
767    }
768}
769
770impl Hash for Value {
771    fn hash<H: Hasher>(&self, state: &mut H) {
772        variant_ord(self).hash(state);
773        match self {
774            Value::Int(v) => v.hash(state),
775            Value::Float(v) => v.hash(state),
776            Value::Bool(v) => v.hash(state),
777            Value::String(v) => v.hash(state),
778            Value::Char(v) => v.hash(state),
779            Value::Void => {}
780            Value::List(v) => v.hash(state),
781            Value::Tuple(v) => v.hash(state),
782            Value::Record(v) => v.hash(state),
783            Value::Enum(v) => v.hash(state),
784            Value::Function(v) => v.hash(state),
785            Value::Optional(v) => v.hash(state),
786            // BTreeSet/BTreeMap iterate in sorted order — hashing is deterministic.
787            Value::Set(v) => {
788                for item in v {
789                    item.hash(state);
790                }
791            }
792            Value::Map(v) => {
793                for (k, val) in v {
794                    k.hash(state);
795                    val.hash(state);
796                }
797            }
798            Value::Result(v) => match v {
799                Ok(inner) => {
800                    0u8.hash(state);
801                    inner.hash(state);
802                }
803                Err(inner) => {
804                    1u8.hash(state);
805                    inner.hash(state);
806                }
807            },
808            Value::Range {
809                start,
810                end,
811                inclusive,
812                step,
813            } => {
814                start.hash(state);
815                end.hash(state);
816                inclusive.hash(state);
817                step.hash(state);
818            }
819            Value::Iterator(v) => v.hash(state),
820            Value::StringBuilder(v) => v.lock().unwrap().hash(state),
821            Value::Future(v) => (Arc::as_ptr(v) as usize).hash(state),
822            Value::Duration(v) => v.hash(state),
823            Value::Instant(v) => v.hash(state),
824            Value::Channel(v) => (Arc::as_ptr(v) as usize).hash(state),
825        }
826    }
827}
828
829impl fmt::Display for Value {
830    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831        match self {
832            Value::Int(v) => write!(f, "{v}"),
833            Value::Float(v) => write!(f, "{v}"),
834            Value::Bool(true) => write!(f, "true"),
835            Value::Bool(false) => write!(f, "false"),
836            Value::String(v) => write!(f, "{v}"),
837            Value::Char(v) => write!(f, "'{v}'"),
838            Value::Void => write!(f, "void"),
839            Value::List(items) => {
840                write!(f, "[")?;
841                for (i, item) in items.iter().enumerate() {
842                    if i > 0 {
843                        write!(f, ", ")?;
844                    }
845                    write!(f, "{item}")?;
846                }
847                write!(f, "]")
848            }
849            Value::Set(items) => {
850                write!(f, "{{")?;
851                for (i, item) in items.iter().enumerate() {
852                    if i > 0 {
853                        write!(f, ", ")?;
854                    }
855                    write!(f, "{item}")?;
856                }
857                write!(f, "}}")
858            }
859            Value::Map(items) => {
860                write!(f, "{{")?;
861                for (i, (k, v)) in items.iter().enumerate() {
862                    if i > 0 {
863                        write!(f, ", ")?;
864                    }
865                    write!(f, "{k}: {v}")?;
866                }
867                write!(f, "}}")
868            }
869            Value::Tuple(items) => {
870                write!(f, "(")?;
871                for (i, item) in items.iter().enumerate() {
872                    if i > 0 {
873                        write!(f, ", ")?;
874                    }
875                    write!(f, "{item}")?;
876                }
877                write!(f, ")")
878            }
879            Value::Record(r) => {
880                write!(f, "{} {{", r.type_name)?;
881                for (i, (k, v)) in r.fields.iter().enumerate() {
882                    if i > 0 {
883                        write!(f, ", ")?;
884                    }
885                    write!(f, "{k}: {v}")?;
886                }
887                write!(f, "}}")
888            }
889            Value::Enum(e) => {
890                write!(f, "{}.{}", e.type_name, e.variant)?;
891                if let Some(payload) = &e.payload {
892                    write!(f, "({payload})")?;
893                }
894                Ok(())
895            }
896            Value::Function(fn_val) => write!(f, "{fn_val}"),
897            Value::Optional(Some(v)) => write!(f, "Some({v})"),
898            Value::Optional(None) => write!(f, "None"),
899            Value::Result(Ok(v)) => write!(f, "Ok({v})"),
900            Value::Result(Err(e)) => write!(f, "Err({e})"),
901            Value::Range {
902                start,
903                end,
904                inclusive,
905                step,
906            } => {
907                if *step == 1 {
908                    if *inclusive {
909                        write!(f, "{start}..={end}")
910                    } else {
911                        write!(f, "{start}..{end}")
912                    }
913                } else if *inclusive {
914                    write!(f, "{start}..={end} step {step}")
915                } else {
916                    write!(f, "{start}..{end} step {step}")
917                }
918            }
919            Value::Iterator(v) => write!(f, "{v}"),
920            Value::StringBuilder(v) => write!(f, "<StringBuilder len={}>", v.lock().unwrap().len()),
921            Value::Future(_) => write!(f, "<future>"),
922            Value::Duration(nanos) => write!(f, "{}", format_duration(*nanos)),
923            Value::Instant(_) => write!(f, "<instant>"),
924            Value::Channel(_) => write!(f, "<channel>"),
925        }
926    }
927}
928
929/// Format a duration in nanoseconds using the most natural unit.
930fn format_duration(nanos: i64) -> String {
931    if nanos == 0 {
932        return "0s".to_string();
933    }
934    let sign = if nanos < 0 { "-" } else { "" };
935    let abs_nanos = nanos.unsigned_abs();
936    if abs_nanos >= 1_000_000_000 {
937        let secs = abs_nanos as f64 / 1_000_000_000.0;
938        format!("{sign}{secs}s")
939    } else if abs_nanos >= 1_000_000 {
940        let ms = abs_nanos as f64 / 1_000_000.0;
941        format!("{sign}{ms}ms")
942    } else if abs_nanos >= 1_000 {
943        let us = abs_nanos as f64 / 1_000.0;
944        format!("{sign}{us}µs")
945    } else {
946        format!("{sign}{abs_nanos}ns")
947    }
948}
949
950// ─── Tests ────────────────────────────────────────────────────────────────────
951
952#[cfg(test)]
953mod tests {
954    use super::*;
955
956    // ── OrdF64 ────────────────────────────────────────────────────────────────
957
958    #[test]
959    fn ordf64_total_order_neg_zero_vs_pos_zero() {
960        let neg = OrdF64(-0.0_f64);
961        let pos = OrdF64(0.0_f64);
962        assert!(neg < pos, "-0.0 should be less than +0.0 under total order");
963    }
964
965    #[test]
966    fn ordf64_nan_is_ordered() {
967        let nan = OrdF64(f64::NAN);
968        let inf = OrdF64(f64::INFINITY);
969        assert!(inf < nan, "+Inf should be less than +NaN under total order");
970    }
971
972    #[test]
973    fn ordf64_equality_uses_total_cmp() {
974        assert_ne!(OrdF64(-0.0), OrdF64(0.0));
975        assert_eq!(OrdF64(1.0), OrdF64(1.0));
976    }
977
978    // ── BockString ────────────────────────────────────────────────────────────
979
980    #[test]
981    fn bock_string_ord() {
982        let a = BockString::new("apple");
983        let b = BockString::new("banana");
984        assert!(a < b);
985    }
986
987    #[test]
988    fn bock_string_display() {
989        let s = BockString::new("hello");
990        assert_eq!(s.to_string(), "hello");
991    }
992
993    // ── Value equality ────────────────────────────────────────────────────────
994
995    #[test]
996    fn int_equality() {
997        assert_eq!(Value::Int(42), Value::Int(42));
998        assert_ne!(Value::Int(1), Value::Int(2));
999    }
1000
1001    #[test]
1002    fn float_equality_is_ieee() {
1003        // `Value`'s `==` uses IEEE 754 partial semantics at the boundary
1004        // (Q-interp-float-ieee-equality): equal magnitudes compare equal,
1005        // `-0.0 == 0.0` is `true`, and `NaN == NaN` is `false`. The total order
1006        // lives in `Value`'s `Ord` impl (see `value_as_btreeset_element` and
1007        // `ordf64_total_order_neg_zero_vs_pos_zero`), not here.
1008        assert_eq!(Value::Float(OrdF64(1.5)), Value::Float(OrdF64(1.5)));
1009        // IEEE: signed zeros are equal under `==`.
1010        assert_eq!(Value::Float(OrdF64(-0.0)), Value::Float(OrdF64(0.0)));
1011        // IEEE: NaN is never equal to anything, including itself.
1012        assert_ne!(
1013            Value::Float(OrdF64(f64::NAN)),
1014            Value::Float(OrdF64(f64::NAN))
1015        );
1016        assert_ne!(Value::Float(OrdF64(1.5)), Value::Float(OrdF64(f64::NAN)));
1017    }
1018
1019    #[test]
1020    fn bool_equality() {
1021        assert_eq!(Value::Bool(true), Value::Bool(true));
1022        assert_ne!(Value::Bool(true), Value::Bool(false));
1023    }
1024
1025    #[test]
1026    fn string_equality() {
1027        assert_eq!(
1028            Value::String(BockString::new("hi")),
1029            Value::String(BockString::new("hi"))
1030        );
1031    }
1032
1033    #[test]
1034    fn void_equality() {
1035        assert_eq!(Value::Void, Value::Void);
1036    }
1037
1038    #[test]
1039    fn different_variants_not_equal() {
1040        assert_ne!(Value::Int(0), Value::Bool(false));
1041    }
1042
1043    // ── Function identity ─────────────────────────────────────────────────────
1044
1045    #[test]
1046    fn fn_equality_by_identity() {
1047        let f1 = FnValue::new_named("foo");
1048        let f2 = FnValue::new_named("foo");
1049        assert_eq!(f1, f1.clone());
1050        assert_ne!(f1, f2);
1051    }
1052
1053    #[test]
1054    fn fn_value_equality_by_identity() {
1055        let f1 = FnValue::new_anonymous();
1056        let f2 = FnValue::new_anonymous();
1057        let v1 = Value::Function(f1.clone());
1058        let v1_clone = Value::Function(f1);
1059        let v2 = Value::Function(f2);
1060        assert_eq!(v1, v1_clone);
1061        assert_ne!(v1_clone, v2);
1062    }
1063
1064    #[test]
1065    #[should_panic(expected = "function values are not orderable")]
1066    fn fn_value_ordering_panics() {
1067        let f1 = Value::Function(FnValue::new_anonymous());
1068        let f2 = Value::Function(FnValue::new_anonymous());
1069        let _ = f1.cmp(&f2);
1070    }
1071
1072    // ── Value ordering ────────────────────────────────────────────────────────
1073
1074    #[test]
1075    fn int_ordering() {
1076        assert!(Value::Int(1) < Value::Int(2));
1077        assert!(Value::Int(2) > Value::Int(1));
1078    }
1079
1080    #[test]
1081    fn bool_ordering() {
1082        assert!(Value::Bool(false) < Value::Bool(true));
1083    }
1084
1085    #[test]
1086    fn optional_none_less_than_some() {
1087        assert!(Value::Optional(None) < Value::Optional(Some(Box::new(Value::Int(0)))));
1088    }
1089
1090    #[test]
1091    fn result_ok_less_than_err() {
1092        let ok = Value::Result(Ok(Box::new(Value::Int(0))));
1093        let err = Value::Result(Err(Box::new(Value::Int(0))));
1094        assert!(ok < err);
1095    }
1096
1097    #[test]
1098    fn cross_variant_ordering_by_discriminant() {
1099        // Void (0) < Bool (1) < Int (2)
1100        assert!(Value::Void < Value::Bool(false));
1101        assert!(Value::Bool(false) < Value::Int(0));
1102    }
1103
1104    // ── Value in BTreeMap / BTreeSet ─────────────────────────────────────────
1105
1106    #[test]
1107    fn value_as_btreemap_key() {
1108        let mut map = BTreeMap::new();
1109        map.insert(Value::Int(1), Value::String(BockString::new("one")));
1110        map.insert(Value::Int(2), Value::String(BockString::new("two")));
1111        assert_eq!(
1112            map.get(&Value::Int(1)),
1113            Some(&Value::String(BockString::new("one")))
1114        );
1115    }
1116
1117    #[test]
1118    fn value_as_btreeset_element() {
1119        let mut set = BTreeSet::new();
1120        set.insert(Value::Int(3));
1121        set.insert(Value::Int(1));
1122        set.insert(Value::Int(2));
1123        let sorted: Vec<_> = set.iter().collect();
1124        assert_eq!(sorted[0], &Value::Int(1));
1125        assert_eq!(sorted[2], &Value::Int(3));
1126    }
1127
1128    #[test]
1129    fn float_as_btreeset_element() {
1130        let mut set = BTreeSet::new();
1131        set.insert(Value::Float(OrdF64(3.0)));
1132        set.insert(Value::Float(OrdF64(1.0)));
1133        set.insert(Value::Float(OrdF64(2.0)));
1134        let mut iter = set.iter();
1135        assert_eq!(iter.next(), Some(&Value::Float(OrdF64(1.0))));
1136    }
1137
1138    // ── Display ───────────────────────────────────────────────────────────────
1139
1140    #[test]
1141    fn display_primitives() {
1142        assert_eq!(Value::Int(42).to_string(), "42");
1143        assert_eq!(Value::Float(OrdF64(3.5)).to_string(), "3.5");
1144        assert_eq!(Value::Bool(true).to_string(), "true");
1145        assert_eq!(Value::Bool(false).to_string(), "false");
1146        assert_eq!(Value::String(BockString::new("hi")).to_string(), "hi");
1147        assert_eq!(Value::Char('x').to_string(), "'x'");
1148        assert_eq!(Value::Void.to_string(), "void");
1149    }
1150
1151    #[test]
1152    fn display_list() {
1153        let v = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
1154        assert_eq!(v.to_string(), "[1, 2, 3]");
1155    }
1156
1157    #[test]
1158    fn display_tuple() {
1159        let v = Value::Tuple(vec![Value::Int(1), Value::Bool(true)]);
1160        assert_eq!(v.to_string(), "(1, true)");
1161    }
1162
1163    #[test]
1164    fn display_optional() {
1165        assert_eq!(
1166            Value::Optional(Some(Box::new(Value::Int(5)))).to_string(),
1167            "Some(5)"
1168        );
1169        assert_eq!(Value::Optional(None).to_string(), "None");
1170    }
1171
1172    #[test]
1173    fn display_result() {
1174        assert_eq!(
1175            Value::Result(Ok(Box::new(Value::Int(0)))).to_string(),
1176            "Ok(0)"
1177        );
1178        assert_eq!(
1179            Value::Result(Err(Box::new(Value::String(BockString::new("fail"))))).to_string(),
1180            "Err(fail)"
1181        );
1182    }
1183
1184    #[test]
1185    fn display_enum_without_payload() {
1186        let v = Value::Enum(EnumValue {
1187            type_name: "Color".into(),
1188            variant: "Red".into(),
1189            payload: None,
1190        });
1191        assert_eq!(v.to_string(), "Color.Red");
1192    }
1193
1194    #[test]
1195    fn display_enum_with_payload() {
1196        let v = Value::Enum(EnumValue {
1197            type_name: "Shape".into(),
1198            variant: "Circle".into(),
1199            payload: Some(Box::new(Value::Float(OrdF64(1.0)))),
1200        });
1201        assert_eq!(v.to_string(), "Shape.Circle(1)");
1202    }
1203
1204    #[test]
1205    fn display_record() {
1206        let mut fields = BTreeMap::new();
1207        fields.insert("x".to_string(), Value::Int(1));
1208        fields.insert("y".to_string(), Value::Int(2));
1209        let v = Value::Record(RecordValue {
1210            type_name: "Point".into(),
1211            fields,
1212        });
1213        assert_eq!(v.to_string(), "Point {x: 1, y: 2}");
1214    }
1215
1216    #[test]
1217    fn display_function_named() {
1218        let v = Value::Function(FnValue::new_named("add"));
1219        assert_eq!(v.to_string(), "<fn add>");
1220    }
1221
1222    // ── Clone ─────────────────────────────────────────────────────────────────
1223
1224    #[test]
1225    fn value_clone() {
1226        let original = Value::List(vec![Value::Int(1), Value::Bool(true)]);
1227        let cloned = original.clone();
1228        assert_eq!(original, cloned);
1229    }
1230
1231    // ── Nested values ─────────────────────────────────────────────────────────
1232
1233    #[test]
1234    fn nested_map_value() {
1235        let inner = Value::Map(BTreeMap::from([(Value::Int(1), Value::Bool(true))]));
1236        let outer = Value::List(vec![inner]);
1237        assert_eq!(outer.to_string(), "[{1: true}]");
1238    }
1239}