diff options
Diffstat (limited to 'crates/mbe/src/subtree_source.rs')
-rw-r--r-- | crates/mbe/src/subtree_source.rs | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs new file mode 100644 index 000000000..41461b315 --- /dev/null +++ b/crates/mbe/src/subtree_source.rs | |||
@@ -0,0 +1,197 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use parser::{Token, TokenSource}; | ||
4 | use std::cell::{Cell, Ref, RefCell}; | ||
5 | use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; | ||
6 | use tt::buffer::{Cursor, TokenBuffer}; | ||
7 | |||
8 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
9 | struct TtToken { | ||
10 | pub kind: SyntaxKind, | ||
11 | pub is_joint_to_next: bool, | ||
12 | pub text: SmolStr, | ||
13 | } | ||
14 | |||
15 | pub(crate) struct SubtreeTokenSource<'a> { | ||
16 | cached_cursor: Cell<Cursor<'a>>, | ||
17 | cached: RefCell<Vec<Option<TtToken>>>, | ||
18 | curr: (Token, usize), | ||
19 | } | ||
20 | |||
21 | impl<'a> SubtreeTokenSource<'a> { | ||
22 | // Helper function used in test | ||
23 | #[cfg(test)] | ||
24 | pub fn text(&self) -> SmolStr { | ||
25 | match *self.get(self.curr.1) { | ||
26 | Some(ref tt) => tt.text.clone(), | ||
27 | _ => SmolStr::new(""), | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | impl<'a> SubtreeTokenSource<'a> { | ||
33 | pub fn new(buffer: &'a TokenBuffer) -> SubtreeTokenSource<'a> { | ||
34 | let cursor = buffer.begin(); | ||
35 | |||
36 | let mut res = SubtreeTokenSource { | ||
37 | curr: (Token { kind: EOF, is_jointed_to_next: false }, 0), | ||
38 | cached_cursor: Cell::new(cursor), | ||
39 | cached: RefCell::new(Vec::with_capacity(10)), | ||
40 | }; | ||
41 | res.curr = (res.mk_token(0), 0); | ||
42 | res | ||
43 | } | ||
44 | |||
45 | fn mk_token(&self, pos: usize) -> Token { | ||
46 | match *self.get(pos) { | ||
47 | Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, | ||
48 | None => Token { kind: EOF, is_jointed_to_next: false }, | ||
49 | } | ||
50 | } | ||
51 | |||
52 | fn get(&self, pos: usize) -> Ref<Option<TtToken>> { | ||
53 | fn is_lifetime(c: Cursor) -> Option<(Cursor, SmolStr)> { | ||
54 | let tkn = c.token_tree(); | ||
55 | |||
56 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tkn { | ||
57 | if punct.char == '\'' { | ||
58 | let next = c.bump(); | ||
59 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = next.token_tree() { | ||
60 | let res_cursor = next.bump(); | ||
61 | let text = SmolStr::new("'".to_string() + &ident.to_string()); | ||
62 | |||
63 | return Some((res_cursor, text)); | ||
64 | } else { | ||
65 | panic!("Next token must be ident : {:#?}", next.token_tree()); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | None | ||
71 | } | ||
72 | |||
73 | if pos < self.cached.borrow().len() { | ||
74 | return Ref::map(self.cached.borrow(), |c| &c[pos]); | ||
75 | } | ||
76 | |||
77 | { | ||
78 | let mut cached = self.cached.borrow_mut(); | ||
79 | while pos >= cached.len() { | ||
80 | let cursor = self.cached_cursor.get(); | ||
81 | if cursor.eof() { | ||
82 | cached.push(None); | ||
83 | continue; | ||
84 | } | ||
85 | |||
86 | if let Some((curr, text)) = is_lifetime(cursor) { | ||
87 | cached.push(Some(TtToken { kind: LIFETIME, is_joint_to_next: false, text })); | ||
88 | self.cached_cursor.set(curr); | ||
89 | continue; | ||
90 | } | ||
91 | |||
92 | match cursor.token_tree() { | ||
93 | Some(tt::TokenTree::Leaf(leaf)) => { | ||
94 | cached.push(Some(convert_leaf(&leaf))); | ||
95 | self.cached_cursor.set(cursor.bump()); | ||
96 | } | ||
97 | Some(tt::TokenTree::Subtree(subtree)) => { | ||
98 | self.cached_cursor.set(cursor.subtree().unwrap()); | ||
99 | cached.push(Some(convert_delim(subtree.delimiter_kind(), false))); | ||
100 | } | ||
101 | None => { | ||
102 | if let Some(subtree) = cursor.end() { | ||
103 | cached.push(Some(convert_delim(subtree.delimiter_kind(), true))); | ||
104 | self.cached_cursor.set(cursor.bump()); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | Ref::map(self.cached.borrow(), |c| &c[pos]) | ||
112 | } | ||
113 | } | ||
114 | |||
115 | impl<'a> TokenSource for SubtreeTokenSource<'a> { | ||
116 | fn current(&self) -> Token { | ||
117 | self.curr.0 | ||
118 | } | ||
119 | |||
120 | /// Lookahead n token | ||
121 | fn lookahead_nth(&self, n: usize) -> Token { | ||
122 | self.mk_token(self.curr.1 + n) | ||
123 | } | ||
124 | |||
125 | /// bump cursor to next token | ||
126 | fn bump(&mut self) { | ||
127 | if self.current().kind == EOF { | ||
128 | return; | ||
129 | } | ||
130 | |||
131 | self.curr = (self.mk_token(self.curr.1 + 1), self.curr.1 + 1); | ||
132 | } | ||
133 | |||
134 | /// Is the current token a specified keyword? | ||
135 | fn is_keyword(&self, kw: &str) -> bool { | ||
136 | match *self.get(self.curr.1) { | ||
137 | Some(ref t) => t.text == *kw, | ||
138 | _ => false, | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken { | ||
144 | let (kinds, texts) = match d { | ||
145 | Some(tt::DelimiterKind::Parenthesis) => ([T!['('], T![')']], "()"), | ||
146 | Some(tt::DelimiterKind::Brace) => ([T!['{'], T!['}']], "{}"), | ||
147 | Some(tt::DelimiterKind::Bracket) => ([T!['['], T![']']], "[]"), | ||
148 | None => ([L_DOLLAR, R_DOLLAR], ""), | ||
149 | }; | ||
150 | |||
151 | let idx = closing as usize; | ||
152 | let kind = kinds[idx]; | ||
153 | let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; | ||
154 | TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text) } | ||
155 | } | ||
156 | |||
157 | fn convert_literal(l: &tt::Literal) -> TtToken { | ||
158 | let kind = lex_single_syntax_kind(&l.text) | ||
159 | .map(|(kind, _error)| kind) | ||
160 | .filter(|kind| kind.is_literal()) | ||
161 | .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); | ||
162 | |||
163 | TtToken { kind, is_joint_to_next: false, text: l.text.clone() } | ||
164 | } | ||
165 | |||
166 | fn convert_ident(ident: &tt::Ident) -> TtToken { | ||
167 | let kind = match ident.text.as_ref() { | ||
168 | "true" => T![true], | ||
169 | "false" => T![false], | ||
170 | i if i.starts_with('\'') => LIFETIME, | ||
171 | _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), | ||
172 | }; | ||
173 | |||
174 | TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } | ||
175 | } | ||
176 | |||
177 | fn convert_punct(p: tt::Punct) -> TtToken { | ||
178 | let kind = match SyntaxKind::from_char(p.char) { | ||
179 | None => panic!("{:#?} is not a valid punct", p), | ||
180 | Some(kind) => kind, | ||
181 | }; | ||
182 | |||
183 | let text = { | ||
184 | let mut buf = [0u8; 4]; | ||
185 | let s: &str = p.char.encode_utf8(&mut buf); | ||
186 | SmolStr::new(s) | ||
187 | }; | ||
188 | TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text } | ||
189 | } | ||
190 | |||
191 | fn convert_leaf(leaf: &tt::Leaf) -> TtToken { | ||
192 | match leaf { | ||
193 | tt::Leaf::Literal(l) => convert_literal(l), | ||
194 | tt::Leaf::Ident(ident) => convert_ident(ident), | ||
195 | tt::Leaf::Punct(punct) => convert_punct(*punct), | ||
196 | } | ||
197 | } | ||