1use crate::token::{LexError, Lexed, Token};
4use crate::{BodyDelimiter, LexOptions};
5use sql_dialect_fmt_syntax::{Dialect, SyntaxKind};
6
7pub fn tokenize(input: &str) -> Lexed<'_> {
11 tokenize_with_options(input, LexOptions::default())
12}
13
14pub fn tokenize_with_options<'a>(input: &'a str, options: LexOptions<'_>) -> Lexed<'a> {
15 Lexer::new(input, options).run()
16}
17
18pub fn tokenize_for_dialect(input: &str, dialect: Dialect) -> Lexed<'_> {
23 tokenize_with_options(input, LexOptions::default().with_dialect(dialect))
24}
25
26struct Lexer<'a, 'cfg> {
27 input: &'a str,
28 bytes: &'a [u8],
29 options: LexOptions<'cfg>,
30 pos: usize,
31 tokens: Vec<Token<'a>>,
32 errors: Vec<LexError>,
33}
34
35impl<'a, 'cfg> Lexer<'a, 'cfg> {
36 fn new(input: &'a str, options: LexOptions<'cfg>) -> Self {
37 Lexer {
38 input,
39 bytes: input.as_bytes(),
40 options,
41 pos: 0,
42 tokens: Vec::new(),
43 errors: Vec::new(),
44 }
45 }
46
47 #[inline]
48 fn at_end(&self) -> bool {
49 self.pos >= self.bytes.len()
50 }
51
52 #[inline]
54 fn peek(&self) -> u8 {
55 if self.pos < self.bytes.len() {
56 self.bytes[self.pos]
57 } else {
58 0
59 }
60 }
61
62 #[inline]
63 fn peek_at(&self, n: usize) -> u8 {
64 let i = self.pos + n;
65 if i < self.bytes.len() {
66 self.bytes[i]
67 } else {
68 0
69 }
70 }
71
72 #[inline]
74 fn bump(&mut self) -> u8 {
75 let c = self.bytes[self.pos];
76 self.pos += 1;
77 c
78 }
79
80 #[inline]
81 fn eat_while(&mut self, mut pred: impl FnMut(u8) -> bool) {
82 while !self.at_end() && pred(self.peek()) {
83 self.pos += 1;
84 }
85 }
86
87 #[inline]
88 fn starts_with(&self, text: &str) -> bool {
89 self.bytes[self.pos..].starts_with(text.as_bytes())
90 }
91
92 #[inline]
93 fn push(&mut self, kind: SyntaxKind, start: usize) {
94 let text = &self.input[start..self.pos];
97 self.tokens.push(Token { kind, text });
98 }
99
100 #[inline]
104 fn error(&mut self, message: impl Into<String>, offset: usize) {
105 self.errors.push(LexError {
106 message: message.into(),
107 offset,
108 len: self.pos.saturating_sub(offset),
109 });
110 }
111
112 fn run(mut self) -> Lexed<'a> {
113 let dollar_quoting = self.options.dialect.supports_dollar_quoting();
116 while !self.at_end() {
117 let start = self.pos;
118 if dollar_quoting {
119 if let Some(delimiter) = self.body_delimiter_at() {
120 self.delimited_body(start, delimiter);
121 self.push(SyntaxKind::DOLLAR_STRING, start);
122 continue;
123 }
124 }
125 match self.peek() {
126 b' ' | b'\t' => {
127 self.eat_while(|c| c == b' ' || c == b'\t');
128 self.push(SyntaxKind::WHITESPACE, start);
129 }
130 b'\n' => {
131 self.pos += 1;
132 self.push(SyntaxKind::NEWLINE, start);
133 }
134 b'\r' => {
135 self.pos += 1;
136 if self.peek() == b'\n' {
137 self.pos += 1;
138 }
139 self.push(SyntaxKind::NEWLINE, start);
140 }
141 b'-' if self.peek_at(1) == b'-' => {
143 self.line_comment();
144 self.push(SyntaxKind::COMMENT, start);
145 }
146 b'/' if self.peek_at(1) == b'/' => {
147 self.line_comment();
148 self.push(SyntaxKind::COMMENT, start);
149 }
150 b'/' if self.peek_at(1) == b'*' => {
152 self.block_comment(start);
153 self.push(SyntaxKind::BLOCK_COMMENT, start);
154 }
155 b'\'' => {
156 self.string_body(start);
157 self.push(SyntaxKind::STRING, start);
158 }
159 b'"' => {
160 self.quoted_ident_body(start);
161 self.push(SyntaxKind::QUOTED_IDENT, start);
162 }
163 b'`' if self.options.dialect.supports_backtick_identifiers() => {
164 self.backtick_ident_body(start);
165 self.push(SyntaxKind::QUOTED_IDENT, start);
166 }
167 b'$' if dollar_quoting
170 && (is_ident_start(self.peek_at(1)) || self.peek_at(1).is_ascii_digit()) =>
171 {
172 self.pos += 1; self.eat_while(is_ident_continue);
174 self.push(SyntaxKind::VARIABLE, start);
175 }
176 c if c.is_ascii_digit() => self.number(start),
177 b'.' if self.peek_at(1).is_ascii_digit() => self.number(start),
179 c if is_ident_start(c) => {
180 self.eat_while(is_ident_continue);
181 self.push(SyntaxKind::IDENT, start);
182 }
183 _ => self.operator(start),
184 }
185 }
186 Lexed {
187 tokens: self.tokens,
188 errors: self.errors,
189 }
190 }
191
192 fn line_comment(&mut self) {
194 self.pos += 2;
195 while !self.at_end() {
196 let c = self.peek();
197 if c == b'\n' || c == b'\r' {
198 break;
199 }
200 self.pos += 1;
201 }
202 }
203
204 fn block_comment(&mut self, start: usize) {
206 self.pos += 2; loop {
208 if self.at_end() {
209 self.error("unterminated block comment", start);
210 break;
211 }
212 if self.peek() == b'*' && self.peek_at(1) == b'/' {
213 self.pos += 2;
214 break;
215 }
216 self.pos += 1;
217 }
218 }
219
220 fn string_body(&mut self, start: usize) {
223 self.pos += 1; loop {
225 if self.at_end() {
226 self.error("unterminated string literal", start);
227 break;
228 }
229 match self.bump() {
230 b'\\' => {
231 if !self.at_end() {
233 self.pos += 1;
234 }
235 }
236 b'\'' => {
237 if self.peek() == b'\'' {
238 self.pos += 1; } else {
240 break; }
242 }
243 _ => {}
244 }
245 }
246 }
247
248 fn quoted_ident_body(&mut self, start: usize) {
250 self.pos += 1; loop {
252 if self.at_end() {
253 self.error("unterminated quoted identifier", start);
254 break;
255 }
256 if self.bump() == b'"' {
257 if self.peek() == b'"' {
258 self.pos += 1; } else {
260 break; }
262 }
263 }
264 }
265
266 fn backtick_ident_body(&mut self, start: usize) {
268 self.pos += 1; loop {
270 if self.at_end() {
271 self.error("unterminated backtick identifier", start);
272 break;
273 }
274 if self.bump() == b'`' {
275 if self.peek() == b'`' {
276 self.pos += 1; } else {
278 break; }
280 }
281 }
282 }
283
284 fn body_delimiter_at(&self) -> Option<BodyDelimiter> {
285 self.options
286 .body_delimiters
287 .iter()
288 .copied()
289 .filter(|delimiter| {
290 !delimiter.opener.is_empty()
291 && !delimiter.closer.is_empty()
292 && self.starts_with(delimiter.opener)
293 })
294 .max_by_key(|delimiter| delimiter.opener.len())
295 }
296
297 fn delimited_body(&mut self, start: usize, delimiter: BodyDelimiter) {
299 self.pos += delimiter.opener.len();
300 loop {
301 if self.at_end() {
302 self.error(format!("unterminated {}", delimiter.name), start);
303 break;
304 }
305 if self.starts_with(delimiter.closer) {
306 self.pos += delimiter.closer.len();
307 break;
308 }
309 self.pos += 1;
310 }
311 }
312
313 fn number(&mut self, start: usize) {
315 let mut is_float = false;
316 if self.peek() == b'.' {
317 is_float = true;
319 self.pos += 1;
320 self.eat_while(|c| c.is_ascii_digit());
321 } else {
322 self.eat_while(|c| c.is_ascii_digit());
323 if self.peek() == b'.' {
324 is_float = true;
326 self.pos += 1;
327 self.eat_while(|c| c.is_ascii_digit());
328 }
329 }
330 if self.peek() == b'e' || self.peek() == b'E' {
332 let save = self.pos;
333 self.pos += 1;
334 if self.peek() == b'+' || self.peek() == b'-' {
335 self.pos += 1;
336 }
337 if self.peek().is_ascii_digit() {
338 is_float = true;
339 self.eat_while(|c| c.is_ascii_digit());
340 } else {
341 self.pos = save; }
343 }
344 self.push(
345 if is_float {
346 SyntaxKind::FLOAT_NUMBER
347 } else {
348 SyntaxKind::INT_NUMBER
349 },
350 start,
351 );
352 }
353
354 fn operator(&mut self, start: usize) {
356 let c = self.peek();
357 if c >= 0x80 {
361 if let Some(ch) = self.input[self.pos..].chars().next() {
362 self.pos += ch.len_utf8();
363 self.error(format!("unexpected character {ch:?}"), start);
364 } else {
365 self.pos += 1;
366 self.error("unexpected byte", start);
367 }
368 self.push(SyntaxKind::ERROR, start);
369 return;
370 }
371
372 self.pos += 1; let kind = match c {
374 b'(' => SyntaxKind::L_PAREN,
375 b')' => SyntaxKind::R_PAREN,
376 b'[' => SyntaxKind::L_BRACKET,
377 b']' => SyntaxKind::R_BRACKET,
378 b'{' => SyntaxKind::L_BRACE,
379 b'}' => SyntaxKind::R_BRACE,
380 b',' => SyntaxKind::COMMA,
381 b';' => SyntaxKind::SEMICOLON,
382 b'+' => SyntaxKind::PLUS,
383 b'*' => SyntaxKind::STAR,
384 b'/' => SyntaxKind::SLASH,
385 b'%' => SyntaxKind::PERCENT,
386 b'&' => SyntaxKind::AMP,
387 b'^' => SyntaxKind::CARET,
388 b'~' => SyntaxKind::TILDE,
389 b'@' if self.options.dialect.supports_stage_refs() => SyntaxKind::AT,
392 b'?' => SyntaxKind::QUESTION,
393 b'.' => SyntaxKind::DOT,
394 b'$' => SyntaxKind::DOLLAR,
395 b'=' => {
396 if self.peek() == b'>' {
397 self.pos += 1;
398 SyntaxKind::FAT_ARROW
399 } else {
400 SyntaxKind::EQ
401 }
402 }
403 b'!' => {
404 if self.peek() == b'=' {
405 self.pos += 1;
406 SyntaxKind::NEQ
407 } else {
408 self.error("unexpected '!'", start);
409 SyntaxKind::BANG
410 }
411 }
412 b'<' => {
413 if self.peek() == b'=' {
414 self.pos += 1;
415 SyntaxKind::LTE
416 } else if self.peek() == b'>' {
417 self.pos += 1;
418 SyntaxKind::NEQ
419 } else {
420 SyntaxKind::LT
421 }
422 }
423 b'>' => {
424 if self.peek() == b'=' {
425 self.pos += 1;
426 SyntaxKind::GTE
427 } else {
428 SyntaxKind::GT
429 }
430 }
431 b':' => {
432 if self.peek() == b':' {
433 self.pos += 1;
434 SyntaxKind::COLON2
435 } else if self.peek() == b'=' {
436 self.pos += 1;
437 SyntaxKind::ASSIGN
438 } else {
439 SyntaxKind::COLON
440 }
441 }
442 b'-' => {
443 if self.peek() == b'>' && self.peek_at(1) == b'>' {
444 self.pos += 2;
445 SyntaxKind::FLOW_PIPE
446 } else if self.peek() == b'>' {
447 self.pos += 1;
448 SyntaxKind::ARROW
449 } else {
450 SyntaxKind::MINUS
451 }
452 }
453 b'|' => {
454 if self.peek() == b'>' {
455 self.pos += 1;
456 SyntaxKind::PIPE_GT
457 } else if self.peek() == b'|' {
458 self.pos += 1;
459 SyntaxKind::CONCAT
460 } else {
461 SyntaxKind::PIPE
462 }
463 }
464 other => {
465 self.error(format!("unexpected character {:?}", other as char), start);
466 SyntaxKind::ERROR
467 }
468 };
469 self.push(kind, start);
470 }
471}
472
473#[inline]
474fn is_ident_start(c: u8) -> bool {
475 c.is_ascii_alphabetic() || c == b'_'
476}
477
478#[inline]
479fn is_ident_continue(c: u8) -> bool {
480 c.is_ascii_alphanumeric() || c == b'_' || c == b'$'
481}