squawk_parser/
shortcuts.rs1use 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 enter(&mut self, kind: SyntaxKind) {
68 match mem::replace(&mut self.state, State::Normal) {
69 State::PendingEnter => {
70 (self.sink)(StrStep::Enter { kind });
71 return;
74 }
75 State::PendingExit => (self.sink)(StrStep::Exit),
76 State::Normal => (),
77 }
78
79 let n_trivias = (self.pos..self.lexed.len())
80 .take_while(|&it| self.lexed.kind(it).is_trivia())
81 .count();
82 let leading_trivias = self.pos..self.pos + n_trivias;
83 let n_attached_trivias = n_attached_trivias(
84 kind,
85 leading_trivias
86 .rev()
87 .map(|it| (self.lexed.kind(it), self.lexed.text(it))),
88 );
89 self.eat_n_trivias(n_trivias - n_attached_trivias);
90 (self.sink)(StrStep::Enter { kind });
91 self.eat_n_trivias(n_attached_trivias);
92 }
93
94 fn exit(&mut self) {
95 match mem::replace(&mut self.state, State::PendingExit) {
96 State::PendingEnter => unreachable!(),
97 State::PendingExit => (self.sink)(StrStep::Exit),
98 State::Normal => (),
99 }
100 }
101
102 fn eat_trivias(&mut self) {
103 while self.pos < self.lexed.len() {
104 let kind = self.lexed.kind(self.pos);
105 if !kind.is_trivia() {
106 break;
107 }
108 self.do_token(kind, 1);
109 }
110 }
111
112 fn eat_n_trivias(&mut self, n: usize) {
113 for _ in 0..n {
114 let kind = self.lexed.kind(self.pos);
115 assert!(kind.is_trivia());
116 self.do_token(kind, 1);
117 }
118 }
119
120 fn do_token(&mut self, kind: SyntaxKind, n_tokens: usize) {
121 let text = &self.lexed.range_text(self.pos..self.pos + n_tokens);
122 self.pos += n_tokens;
123 (self.sink)(StrStep::Token { kind, text });
124 }
125}
126
127impl LexedStr<'_> {
128 pub fn to_input(&self) -> crate::Input {
129 let mut res = crate::Input::default();
130 let mut was_joint = false;
131 for i in 0..self.len() {
132 let kind = self.kind(i);
133 if kind.is_trivia() {
134 was_joint = false;
135 }
138 else {
145 if was_joint {
146 res.was_joint();
147 }
148 res.push(kind);
149 was_joint = true;
150 }
151 }
152 res
153 }
154
155 pub fn intersperse_trivia(&self, output: &Output, sink: &mut dyn FnMut(StrStep<'_>)) -> bool {
157 let mut builder = Builder {
158 lexed: self,
159 pos: 0,
160 state: State::PendingEnter,
161 sink,
162 };
163
164 for event in output.iter() {
165 match event {
166 Step::Token {
167 kind,
168 n_input_tokens: n_raw_tokens,
169 } => builder.token(kind, n_raw_tokens),
170 Step::Enter { kind } => builder.enter(kind),
171 Step::Exit => builder.exit(),
172 Step::Error { msg } => {
173 let text_pos = builder.lexed.text_start(builder.pos);
174 (builder.sink)(StrStep::Error { msg, pos: text_pos });
175 }
176 }
177 }
178
179 match mem::replace(&mut builder.state, State::Normal) {
180 State::PendingExit => {
181 builder.eat_trivias();
182 (builder.sink)(StrStep::Exit);
183 }
184 State::PendingEnter | State::Normal => unreachable!(),
185 }
186
187 builder.pos == builder.lexed.len()
189 }
190}
191
192fn n_attached_trivias<'a>(
193 kind: SyntaxKind,
194 trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
195) -> usize {
196 match kind {
197 SyntaxKind::CREATE_TABLE => {
201 let mut res = 0;
202 let mut trivias = trivias.enumerate().peekable();
203
204 while let Some((i, (kind, text))) = trivias.next() {
205 match kind {
206 SyntaxKind::WHITESPACE if text.contains("\n\n") => {
207 if let Some((SyntaxKind::COMMENT, peek_text)) =
210 trivias.peek().map(|(_, pair)| pair)
211 {
212 if is_outer(peek_text) {
213 continue;
214 }
215 }
216 break;
217 }
218 SyntaxKind::COMMENT => {
219 if is_inner(text) {
220 break;
221 }
222 res = i + 1;
223 }
224 _ => (),
225 }
226 }
227 res
228 }
229 _ => 0,
230 }
231}
232
233fn is_outer(text: &str) -> bool {
234 if text.starts_with("////") || text.starts_with("/***") {
235 return false;
236 }
237 text.starts_with("///") || text.starts_with("/**")
238}
239
240fn is_inner(text: &str) -> bool {
241 text.starts_with("//!") || text.starts_with("/*!")
242}