Skip to main content

logicaffeine_data/
opt.rs

1//! A value or `nothing`: what a compiled program keeps in a place the VM can store `nothing` in.
2//!
3//! The VM holds any value in any place. A compiled tier gives each place one type for its whole life, so a place a
4//! program can store `nothing` in — a binding, an item, a field, a parameter, a result — holds a [`LogosOpt`]: the
5//! value the place would hold anyway, or `nothing`. Reading it where a value is needed stops the program in the
6//! words the VM stops with for that operation on `nothing`, which the compiler passes in.
7
8/// A value of `T`, or `nothing`.
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10pub struct LogosOpt<T>(pub Option<T>);
11
12impl<T> LogosOpt<T> {
13    /// `nothing`.
14    pub const NOTHING: Self = LogosOpt(None);
15
16    /// Whether it holds `nothing`.
17    #[inline]
18    pub fn is_nothing(&self) -> bool {
19        self.0.is_none()
20    }
21
22    /// The value it holds. Holding `nothing`, the program stops with `failure`.
23    #[inline]
24    pub fn held(&self, failure: &str) -> &T {
25        match &self.0 {
26            Some(value) => value,
27            None => stopped(failure),
28        }
29    }
30
31    /// The value it holds, to change in place. Holding `nothing`, the program stops with `failure`.
32    #[inline]
33    pub fn held_mut(&mut self, failure: &str) -> &mut T {
34        match &mut self.0 {
35            Some(value) => value,
36            None => stopped(failure),
37        }
38    }
39
40    /// The value it holds, taken. Holding `nothing`, the program stops with `failure`.
41    #[inline]
42    pub fn into_held(self, failure: &str) -> T {
43        match self.0 {
44            Some(value) => value,
45            None => stopped(failure),
46        }
47    }
48
49    /// The values two operands hold, for an operation on both, each evaluated before either is read. Either holding
50    /// `nothing`, the program stops with `failures`: the words for the left one alone, the right one alone, and both.
51    #[inline]
52    pub fn pair<U>(left: LogosOpt<T>, right: LogosOpt<U>, failures: [&str; 3]) -> (T, U) {
53        match (left.0, right.0) {
54            (Some(l), Some(r)) => (l, r),
55            (None, Some(_)) => stopped(failures[0]),
56            (Some(_), None) => stopped(failures[1]),
57            (None, None) => stopped(failures[2]),
58        }
59    }
60
61    /// Whether `self` and `other` are one value: both `nothing`, or two values `same` finds equal.
62    #[inline]
63    pub fn equals_with<U>(&self, other: &LogosOpt<U>, same: impl FnOnce(&T, &U) -> bool) -> bool {
64        match (&self.0, &other.0) {
65            (Some(a), Some(b)) => same(a, b),
66            (None, None) => true,
67            _ => false,
68        }
69    }
70}
71
72/// A place starts holding its type's own first value, as the VM's does.
73impl<T: Default> Default for LogosOpt<T> {
74    #[inline]
75    fn default() -> Self {
76        LogosOpt(Some(T::default()))
77    }
78}
79
80impl<T> From<T> for LogosOpt<T> {
81    #[inline]
82    fn from(value: T) -> Self {
83        LogosOpt(Some(value))
84    }
85}
86
87/// `nothing` is false where a condition reads it, and a value is as true as it is.
88impl<T: crate::ops::Truthy> crate::ops::Truthy for LogosOpt<T> {
89    #[inline]
90    fn truthy(&self) -> bool {
91        self.0.as_ref().is_some_and(crate::ops::Truthy::truthy)
92    }
93}
94
95#[cold]
96#[inline(never)]
97fn stopped(failure: &str) -> ! {
98    panic!("{failure}")
99}
100
101#[cfg(test)]
102#[path = "../../logicaffeine_tests/unit/logicaffeine_data/opt/tests.rs"]
103mod tests;