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