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