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 self.eat_trivias();
80 (self.sink)(StrStep::Enter { kind });
81 }
82
83 fn exit(&mut self) {
84 match mem::replace(&mut self.state, State::PendingExit) {
85 State::PendingEnter => unreachable!(),
86 State::PendingExit => (self.sink)(StrStep::Exit),
87 State::Normal => (),
88 }
89 }
90
91 fn eat_trivias(&mut self) {
92 while self.pos < self.lexed.len() {
93 let kind = self.lexed.kind(self.pos);
94 if !kind.is_trivia() {
95 break;
96 }
97 self.do_token(kind, 1);
98 }
99 }
100
101 fn do_token(&mut self, kind: SyntaxKind, n_tokens: usize) {
102 let text = &self.lexed.range_text(self.pos..self.pos + n_tokens);
103 self.pos += n_tokens;
104 (self.sink)(StrStep::Token { kind, text });
105 }
106}
107
108impl LexedStr<'_> {
109 pub fn to_input(&self) -> crate::Input {
110 let mut res = crate::Input::default();
111 let mut was_joint = false;
112 for i in 0..self.len() {
113 let kind = self.kind(i);
114 if kind.is_trivia() {
115 was_joint = false;
116 } else {
119 if was_joint {
120 res.was_joint();
121 }
122 if kind == SyntaxKind::IDENT {
123 let contextual_kind = SyntaxKind::from_contextual_keyword(self.text(i))
124 .unwrap_or(SyntaxKind::IDENT);
125 res.push_ident(contextual_kind);
126 } else {
127 res.push(kind);
128 }
129 was_joint = true;
130 }
131 }
132 res
133 }
134
135 pub fn intersperse_trivia(&self, output: &Output, sink: &mut dyn FnMut(StrStep<'_>)) -> bool {
137 let mut builder = Builder {
138 lexed: self,
139 pos: 0,
140 state: State::PendingEnter,
141 sink,
142 };
143
144 for event in output.iter() {
145 match event {
146 Step::Token {
147 kind,
148 n_input_tokens: n_raw_tokens,
149 } => builder.token(kind, n_raw_tokens),
150 Step::Enter { kind } => builder.enter(kind),
151 Step::Exit => builder.exit(),
152 Step::Error { msg } => {
153 let text_pos = builder.lexed.text_start(builder.pos);
154 (builder.sink)(StrStep::Error { msg, pos: text_pos });
155 }
156 }
157 }
158
159 match mem::replace(&mut builder.state, State::Normal) {
160 State::PendingExit => {
161 builder.eat_trivias();
162 (builder.sink)(StrStep::Exit);
163 }
164 State::PendingEnter | State::Normal => unreachable!(),
165 }
166
167 builder.pos == builder.lexed.len()
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::LexedStr;
175 use crate::SyntaxKind;
176
177 fn kinds(text: &str) -> Vec<(SyntaxKind, SyntaxKind)> {
178 let lexed = LexedStr::new(text);
179 let input = lexed.to_input();
180 (0..text.split_whitespace().count())
181 .map(|i| (input.kind(i), input.contextual_kind(i)))
182 .collect()
183 }
184
185 #[test]
186 fn plpgsql_keywords_stay_idents_with_a_contextual_kind() {
187 assert_eq!(
188 kinds("message raise elsif"),
189 vec![
190 (SyntaxKind::IDENT, SyntaxKind::MESSAGE_KW),
191 (SyntaxKind::IDENT, SyntaxKind::RAISE_KW),
192 (SyntaxKind::IDENT, SyntaxKind::ELSIF_KW),
193 ]
194 );
195 }
196
197 #[test]
198 fn sql_keywords_are_not_contextual() {
199 assert_eq!(
200 kinds("select begin declare"),
201 vec![
202 (SyntaxKind::SELECT_KW, SyntaxKind::EOF),
203 (SyntaxKind::BEGIN_KW, SyntaxKind::EOF),
204 (SyntaxKind::DECLARE_KW, SyntaxKind::EOF),
205 ]
206 );
207 }
208
209 #[test]
210 fn plain_idents_have_no_contextual_kind() {
211 assert_eq!(
212 kinds("foo bar"),
213 vec![
214 (SyntaxKind::IDENT, SyntaxKind::IDENT),
215 (SyntaxKind::IDENT, SyntaxKind::IDENT),
216 ]
217 );
218 }
219}