stak_vm/
cons.rs

1use crate::{Error, value::Value, value_inner};
2use core::fmt::{self, Display, Formatter};
3
4/// A tag.
5pub type Tag = u16;
6
7/// An unreachable cons. In other words, it is a "null" pointer but not `null`
8/// in Scheme.
9///
10/// If this value is in a `car` field in a cons, that means the cons is moved
11/// already on garbage collection. See also [`crate::Memory::collect_garbages`].
12pub(crate) const NEVER: Cons = Cons::new(1); // A cons can never point an odd index.
13
14const TAG_SIZE: usize = Tag::BITS as usize;
15const TAG_MASK: u64 = Tag::MAX as u64;
16
17/// A cons.
18#[derive(Clone, Copy, Debug)]
19pub struct Cons(u64);
20
21impl Cons {
22    /// Creates a cons from a memory address on heap.
23    #[inline]
24    pub const fn new(index: u64) -> Self {
25        Self::r#box(index << TAG_SIZE)
26    }
27
28    /// Returns a memory address on heap.
29    #[inline]
30    pub const fn index(self) -> usize {
31        (self.unbox() >> TAG_SIZE) as _
32    }
33
34    /// Returns a tag.
35    #[inline]
36    pub const fn tag(self) -> Tag {
37        (self.unbox() & TAG_MASK) as _
38    }
39
40    /// Sets a tag.
41    #[inline]
42    pub const fn set_tag(self, tag: Tag) -> Self {
43        Self::r#box(self.unbox() & !TAG_MASK | (tag as u64 & TAG_MASK))
44    }
45
46    #[inline]
47    const fn r#box(value: u64) -> Self {
48        Self(value_inner::box_cons(value))
49    }
50
51    #[inline]
52    const fn unbox(self) -> u64 {
53        value_inner::unbox_cons(self.0)
54    }
55
56    #[inline]
57    pub(crate) const fn from_raw(raw: u64) -> Self {
58        Self(raw)
59    }
60
61    #[inline]
62    pub(crate) const fn to_raw(self) -> u64 {
63        self.0
64    }
65}
66
67impl PartialEq for Cons {
68    #[inline]
69    fn eq(&self, other: &Self) -> bool {
70        self.index() == other.index()
71    }
72}
73
74impl Eq for Cons {}
75
76impl TryFrom<Value> for Cons {
77    type Error = Error;
78
79    #[inline]
80    fn try_from(value: Value) -> Result<Self, Self::Error> {
81        value.to_cons().ok_or(Error::ConsExpected)
82    }
83}
84
85impl Display for Cons {
86    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
87        if *self == NEVER {
88            return write!(formatter, "!");
89        }
90
91        write!(formatter, "c{:x}", self.index())?;
92
93        if self.tag() > 0 {
94            write!(formatter, ":{}", self.tag())?;
95        }
96
97        Ok(())
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn tag() {
107        let cons = Cons::new(42);
108
109        assert_eq!(cons.index(), 42);
110        assert_eq!(cons.tag(), 0);
111
112        let cons = cons.set_tag(1);
113
114        assert_eq!(cons.index(), 42);
115        assert_eq!(cons.tag(), 1);
116
117        let cons = cons.set_tag(3);
118
119        assert_eq!(cons.index(), 42);
120        assert_eq!(cons.tag(), 3);
121    }
122
123    #[test]
124    fn reset_tag() {
125        assert_eq!(Cons::new(42).set_tag(2).set_tag(1).tag(), 1);
126    }
127
128    #[test]
129    fn set_too_large_tag() {
130        let cons = Cons::new(0).set_tag(Tag::MAX);
131
132        assert_eq!(cons.index(), 0);
133        assert_eq!(cons.tag(), TAG_MASK as Tag);
134    }
135}