axsys_noun/
cell.rs

1use crate::{atom::Atom, noun::Noun, Rc};
2use std::{
3    collections::hash_map::DefaultHasher,
4    fmt::{Display, Error, Formatter},
5    hash::{Hash, Hasher},
6    mem::MaybeUninit,
7};
8
9pub mod axes {
10    pub fn cap(axis: usize) -> usize {
11        match axis {
12            0 => panic!("axis 0 is invalid"),
13            1 => panic!("axis 1 is invalid"),
14            2 => 2,
15            3 => 3,
16            _ => cap(axis / 2),
17        }
18    }
19
20    pub fn mas(axis: usize) -> usize {
21        match axis {
22            0 | 1 => panic!("axis 0 or 1 is invalid"),
23            2 | 3 => 1,
24            _ => {
25                let recur = mas(axis / 2);
26                (axis % 2) + (recur * 2)
27            }
28        }
29    }
30    pub fn peg(a: usize, b: usize) -> usize {
31        if a == 0 || b == 0 {
32            panic!("axis 0 is invalid");
33        }
34        match b {
35            1 => a,
36            2 => 2 * a,
37            3 => 2 * a + 1,
38            _ => {
39                let recur = peg(a, b / 2);
40                (b % 2) + (recur * 2)
41            }
42        }
43    }
44}
45
46/// A pair of reference-counted nouns.
47///
48/// A cell can be:
49/// - created from an array of atoms, cells, nouns, or types that can easily be converted into
50///   atoms;
51/// - compared to other cells;
52/// - unpacked into an array of nouns;
53/// - pretty-printed;
54/// - converted into a noun.
55///
56/// # Examples
57///
58/// To create a new cell, use one of the `From<[T; N]>` implementations:
59///
60/// ```
61/// # use axsys_noun::{atom::Atom, cell::Cell, Noun, cell};
62/// let cell = Cell::from(["hello", "world"]);
63/// assert_eq!(*cell.head(), Noun::from(Atom::from("hello")));
64/// assert_eq!(*cell.tail(), Noun::from(Atom::from("world")));
65/// ```
66///
67/// ```
68/// # use axsys_noun::{atom::Atom, cell::Cell, Noun, cell};
69/// let cell = Cell::from([0u8, 2u8, 4u8, 8u8]);
70/// assert_eq!(*cell.head(), Noun::from(Atom::from(0u8)));
71/// assert_eq!(*cell.tail(), Noun::from(Cell::from([2u8, 4u8, 8u8])));
72/// ```
73#[derive(Clone, Debug, Eq, Hash, PartialEq)]
74pub struct Cell {
75    head: Rc<Noun>,
76    tail: Rc<Noun>,
77}
78
79impl Cell {
80    /// Constructs a new cell.
81    fn new(head: Rc<Noun>, tail: Rc<Noun>) -> Self {
82        Self { head, tail }
83    }
84
85    /// Returns the head of this cell.
86    pub fn head(&self) -> Rc<Noun> {
87        self.head.clone()
88    }
89
90    /// Returns the head of this cell as a reference.
91    pub fn head_ref(&self) -> &Noun {
92        &self.head
93    }
94
95    /// Returns the tail of this cell.
96    pub fn tail(&self) -> Rc<Noun> {
97        self.tail.clone()
98    }
99
100    /// Returns the tail of this cell as a reference.
101    pub fn tail_ref(&self) -> &Noun {
102        &self.tail
103    }
104
105    /// Computes the hash of this cell.
106    pub fn hash(&self) -> u64 {
107        let mut hasher = DefaultHasher::new();
108        hasher.write_u64((*self.head()).hash());
109        hasher.write_u64((*self.tail()).hash());
110        hasher.finish()
111    }
112
113    /// Unpacks this cell into an array of length `N`, returning `None` if the cell is not of the
114    /// form `[a1 a2 ... aN]`.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// # use axsys_noun::{atom::Atom, cell::Cell, Noun, cell};
120    /// let cell = Cell::from([0u8, 1u8, 2u8, 3u8, 4u8, 5u8]);
121    ///
122    /// let nouns = cell.to_array::<6>().unwrap();
123    /// assert_eq!(*nouns[0], Noun::from(Atom::from(0u8)));
124    /// assert_eq!(*nouns[1], Noun::from(Atom::from(1u8)));
125    /// assert_eq!(*nouns[2], Noun::from(Atom::from(2u8)));
126    /// assert_eq!(*nouns[3], Noun::from(Atom::from(3u8)));
127    /// assert_eq!(*nouns[4], Noun::from(Atom::from(4u8)));
128    /// assert_eq!(*nouns[5], Noun::from(Atom::from(5u8)));
129    /// ```
130    ///
131    /// ```
132    /// # use axsys_noun::{atom::Atom, cell::Cell, cell};
133    /// let cell = Cell::from([0u8, 1u8, 2u8, 3u8]);
134    ///
135    /// assert_eq!(cell.to_array::<6>(), None);
136    /// ```
137    pub fn to_array<const N: usize>(&self) -> Option<[Rc<Noun>; N]> {
138        debug_assert!(N >= 2);
139        // See https://doc.rust-lang.org/nomicon/unchecked-uninit.html.
140        let mut nouns: [MaybeUninit<Rc<Noun>>; N] = unsafe { MaybeUninit::uninit().assume_init() };
141        nouns[0] = MaybeUninit::new(self.head());
142        let mut noun = self.tail();
143        for (i, n) in nouns.iter_mut().enumerate().take(N).skip(1) {
144            match *noun {
145                Noun::Atom(_) if i < N - 1 => return None,
146                Noun::Cell(ref cell) if i < N - 1 => {
147                    *n = MaybeUninit::new(cell.head());
148                    noun = cell.tail();
149                }
150                _ => *n = MaybeUninit::new(noun.clone()),
151            }
152        }
153        // Using `mem::transmute()` here as suggested in the Rustnomicon example linked above results in
154        // compiler error E0512.
155        let nouns = unsafe { nouns.as_ptr().cast::<[Rc<Noun>; N]>().read() };
156        Some(nouns)
157    }
158
159    /// Unpacks this cell into a vector.
160    ///
161    /// If the length of the cell is known at compile-time, use [`to_array()`](Self::to_array()) instead.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # use axsys_noun::{atom::Atom, cell::Cell, Noun, cell};
167    /// let cell = Cell::from([0u8, 1u8, 2u8, 4u8, 8u8, 16u8, 32u8, 64u8, 128u8]);
168    ///
169    /// let nouns = cell.to_vec();
170    /// assert_eq!(nouns.len(), 9);
171    /// assert_eq!(*nouns[0], Noun::from(Atom::from(0u8)));
172    /// assert_eq!(*nouns[1], Noun::from(Atom::from(1u8)));
173    /// assert_eq!(*nouns[2], Noun::from(Atom::from(2u8)));
174    /// assert_eq!(*nouns[3], Noun::from(Atom::from(4u8)));
175    /// assert_eq!(*nouns[4], Noun::from(Atom::from(8u8)));
176    /// assert_eq!(*nouns[5], Noun::from(Atom::from(16u8)));
177    /// assert_eq!(*nouns[6], Noun::from(Atom::from(32u8)));
178    /// assert_eq!(*nouns[7], Noun::from(Atom::from(64u8)));
179    /// assert_eq!(*nouns[8], Noun::from(Atom::from(128u8)));
180    ///
181    /// ```
182    pub fn to_vec(&self) -> Vec<Rc<Noun>> {
183        let mut nouns = Vec::new();
184        nouns.push(self.head());
185        let mut noun = self.tail();
186        while let Noun::Cell(cell) = &*noun {
187            nouns.push(cell.head());
188            noun = cell.tail();
189        }
190        nouns.push(noun);
191        nouns
192    }
193
194    /// Converts this cell into its head and tail, consuming the cell.
195    pub fn into_parts(self) -> (Rc<Noun>, Rc<Noun>) {
196        (self.head, self.tail)
197    }
198
199    pub fn as_noun(&self) -> Noun {
200        Noun::Cell(self.clone())
201    }
202
203    pub fn into_noun(self) -> Noun {
204        Noun::Cell(self)
205    }
206
207    pub fn in_rc(&self) -> Rc<Noun> {
208        Rc::new(self.as_noun())
209    }
210    /// Returns the noun at the given axis, or None if the axis is invalid.
211    ///
212    /// # Examples
213    /// ```
214    /// use axsys_noun::{atom::Atom, cell::Cell, Noun, cell};
215    /// let cell = Cell::from([0u8, 1u8, 2u8]);
216    ///
217    /// assert_eq!(cell.slot(0), None);
218    /// assert_eq!(cell.slot(1), Some(cell.in_rc()));
219    /// assert_eq!(cell.slot(2), Some(cell.head()));
220    /// assert_eq!(cell.slot(3), Some(cell.tail()));
221    /// assert_eq!(cell.slot(6), Some(cell.tail().as_cell().unwrap().head()));
222    /// assert_eq!(cell.slot(7), Some(cell.tail().as_cell().unwrap().tail()));
223    /// ```
224    pub fn slot(&self, axis: usize) -> Option<Rc<Noun>> {
225        match axis {
226            0 => None,
227            1 => Some(Rc::new(self.to_owned().as_noun())),
228            _ => {
229                let nex = axes::cap(axis);
230                let mas = axes::mas(axis);
231                println!("nex: {}", nex);
232                println!("mas: {}", mas);
233                match nex {
234                    2 => {
235                        let res = self.head();
236                        if mas == 1 {
237                            Some(res)
238                        } else {
239                            res.as_cell().and_then(|c| c.slot(mas))
240                        }
241                    }
242                    3 => {
243                        let res = self.tail();
244                        if mas == 1 {
245                            Some(res)
246                        } else {
247                            res.as_cell().and_then(|c| c.slot(mas))
248                        }
249                    }
250                    _ => panic!("axis {} is invalid", axis),
251                }
252            }
253        }
254    }
255}
256
257impl Display for Cell {
258    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
259        // This is unfortunately more complicated than
260        // `write!(f, "[{} {}]", self.head(), self.tail())` to handle the fact that brackets are
261        // left-associative and therefore need not always be printed.
262        write!(f, "[")?;
263        match (&*self.head(), &*self.tail()) {
264            (head, Noun::Atom(tail)) => write!(f, "{} {}", head, tail)?,
265            (head, _) => {
266                write!(f, "{} ", head)?;
267                let mut tail = self.tail();
268                while let Noun::Cell(cell) = &*tail {
269                    write!(f, "{} ", cell.head())?;
270                    tail = cell.tail();
271                }
272                write!(f, "{}", tail)?;
273            }
274        }
275        write!(f, "]")
276    }
277}
278
279/// Create a cell of the form `[a1 a2 ... aN]` from an `N`-element array of [`Rc<Noun>`].
280macro_rules! cell_from_array {
281    ($array:expr) => {{
282        debug_assert!($array.len() >= 2);
283        let (mut remaining, pair) = $array.split_at($array.len() - 2);
284        let mut cell = {
285            let head: &Rc<Noun> = &pair[0];
286            let tail: &Rc<Noun> = &pair[1];
287            Cell::new(head.clone(), tail.clone())
288        };
289        while !remaining.is_empty() {
290            let split = remaining.split_at(remaining.len() - 1);
291            remaining = split.0;
292            let single = split.1;
293            cell = Cell::new(single[0].clone(), Rc::new(Noun::from(cell)));
294        }
295        cell
296    }};
297}
298
299macro_rules! impl_from_array_for_cell {
300    (n = $n:expr) => {
301        impl_from_array_for_cell!([Atom; $n]);
302        impl_from_array_for_cell!([Cell; $n]);
303        impl_from_array_for_cell!([Noun; $n]);
304        impl_from_array_for_cell!([Rc<Noun>; $n]);
305        impl_from_array_for_cell!([&str; $n]);
306        impl_from_array_for_cell!([String; $n]);
307        impl_from_array_for_cell!([u8; $n]);
308        impl_from_array_for_cell!([u16; $n]);
309        impl_from_array_for_cell!([u32; $n]);
310        impl_from_array_for_cell!([u64; $n]);
311        impl_from_array_for_cell!([u128; $n]);
312        impl_from_array_for_cell!([usize; $n]);
313        impl_from_array_for_cell!([Vec<u8>; $n]);
314    };
315    ([Atom; $len:expr]) => {
316        impl From<[Atom; $len]> for Cell {
317            fn from(atoms: [Atom; $len]) -> Self {
318                let atoms = atoms.map(|a| Rc::new(Noun::from(a)));
319                cell_from_array!(atoms)
320            }
321        }
322    };
323    ([Cell; $len:expr]) => {
324        impl From<[Cell; $len]> for Cell {
325            fn from(cells: [Self; $len]) -> Self {
326                let cells = cells.map(|c| Rc::new(Noun::from(c)));
327                cell_from_array!(cells)
328            }
329        }
330    };
331    ([Noun; $len:expr]) => {
332        impl From<[Noun; $len]> for Cell {
333            fn from(nouns: [Noun; $len]) -> Self {
334                let nouns = nouns.map(|n| Rc::new(n));
335                cell_from_array!(nouns)
336            }
337        }
338    };
339    ([Rc<Noun>; $len:expr]) => {
340        impl From<[Rc<Noun>; $len]> for Cell {
341            fn from(nouns: [Rc<Noun>; $len]) -> Self {
342                cell_from_array!(nouns)
343            }
344        }
345    };
346    ([$atom_src:ty; $len:expr]) => {
347        impl From<[$atom_src; $len]> for Cell {
348            fn from(atom_srcs: [$atom_src; $len]) -> Self {
349                let atom_srcs = atom_srcs.map(|a| Rc::new(Noun::from(Atom::from(a))));
350                cell_from_array!(atom_srcs)
351            }
352        }
353    };
354}
355
356impl_from_array_for_cell!(n = 2);
357impl_from_array_for_cell!(n = 3);
358impl_from_array_for_cell!(n = 4);
359impl_from_array_for_cell!(n = 5);
360impl_from_array_for_cell!(n = 6);
361impl_from_array_for_cell!(n = 7);
362impl_from_array_for_cell!(n = 8);
363impl_from_array_for_cell!(n = 9);
364impl_from_array_for_cell!(n = 10);
365impl_from_array_for_cell!(n = 11);
366impl_from_array_for_cell!(n = 12);
367impl_from_array_for_cell!(n = 13);
368impl_from_array_for_cell!(n = 14);
369impl_from_array_for_cell!(n = 15);
370impl_from_array_for_cell!(n = 16);
371impl_from_array_for_cell!(n = 17);
372impl_from_array_for_cell!(n = 18);
373impl_from_array_for_cell!(n = 19);
374impl_from_array_for_cell!(n = 20);
375impl_from_array_for_cell!(n = 21);
376impl_from_array_for_cell!(n = 22);
377impl_from_array_for_cell!(n = 23);
378impl_from_array_for_cell!(n = 24);
379impl_from_array_for_cell!(n = 25);
380impl_from_array_for_cell!(n = 26);
381impl_from_array_for_cell!(n = 27);
382impl_from_array_for_cell!(n = 28);
383impl_from_array_for_cell!(n = 29);
384impl_from_array_for_cell!(n = 30);
385
386impl From<Vec<Rc<Noun>>> for Cell {
387    fn from(nouns: Vec<Rc<Noun>>) -> Self {
388        cell_from_array!(nouns)
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395
396    #[test]
397    fn to_array() {
398        {
399            let cell = Cell::from([
400                Noun::from(Atom::from("request")),
401                Noun::from(Atom::from(0u8)),
402                Noun::from(Atom::from("POST")),
403                Noun::from(Atom::from("http://eth-mainnet.urbit.org:8545")),
404                Noun::from(Cell::from([
405                    Noun::from(Cell::from([
406                        Atom::from("Content-Type"),
407                        Atom::from("application/json"),
408                    ])),
409                    Noun::from(Atom::from(0u8)),
410                ])),
411                Noun::from(Atom::from(0u8)),
412                Noun::from(Atom::from(78u8)),
413                Noun::from(Atom::from(
414                    r#"[{"params":[],"id":"block number","jsonrpc":"2.0","method":"eth_blockNumber"}]"#,
415                )),
416            ]);
417            let [tag, req_num, method, uri, headers, body] = cell.to_array::<6>().expect("as list");
418            if let (Noun::Atom(tag), Noun::Atom(req_num), Noun::Atom(method), Noun::Atom(uri)) =
419                (&*tag, &*req_num, &*method, &*uri)
420            {
421                assert_eq!(tag, "request");
422                assert_eq!(*req_num, 0u8);
423                assert_eq!(method, "POST");
424                assert_eq!(uri, "http://eth-mainnet.urbit.org:8545");
425            } else {
426                panic!("unexpected cell");
427            }
428            if let Noun::Cell(headers) = &*headers {
429                if let Noun::Cell(header) = &*headers.head() {
430                    if let (Noun::Atom(key), Noun::Atom(val)) = (&*header.head(), &*header.tail()) {
431                        assert_eq!(key, "Content-Type");
432                        assert_eq!(val, "application/json");
433                    } else {
434                        panic!("unexpected cell");
435                    }
436                } else {
437                    panic!("unexpected atom");
438                }
439                if let Noun::Atom(null) = &*headers.tail() {
440                    assert_eq!(*null, 0u8);
441                } else {
442                    panic!("unexpected cell");
443                }
444            } else {
445                panic!("unexpected atom");
446            }
447            if let Noun::Cell(body) = &*body {
448                if let Noun::Atom(null) = &*body.head() {
449                    assert_eq!(*null, 0u8);
450                } else {
451                    panic!("unexpected cell");
452                }
453                if let Noun::Cell(body) = &*body.tail() {
454                    if let (Noun::Atom(body_len), Noun::Atom(body)) = (&*body.head(), &*body.tail())
455                    {
456                        assert_eq!(*body_len, 78u8);
457                        assert_eq!(
458                            body,
459                            r#"[{"params":[],"id":"block number","jsonrpc":"2.0","method":"eth_blockNumber"}]"#
460                        );
461                    } else {
462                        panic!("unexpected cell");
463                    }
464                } else {
465                    panic!("unexpected atom");
466                }
467            } else {
468                panic!("unexpected atom");
469            }
470        }
471    }
472
473    #[test]
474    fn from_vec() {
475        {
476            let _0 = Rc::<Noun>::from(Atom::from(0u8));
477            let _2 = Rc::<Noun>::from(Atom::from(2u8));
478            let _8 = Rc::<Noun>::from(Atom::from(8u8));
479            let _32 = Rc::<Noun>::from(Atom::from(32u8));
480            let _128 = Rc::<Noun>::from(Atom::from(128u8));
481            let cell = Cell::from(vec![
482                _0.clone(),
483                _2.clone(),
484                _8.clone(),
485                _32.clone(),
486                _128.clone(),
487            ]);
488
489            let [a, b, c, d, e] = cell.to_array::<5>().expect("cell to array");
490            assert_eq!(a, _0);
491            assert_eq!(b, _2);
492            assert_eq!(c, _8);
493            assert_eq!(d, _32);
494            assert_eq!(e, _128);
495        }
496    }
497}