diff options
Diffstat (limited to 'crates/ra_tt/src/buffer.rs')
-rw-r--r-- | crates/ra_tt/src/buffer.rs | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/crates/ra_tt/src/buffer.rs b/crates/ra_tt/src/buffer.rs new file mode 100644 index 000000000..a149205fb --- /dev/null +++ b/crates/ra_tt/src/buffer.rs | |||
@@ -0,0 +1,169 @@ | |||
1 | use crate::{TokenTree, Subtree, Leaf}; | ||
2 | |||
3 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
4 | struct EntryId(usize); | ||
5 | |||
6 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
7 | struct EntryPtr(EntryId, usize); | ||
8 | |||
9 | /// Internal type which is used instead of `TokenTree` to represent a token tree | ||
10 | /// within a `TokenBuffer`. | ||
11 | #[derive(Debug)] | ||
12 | enum Entry { | ||
13 | // Mimicking types from proc-macro. | ||
14 | Subtree(Subtree, EntryId), | ||
15 | Leaf(Leaf), | ||
16 | // End entries contain a pointer to the entry from the containing | ||
17 | // token tree, or None if this is the outermost level. | ||
18 | End(Option<EntryPtr>), | ||
19 | } | ||
20 | |||
21 | /// A token tree buffer | ||
22 | /// The safe version of `syn` [`TokenBuffer`](https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L41) | ||
23 | #[derive(Debug)] | ||
24 | pub struct TokenBuffer { | ||
25 | buffers: Vec<Box<[Entry]>>, | ||
26 | } | ||
27 | |||
28 | impl TokenBuffer { | ||
29 | pub fn new(tokens: &[TokenTree]) -> TokenBuffer { | ||
30 | let mut buffers = vec![]; | ||
31 | |||
32 | let idx = TokenBuffer::new_inner(tokens.to_vec(), &mut buffers, None); | ||
33 | assert_eq!(idx, 0); | ||
34 | |||
35 | TokenBuffer { buffers } | ||
36 | } | ||
37 | |||
38 | fn new_inner( | ||
39 | tokens: Vec<TokenTree>, | ||
40 | buffers: &mut Vec<Box<[Entry]>>, | ||
41 | next: Option<EntryPtr>, | ||
42 | ) -> usize { | ||
43 | let mut entries = vec![]; | ||
44 | let mut children = vec![]; | ||
45 | |||
46 | for (idx, tt) in tokens.into_iter().enumerate() { | ||
47 | match tt { | ||
48 | TokenTree::Leaf(leaf) => { | ||
49 | entries.push(Entry::Leaf(leaf)); | ||
50 | } | ||
51 | TokenTree::Subtree(subtree) => { | ||
52 | entries.push(Entry::End(None)); | ||
53 | children.push((idx, subtree)); | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | entries.push(Entry::End(next)); | ||
59 | let res = buffers.len(); | ||
60 | buffers.push(entries.into_boxed_slice()); | ||
61 | |||
62 | for (child_idx, subtree) in children { | ||
63 | let idx = TokenBuffer::new_inner( | ||
64 | subtree.token_trees.clone(), | ||
65 | buffers, | ||
66 | Some(EntryPtr(EntryId(res), child_idx + 1)), | ||
67 | ); | ||
68 | buffers[res].as_mut()[child_idx] = Entry::Subtree(subtree, EntryId(idx)); | ||
69 | } | ||
70 | |||
71 | res | ||
72 | } | ||
73 | |||
74 | /// Creates a cursor referencing the first token in the buffer and able to | ||
75 | /// traverse until the end of the buffer. | ||
76 | pub fn begin(&self) -> Cursor { | ||
77 | Cursor::create(self, EntryPtr(EntryId(0), 0)) | ||
78 | } | ||
79 | |||
80 | fn entry(&self, ptr: &EntryPtr) -> Option<&Entry> { | ||
81 | let id = ptr.0; | ||
82 | self.buffers[id.0].get(ptr.1) | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 | ||
87 | #[derive(Copy, Clone, Debug)] | ||
88 | pub struct Cursor<'a> { | ||
89 | buffer: &'a TokenBuffer, | ||
90 | ptr: EntryPtr, | ||
91 | } | ||
92 | |||
93 | impl<'a> PartialEq for Cursor<'a> { | ||
94 | fn eq(&self, other: &Cursor) -> bool { | ||
95 | self.ptr == other.ptr && std::ptr::eq(self.buffer, other.buffer) | ||
96 | } | ||
97 | } | ||
98 | |||
99 | impl<'a> Eq for Cursor<'a> {} | ||
100 | |||
101 | impl<'a> Cursor<'a> { | ||
102 | /// Check whether it is eof | ||
103 | pub fn eof(self) -> bool { | ||
104 | match self.buffer.entry(&self.ptr) { | ||
105 | None | Some(Entry::End(None)) => true, | ||
106 | _ => false, | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /// If the cursor is pointing at the end of a subtree, returns | ||
111 | /// the parent subtree | ||
112 | pub fn end(self) -> Option<(&'a Subtree)> { | ||
113 | match self.entry() { | ||
114 | Some(Entry::End(Some(ptr))) => { | ||
115 | let idx = ptr.1; | ||
116 | if let Some(Entry::Subtree(subtree, _)) = | ||
117 | self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) | ||
118 | { | ||
119 | return Some(subtree); | ||
120 | } | ||
121 | |||
122 | None | ||
123 | } | ||
124 | _ => None, | ||
125 | } | ||
126 | } | ||
127 | |||
128 | fn entry(self) -> Option<(&'a Entry)> { | ||
129 | self.buffer.entry(&self.ptr) | ||
130 | } | ||
131 | |||
132 | /// If the cursor is pointing at a `Subtree`, returns | ||
133 | /// a cursor into that subtree | ||
134 | pub fn subtree(self) -> Option<Cursor<'a>> { | ||
135 | match self.entry() { | ||
136 | Some(Entry::Subtree(_, entry_id)) => { | ||
137 | Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) | ||
138 | } | ||
139 | _ => None, | ||
140 | } | ||
141 | } | ||
142 | |||
143 | /// If the cursor is pointing at a `TokenTree`, returns it | ||
144 | pub fn token_tree(self) -> Option<(TokenTree)> { | ||
145 | match self.entry() { | ||
146 | Some(Entry::Leaf(leaf)) => Some(leaf.clone().into()), | ||
147 | Some(Entry::Subtree(subtree, _)) => Some(subtree.clone().into()), | ||
148 | Some(Entry::End(_)) => None, | ||
149 | None => None, | ||
150 | } | ||
151 | } | ||
152 | |||
153 | fn create(buffer: &'a TokenBuffer, ptr: EntryPtr) -> Cursor<'a> { | ||
154 | Cursor { buffer, ptr } | ||
155 | } | ||
156 | |||
157 | /// Bump the cursor | ||
158 | pub fn bump(self) -> Cursor<'a> { | ||
159 | if let Some(Entry::End(exit)) = self.buffer.entry(&self.ptr) { | ||
160 | if let Some(exit) = exit { | ||
161 | Cursor::create(self.buffer, *exit) | ||
162 | } else { | ||
163 | self | ||
164 | } | ||
165 | } else { | ||
166 | Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)) | ||
167 | } | ||
168 | } | ||
169 | } | ||