1use std::mem;
13
14use crate::{
15 Edition, LexedStr, Step,
16 SyntaxKind::{self, *},
17};
18
19#[derive(Debug)]
20pub enum StrStep<'a> {
21 Token { kind: SyntaxKind, text: &'a str },
22 Enter { kind: SyntaxKind },
23 Exit,
24 Error { msg: &'a str, pos: usize },
25}
26
27impl LexedStr<'_> {
28 pub fn to_input(&self, edition: Edition) -> crate::Input {
29 let _p = tracing::info_span!("LexedStr::to_input").entered();
30 let mut res = crate::Input::with_capacity(self.len());
31 let mut was_joint = false;
32 for i in 0..self.len() {
33 let kind = self.kind(i);
34 if kind.is_trivia() {
35 was_joint = false
36 } else if kind == SyntaxKind::IDENT {
37 let token_text = self.text(i);
38 res.push_ident(
39 SyntaxKind::from_contextual_keyword(token_text, edition)
40 .unwrap_or(SyntaxKind::IDENT),
41 edition,
42 )
43 } else {
44 if was_joint {
45 res.was_joint();
46 }
47 res.push(kind, edition);
48 if kind == SyntaxKind::FLOAT_NUMBER {
52 if !self.text(i).ends_with('.') {
53 res.was_joint();
54 } else {
55 was_joint = false;
56 }
57 } else {
58 was_joint = true;
59 }
60 }
61 }
62 res
63 }
64
65 pub fn intersperse_trivia(
67 &self,
68 output: &crate::Output,
69 sink: &mut dyn FnMut(StrStep<'_>),
70 ) -> bool {
71 let mut builder = Builder { lexed: self, pos: 0, state: State::PendingEnter, sink };
72
73 for event in output.iter() {
74 match event {
75 Step::Token { kind, n_input_tokens: n_raw_tokens } => {
76 builder.token(kind, n_raw_tokens)
77 }
78 Step::FloatSplit { ends_in_dot: has_pseudo_dot } => {
79 builder.float_split(has_pseudo_dot)
80 }
81 Step::Enter { kind } => builder.enter(kind),
82 Step::Exit => builder.exit(),
83 Step::Error { msg } => {
84 let text_pos = builder.lexed.text_start(builder.pos);
85 (builder.sink)(StrStep::Error { msg, pos: text_pos });
86 }
87 }
88 }
89
90 match mem::replace(&mut builder.state, State::Normal) {
91 State::PendingExit => {
92 builder.eat_trivias();
93 (builder.sink)(StrStep::Exit);
94 }
95 State::PendingEnter | State::Normal => unreachable!(),
96 }
97
98 builder.pos == builder.lexed.len()
100 }
101}
102
103struct Builder<'a, 'b> {
104 lexed: &'a LexedStr<'a>,
105 pos: usize,
106 state: State,
107 sink: &'b mut dyn FnMut(StrStep<'_>),
108}
109
110enum State {
111 PendingEnter,
112 Normal,
113 PendingExit,
114}
115
116impl Builder<'_, '_> {
117 fn token(&mut self, kind: SyntaxKind, n_tokens: u8) {
118 match mem::replace(&mut self.state, State::Normal) {
119 State::PendingEnter => unreachable!(),
120 State::PendingExit => (self.sink)(StrStep::Exit),
121 State::Normal => (),
122 }
123 self.eat_trivias();
124 self.do_token(kind, n_tokens as usize);
125 }
126
127 fn float_split(&mut self, has_pseudo_dot: bool) {
128 match mem::replace(&mut self.state, State::Normal) {
129 State::PendingEnter => unreachable!(),
130 State::PendingExit => (self.sink)(StrStep::Exit),
131 State::Normal => (),
132 }
133 self.eat_trivias();
134 self.do_float_split(has_pseudo_dot);
135 }
136
137 fn enter(&mut self, kind: SyntaxKind) {
138 match mem::replace(&mut self.state, State::Normal) {
139 State::PendingEnter => {
140 (self.sink)(StrStep::Enter { kind });
141 return;
144 }
145 State::PendingExit => (self.sink)(StrStep::Exit),
146 State::Normal => (),
147 }
148
149 let n_trivias =
150 (self.pos..self.lexed.len()).take_while(|&it| self.lexed.kind(it).is_trivia()).count();
151 let leading_trivias = self.pos..self.pos + n_trivias;
152 let n_attached_trivias = n_attached_trivias(
153 kind,
154 leading_trivias.rev().map(|it| (self.lexed.kind(it), self.lexed.text(it))),
155 );
156 self.eat_n_trivias(n_trivias - n_attached_trivias);
157 (self.sink)(StrStep::Enter { kind });
158 self.eat_n_trivias(n_attached_trivias);
159 }
160
161 fn exit(&mut self) {
162 match mem::replace(&mut self.state, State::PendingExit) {
163 State::PendingEnter => unreachable!(),
164 State::PendingExit => (self.sink)(StrStep::Exit),
165 State::Normal => (),
166 }
167 }
168
169 fn eat_trivias(&mut self) {
170 while self.pos < self.lexed.len() {
171 let kind = self.lexed.kind(self.pos);
172 if !kind.is_trivia() {
173 break;
174 }
175 self.do_token(kind, 1);
176 }
177 }
178
179 fn eat_n_trivias(&mut self, n: usize) {
180 for _ in 0..n {
181 let kind = self.lexed.kind(self.pos);
182 assert!(kind.is_trivia());
183 self.do_token(kind, 1);
184 }
185 }
186
187 fn do_token(&mut self, kind: SyntaxKind, n_tokens: usize) {
188 let text = &self.lexed.range_text(self.pos..self.pos + n_tokens);
189 self.pos += n_tokens;
190 (self.sink)(StrStep::Token { kind, text });
191 }
192
193 fn do_float_split(&mut self, has_pseudo_dot: bool) {
194 let text = &self.lexed.range_text(self.pos..self.pos + 1);
195
196 match text.split_once('.') {
197 Some((left, right)) => {
198 assert!(!left.is_empty());
199 (self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF });
200 (self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: left });
201 (self.sink)(StrStep::Exit);
202
203 (self.sink)(StrStep::Exit);
205
206 (self.sink)(StrStep::Token { kind: SyntaxKind::DOT, text: "." });
207
208 if has_pseudo_dot {
209 assert!(right.is_empty(), "{left}.{right}");
210 self.state = State::Normal;
211 } else {
212 assert!(!right.is_empty(), "{left}.{right}");
213 (self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF });
214 (self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right });
215 (self.sink)(StrStep::Exit);
216
217 self.state = State::PendingExit;
219 }
220 }
221 None => {
222 (self.sink)(StrStep::Error { msg: "illegal float literal", pos: self.pos });
225 (self.sink)(StrStep::Enter { kind: SyntaxKind::ERROR });
226 (self.sink)(StrStep::Token { kind: SyntaxKind::FLOAT_NUMBER, text });
227 (self.sink)(StrStep::Exit);
228
229 (self.sink)(StrStep::Exit);
231
232 self.state = if has_pseudo_dot { State::Normal } else { State::PendingExit };
233 }
234 }
235
236 self.pos += 1;
237 }
238}
239
240fn n_attached_trivias<'a>(
241 kind: SyntaxKind,
242 trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
243) -> usize {
244 match kind {
245 CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD
246 | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT
247 | EXTERN_CRATE => {
248 let mut res = 0;
249 let mut trivias = trivias.enumerate().peekable();
250
251 while let Some((i, (kind, text))) = trivias.next() {
252 match kind {
253 WHITESPACE if text.contains("\n\n") => {
254 if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair)
257 && is_outer(peek_text)
258 {
259 continue;
260 }
261 break;
262 }
263 COMMENT => {
264 if is_inner(text) {
265 break;
266 }
267 res = i + 1;
268 }
269 _ => (),
270 }
271 }
272 res
273 }
274 _ => 0,
275 }
276}
277
278fn is_outer(text: &str) -> bool {
279 if text.starts_with("////") || text.starts_with("/***") {
280 return false;
281 }
282 text.starts_with("///") || text.starts_with("/**")
283}
284
285fn is_inner(text: &str) -> bool {
286 text.starts_with("//!") || text.starts_with("/*!")
287}