1use std::mem;
28
29use crate::{
30 lexed_str::LexedStr,
31 output::{Output, Step},
32 syntax_kind::SyntaxKind,
33};
34
35#[derive(Debug)]
36pub enum StrStep<'a> {
37 Token { kind: SyntaxKind, text: &'a str },
38 Enter { kind: SyntaxKind },
39 Exit,
40 Error { msg: &'a str, pos: usize },
41}
42
43enum State {
44 PendingEnter,
45 Normal,
46 PendingExit,
47}
48
49struct Builder<'a, 'b> {
50 lexed: &'a LexedStr<'a>,
51 pos: usize,
52 state: State,
53 sink: &'b mut dyn FnMut(StrStep<'_>),
54}
55
56impl Builder<'_, '_> {
57 fn token(&mut self, kind: SyntaxKind, n_tokens: u8) {
58 match mem::replace(&mut self.state, State::Normal) {
59 State::PendingEnter => unreachable!(),
60 State::PendingExit => (self.sink)(StrStep::Exit),
61 State::Normal => (),
62 }
63 self.eat_trivias();
64 self.do_token(kind, n_tokens as usize);
65 }
66
67 fn float_split(&mut self, has_pseudo_dot: bool) {
68 match mem::replace(&mut self.state, State::Normal) {
69 State::PendingEnter => unreachable!(),
70 State::PendingExit => (self.sink)(StrStep::Exit),
71 State::Normal => (),
72 }
73 self.eat_trivias();
74 self.do_float_split(has_pseudo_dot);
75 }
76
77 fn enter(&mut self, kind: SyntaxKind) {
78 match mem::replace(&mut self.state, State::Normal) {
79 State::PendingEnter => {
80 (self.sink)(StrStep::Enter { kind });
81 return;
84 }
85 State::PendingExit => (self.sink)(StrStep::Exit),
86 State::Normal => (),
87 }
88
89 let n_trivias = (self.pos..self.lexed.len())
90 .take_while(|&it| self.lexed.kind(it).is_trivia())
91 .count();
92 let leading_trivias = self.pos..self.pos + n_trivias;
93 let n_attached_trivias = n_attached_trivias(
94 kind,
95 leading_trivias
96 .rev()
97 .map(|it| (self.lexed.kind(it), self.lexed.text(it))),
98 );
99 self.eat_n_trivias(n_trivias - n_attached_trivias);
100 (self.sink)(StrStep::Enter { kind });
101 self.eat_n_trivias(n_attached_trivias);
102 }
103
104 fn exit(&mut self) {
105 match mem::replace(&mut self.state, State::PendingExit) {
106 State::PendingEnter => unreachable!(),
107 State::PendingExit => (self.sink)(StrStep::Exit),
108 State::Normal => (),
109 }
110 }
111
112 fn eat_trivias(&mut self) {
113 while self.pos < self.lexed.len() {
114 let kind = self.lexed.kind(self.pos);
115 if !kind.is_trivia() {
116 break;
117 }
118 self.do_token(kind, 1);
119 }
120 }
121
122 fn eat_n_trivias(&mut self, n: usize) {
123 for _ in 0..n {
124 let kind = self.lexed.kind(self.pos);
125 assert!(kind.is_trivia());
126 self.do_token(kind, 1);
127 }
128 }
129
130 fn do_token(&mut self, kind: SyntaxKind, n_tokens: usize) {
131 let text = &self.lexed.range_text(self.pos..self.pos + n_tokens);
132 self.pos += n_tokens;
133 (self.sink)(StrStep::Token { kind, text });
134 }
135
136 fn do_float_split(&mut self, has_pseudo_dot: bool) {
137 let text = &self.lexed.range_text(self.pos..self.pos + 1);
138
139 match text.split_once('.') {
140 Some((left, right)) => {
141 assert!(!left.is_empty());
142 (self.sink)(StrStep::Enter {
143 kind: SyntaxKind::NAME_REF,
144 });
145 (self.sink)(StrStep::Token {
146 kind: SyntaxKind::INT_NUMBER,
147 text: left,
148 });
149 (self.sink)(StrStep::Exit);
150
151 (self.sink)(StrStep::Exit);
153
154 (self.sink)(StrStep::Token {
155 kind: SyntaxKind::DOT,
156 text: ".",
157 });
158
159 if has_pseudo_dot {
160 assert!(right.is_empty(), "{left}.{right}");
161 self.state = State::Normal;
162 } else {
163 assert!(!right.is_empty(), "{left}.{right}");
164 (self.sink)(StrStep::Enter {
165 kind: SyntaxKind::NAME_REF,
166 });
167 (self.sink)(StrStep::Token {
168 kind: SyntaxKind::INT_NUMBER,
169 text: right,
170 });
171 (self.sink)(StrStep::Exit);
172
173 self.state = State::PendingExit;
175 }
176 }
177 None => {
178 (self.sink)(StrStep::Error {
181 msg: "illegal float literal",
182 pos: self.pos,
183 });
184 (self.sink)(StrStep::Enter {
185 kind: SyntaxKind::ERROR,
186 });
187 (self.sink)(StrStep::Token {
188 kind: SyntaxKind::FLOAT_NUMBER,
189 text,
190 });
191 (self.sink)(StrStep::Exit);
192
193 (self.sink)(StrStep::Exit);
195
196 self.state = if has_pseudo_dot {
197 State::Normal
198 } else {
199 State::PendingExit
200 };
201 }
202 }
203
204 self.pos += 1;
205 }
206}
207
208impl LexedStr<'_> {
209 pub fn to_input(&self) -> crate::Input {
210 let mut res = crate::Input::default();
211 let mut was_joint = false;
212 for i in 0..self.len() {
213 let kind = self.kind(i);
214 if kind.is_trivia() {
215 was_joint = false;
216 }
219 else {
226 if was_joint {
227 res.was_joint();
228 }
229 res.push(kind);
230 was_joint = true;
241 }
243 }
244 res
245 }
246
247 pub fn intersperse_trivia(&self, output: &Output, sink: &mut dyn FnMut(StrStep<'_>)) -> bool {
249 let mut builder = Builder {
250 lexed: self,
251 pos: 0,
252 state: State::PendingEnter,
253 sink,
254 };
255
256 for event in output.iter() {
257 match event {
258 Step::Token {
259 kind,
260 n_input_tokens: n_raw_tokens,
261 } => builder.token(kind, n_raw_tokens),
262 Step::FloatSplit {
263 ends_in_dot: has_pseudo_dot,
264 } => builder.float_split(has_pseudo_dot),
265 Step::Enter { kind } => builder.enter(kind),
266 Step::Exit => builder.exit(),
267 Step::Error { msg } => {
268 let text_pos = builder.lexed.text_start(builder.pos);
269 (builder.sink)(StrStep::Error { msg, pos: text_pos });
270 }
271 }
272 }
273
274 match mem::replace(&mut builder.state, State::Normal) {
275 State::PendingExit => {
276 builder.eat_trivias();
277 (builder.sink)(StrStep::Exit);
278 }
279 State::PendingEnter | State::Normal => unreachable!(),
280 }
281
282 builder.pos == builder.lexed.len()
284 }
285}
286
287fn n_attached_trivias<'a>(
288 kind: SyntaxKind,
289 trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
290) -> usize {
291 match kind {
292 SyntaxKind::CREATE_TABLE => {
296 let mut res = 0;
297 let mut trivias = trivias.enumerate().peekable();
298
299 while let Some((i, (kind, text))) = trivias.next() {
300 match kind {
301 SyntaxKind::WHITESPACE if text.contains("\n\n") => {
302 if let Some((SyntaxKind::COMMENT, peek_text)) =
305 trivias.peek().map(|(_, pair)| pair)
306 {
307 if is_outer(peek_text) {
308 continue;
309 }
310 }
311 break;
312 }
313 SyntaxKind::COMMENT => {
314 if is_inner(text) {
315 break;
316 }
317 res = i + 1;
318 }
319 _ => (),
320 }
321 }
322 res
323 }
324 _ => 0,
325 }
326}
327
328fn is_outer(text: &str) -> bool {
329 if text.starts_with("////") || text.starts_with("/***") {
330 return false;
331 }
332 text.starts_with("///") || text.starts_with("/**")
333}
334
335fn is_inner(text: &str) -> bool {
336 text.starts_with("//!") || text.starts_with("/*!")
337}