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