cuicui_chirp/parser/ast/
list.rs

1//! Iterators over a list of AST nodes.
2//!
3//! The AST is a single large buffer. Some [`Node`]s have a variable size (typically
4//! nodes that contain a list of other nodes).
5//!
6//! [`List`] is an iterator over a list of such variable-size `Node`.
7//!
8//! Other nodes, such as `Import` or `Argument` have a known fixed size, [`SimpleNode`],
9//! This allows `List<T>` to provide more methods, those methods are size-based.
10
11use std::marker::PhantomData;
12
13use super::node::{Argument, Code, IdentOffset, Import, StType};
14use super::node::{ChirpFile, Fn, Method, Spawn, Statement, Template};
15use super::{as_usize, header::Block};
16
17macro_rules! dummy {
18    ($($_:tt)*) => {};
19}
20macro_rules! impl_node {
21    ($($ty:ident : $($header:ident)? |$it:ident| $compute:expr),* $(,)?)=>{
22        $(impl<'a> Node<'a> for $ty<'a> {
23            #[inline] fn len(self) -> u32 { let $it = self; $compute }
24            #[inline] fn first(blocks: &'a [Block]) -> Self { Self::new(blocks) }
25            $(dummy!{$header} #[inline] fn len_from_header(self) -> Option<u32> {Some(self.len())})?
26        })*}
27}
28macro_rules! impl_simple_node {
29    ($($ty:ident: $size:literal),* $(,)?)=>{
30        $(impl SimpleNode for $ty<'_> { const SIZE: u32 = $size; })*
31        impl_node! {$( $ty : header |_it| Self::SIZE ,)*}
32    }
33}
34
35// TODO(clean): Figure out a way to not expose Block in this trait, so that I
36// don't need to make Block pub(in crate::parser)
37pub(in crate::parser) trait Node<'a>: Copy {
38    /// The length.
39    ///
40    /// `Some` if it is possible to compute it based only on reading the header,
41    /// `None` otherwise. This should always return the same as [`Node::len`].
42    fn len_from_header(self) -> Option<u32> {
43        None
44    }
45    /// The number of blocks this node requires.
46    fn len(self) -> u32;
47    fn first(blocks: &'a [Block]) -> Self;
48}
49pub(in crate::parser) trait SimpleNode {
50    const SIZE: u32;
51}
52impl_node! {
53    ChirpFile: |it| it.root_statement_offset() + it.root_statement().len(),
54    Fn:        |it| Self::HEADER_SIZE + it.parameter_len() + it.body().len(),
55    Method:    header |it| Self::HEADER_SIZE + it.argument_len(),
56    Template:  header |it| Self::HEADER_SIZE + it.argument_len() + it.methods_len() + it.children_len(),
57    Spawn:     header |it| Self::HEADER_SIZE + it.methods_len() + it.children_len(),
58    Statement: header |it| match it.typed() {
59        StType::Spawn(s) => s.len(),
60        StType::Template(s) => s.len(),
61        StType::Code(_) => Code::SIZE,
62    },
63}
64impl_simple_node! {Import: 2, Argument: 2, Code: 1}
65
66#[rustfmt::skip] impl SimpleNode for IdentOffset { const SIZE: u32 = 1; }
67#[rustfmt::skip] impl<'a> Node<'a> for IdentOffset {
68    fn len(self) -> u32 { Self::SIZE }
69    fn first(blocks: &'a [Block]) -> Self { Self { start: blocks[0].0 } }
70}
71
72#[derive(Clone, Copy)]
73pub(in crate::parser) struct List<'a, T: Node<'a>>(&'a [Block], PhantomData<T>);
74
75#[derive(Clone)]
76pub(in crate::parser) struct ListIter<'a, T: Node<'a>>(List<'a, T>);
77
78impl<'a, T: Node<'a>> List<'a, T> {
79    pub fn empty() -> Self {
80        Self(&[], PhantomData)
81    }
82    #[inline]
83    pub(super) fn new(blocks: &'a [Block]) -> Self {
84        Self(blocks, PhantomData)
85    }
86    pub fn is_empty(self) -> bool {
87        self.0.is_empty()
88    }
89    #[inline]
90    pub fn first(&self) -> Option<T> {
91        if self.is_empty() {
92            return None;
93        }
94        Some(T::first(self.0))
95    }
96    #[inline]
97    pub fn iter(self) -> ListIter<'a, T> {
98        ListIter(self)
99    }
100}
101impl<'a, T: Node<'a>> Iterator for ListIter<'a, T> {
102    type Item = T;
103
104    #[inline]
105    fn next(&mut self) -> Option<Self::Item> {
106        let ret = self.0.first()?;
107        self.0 .0 = &self.0 .0[as_usize(ret.len())..];
108        Some(ret)
109    }
110}
111impl<'a, T: SimpleNode + Node<'a>> List<'a, T> {
112    pub const fn count(&self) -> usize {
113        self.0.len() / as_usize(T::SIZE)
114    }
115    pub fn get(&self, index: usize) -> Option<T> {
116        let start = index * as_usize(T::SIZE);
117        let end = (index + 1) * as_usize(T::SIZE);
118        let blocks = self.0.get(start..end)?;
119        Some(T::first(blocks))
120    }
121    pub fn last(&self) -> Option<T> {
122        let index = self.count().checked_sub(1)?;
123        self.get(index)
124    }
125}