Skip to main content

miden_rowan/green/
builder.rs

1use alloc::vec::Vec;
2use core::num::NonZeroUsize;
3
4use crate::{
5    NodeOrToken,
6    cow_mut::CowMut,
7    green::{GreenElement, GreenNode, SyntaxKind, node_cache::NodeCache},
8};
9
10/// A checkpoint for maybe wrapping a node. See `GreenNodeBuilder::checkpoint` for details.
11#[derive(Clone, Copy, Debug)]
12pub struct Checkpoint(NonZeroUsize);
13
14impl Checkpoint {
15    fn new(inner: usize) -> Self {
16        Self(NonZeroUsize::new(inner + 1).unwrap())
17    }
18
19    fn into_inner(self) -> usize {
20        self.0.get() - 1
21    }
22}
23
24/// A builder for a green tree.
25#[derive(Default, Debug)]
26pub struct GreenNodeBuilder<'cache> {
27    cache: CowMut<'cache, NodeCache>,
28    parents: Vec<(SyntaxKind, usize)>,
29    children: Vec<(u64, GreenElement)>,
30}
31
32impl GreenNodeBuilder<'_> {
33    /// Creates new builder.
34    pub fn new() -> GreenNodeBuilder<'static> {
35        GreenNodeBuilder::default()
36    }
37
38    /// Reusing `NodeCache` between different `GreenNodeBuilder`s saves memory.
39    /// It allows to structurally share underlying trees.
40    pub fn with_cache(cache: &mut NodeCache) -> GreenNodeBuilder<'_> {
41        GreenNodeBuilder {
42            cache: CowMut::Borrowed(cache),
43            parents: Vec::new(),
44            children: Vec::new(),
45        }
46    }
47
48    /// Adds new token to the current branch.
49    #[inline]
50    pub fn token(&mut self, kind: SyntaxKind, text: &str) {
51        let (hash, token) = self.cache.token(kind, text);
52        self.children.push((hash, token.into()));
53    }
54
55    /// Start new node and make it current.
56    #[inline]
57    pub fn start_node(&mut self, kind: SyntaxKind) {
58        let len = self.children.len();
59        self.parents.push((kind, len));
60    }
61
62    /// Finish current branch and restore previous
63    /// branch as current.
64    #[inline]
65    pub fn finish_node(&mut self) {
66        let (kind, first_child) = self.parents.pop().unwrap();
67        let (hash, node) = self.cache.node(kind, &mut self.children, first_child);
68        self.children.push((hash, node.into()));
69    }
70
71    /// Prepare for maybe wrapping the next node.
72    /// The way wrapping works is that you first of all get a checkpoint,
73    /// then you place all tokens you want to wrap, and then *maybe* call
74    /// `start_node_at`.
75    /// Example:
76    /// ```rust
77    /// # use miden_rowan::{GreenNodeBuilder, SyntaxKind};
78    /// # const PLUS: SyntaxKind = SyntaxKind(0);
79    /// # const OPERATION: SyntaxKind = SyntaxKind(1);
80    /// # struct Parser;
81    /// # impl Parser {
82    /// #     fn peek(&self) -> Option<SyntaxKind> { None }
83    /// #     fn parse_expr(&mut self) {}
84    /// # }
85    /// # let mut builder = GreenNodeBuilder::new();
86    /// # let mut parser = Parser;
87    /// let checkpoint = builder.checkpoint();
88    /// parser.parse_expr();
89    /// if parser.peek() == Some(PLUS) {
90    ///   // 1 + 2 = Add(1, 2)
91    ///   builder.start_node_at(checkpoint, OPERATION);
92    ///   parser.parse_expr();
93    ///   builder.finish_node();
94    /// }
95    /// ```
96    #[inline]
97    pub fn checkpoint(&self) -> Checkpoint {
98        Checkpoint::new(self.children.len())
99    }
100
101    /// Wrap the previous branch marked by `checkpoint` in a new branch and
102    /// make it current.
103    #[inline]
104    pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) {
105        let checkpoint = checkpoint.into_inner();
106        assert!(
107            checkpoint <= self.children.len(),
108            "checkpoint no longer valid, was finish_node called early?"
109        );
110
111        if let Some(&(_, first_child)) = self.parents.last() {
112            assert!(
113                checkpoint >= first_child,
114                "checkpoint no longer valid, was an unmatched start_node_at called?"
115            );
116        }
117
118        self.parents.push((kind, checkpoint));
119    }
120
121    /// Complete tree building. Make sure that
122    /// `start_node_at` and `finish_node` calls
123    /// are paired!
124    #[inline]
125    pub fn finish(mut self) -> GreenNode {
126        assert_eq!(self.children.len(), 1);
127        match self.children.pop().unwrap().1 {
128            NodeOrToken::Node(node) => node,
129            NodeOrToken::Token(_) => panic!(),
130        }
131    }
132}