aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_tt
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_tt')
-rw-r--r--crates/ra_tt/src/buffer.rs169
-rw-r--r--crates/ra_tt/src/lib.rs2
2 files changed, 171 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..56b844b8b
--- /dev/null
+++ b/crates/ra_tt/src/buffer.rs
@@ -0,0 +1,169 @@
1use crate::{TokenTree, Subtree, Leaf};
2
3#[derive(Copy, Clone, Debug, Eq, PartialEq)]
4struct EntryId(usize);
5
6#[derive(Copy, Clone, Debug, Eq, PartialEq)]
7struct 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)]
12enum 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)]
24pub struct TokenBuffer {
25 buffers: Vec<Box<[Entry]>>,
26}
27
28impl TokenBuffer {
29 pub fn new(tokens: &[TokenTree]) -> TokenBuffer {
30 let mut buffers = vec![];
31
32 let idx = TokenBuffer::new_inner(tokens, &mut buffers, None);
33 assert_eq!(idx, 0);
34
35 TokenBuffer { buffers }
36 }
37
38 fn new_inner(
39 tokens: &[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.iter().cloned().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,
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)]
88pub struct Cursor<'a> {
89 buffer: &'a TokenBuffer,
90 ptr: EntryPtr,
91}
92
93impl<'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
99impl<'a> Eq for Cursor<'a> {}
100
101impl<'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}
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index 62c5ac52a..2a48c66c4 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -165,3 +165,5 @@ impl Subtree {
165 self.token_trees.len() + children_count 165 self.token_trees.len() + children_count
166 } 166 }
167} 167}
168
169pub mod buffer;