1use logos::Logos;
2use smol_str::SmolStr;
3use text_size::{TextRange, TextSize};
4
5use crate::{LexToken, SyntaxKind};
6
7#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)]
8enum RawTokenKind {
9 #[token("\u{feff}")]
10 Bom,
11 #[token("shell~=")]
12 ShellFallbackKw,
13 #[token("shell")]
14 ShellKw,
15 #[token("!")]
16 Bang,
17 #[token("#")]
18 Percent,
19 #[token(":")]
20 Colon,
21 #[token("?")]
22 Question,
23 #[token("&")]
24 Amp,
25 #[token("=")]
26 Eq,
27 #[token("@")]
28 At,
29 #[token("(")]
30 LParen,
31 #[token(")")]
32 RParen,
33 #[token("[")]
34 LBracket,
35 #[token("]")]
36 RBracket,
37 #[token("{")]
38 LBrace,
39 #[token("}")]
40 RBrace,
41 #[regex(r#""([^"\n]|\\.)*""#)]
42 String,
43 #[regex(r"[A-Za-z_-][A-Za-z0-9_-]*")]
44 Ident,
45 #[regex(r"//[^\n]*", allow_greedy = true)]
46 Comment,
47 #[regex(r"[ \t]+")]
48 Spaces,
49 #[regex(r"\r\n|\n|\r")]
50 Newline,
51}
52
53pub fn lex(source: &str) -> Vec<LexToken> {
61 let mut lexer = RawTokenKind::lexer(source);
62 let mut tokens = Vec::new();
63 let mut line_start = true;
64
65 while let Some(result) = lexer.next() {
66 let span = lexer.span();
67 let text = &source[span.clone()];
68 let start = TextSize::from(span.start as u32);
69 let end = TextSize::from(span.end as u32);
70 let kind = match result {
71 Ok(RawTokenKind::Bom) if span.start == 0 => SyntaxKind::Bom,
72 Ok(RawTokenKind::Bom) => SyntaxKind::Unknown,
73 Ok(RawTokenKind::ShellFallbackKw) => SyntaxKind::ShellFallbackKw,
74 Ok(RawTokenKind::ShellKw) => SyntaxKind::ShellKw,
75 Ok(RawTokenKind::Bang) => SyntaxKind::Bang,
76 Ok(RawTokenKind::Percent) => SyntaxKind::Percent,
77 Ok(RawTokenKind::Colon) => SyntaxKind::Colon,
78 Ok(RawTokenKind::Question) => SyntaxKind::Question,
79 Ok(RawTokenKind::Amp) => SyntaxKind::Amp,
80 Ok(RawTokenKind::Eq) => SyntaxKind::Eq,
81 Ok(RawTokenKind::At) => SyntaxKind::At,
82 Ok(RawTokenKind::LParen) => SyntaxKind::LParen,
83 Ok(RawTokenKind::RParen) => SyntaxKind::RParen,
84 Ok(RawTokenKind::LBracket) => SyntaxKind::LBracket,
85 Ok(RawTokenKind::RBracket) => SyntaxKind::RBracket,
86 Ok(RawTokenKind::LBrace) => SyntaxKind::LBrace,
87 Ok(RawTokenKind::RBrace) => SyntaxKind::RBrace,
88 Ok(RawTokenKind::String) => SyntaxKind::String,
89 Ok(RawTokenKind::Ident) => SyntaxKind::Ident,
90 Ok(RawTokenKind::Comment) => SyntaxKind::Comment,
91 Ok(RawTokenKind::Newline) => SyntaxKind::Newline,
92 Ok(RawTokenKind::Spaces) if line_start => SyntaxKind::Indent,
93 Ok(RawTokenKind::Spaces) => SyntaxKind::Whitespace,
94 Err(_) => SyntaxKind::Unknown,
95 };
96
97 tokens.push(LexToken {
98 kind,
99 text: SmolStr::new(text),
100 range: TextRange::new(start, end),
101 });
102
103 line_start = kind == SyntaxKind::Newline;
104 }
105
106 let eof = TextSize::from(source.len() as u32);
107 tokens.push(LexToken {
108 kind: SyntaxKind::Eof,
109 text: SmolStr::new(""),
110 range: TextRange::new(eof, eof),
111 });
112
113 tokens
114}