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