1use std::ops::Range;
4
5use logos::Logos;
6
7use crate::keywords::keyword_kind;
8use crate::syntax_kind::SyntaxKind;
9
10#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct LexedSource<'a> {
19 source: &'a str,
20 tokens: Vec<Token<'a>>,
21}
22
23impl<'a> LexedSource<'a> {
24 pub fn as_str(&self) -> &'a str {
26 self.source
27 }
28
29 pub fn tokens(&self) -> &[Token<'a>] {
31 &self.tokens
32 }
33
34 pub fn iter(&self) -> impl Iterator<Item = &Token<'a>> {
36 self.tokens.iter()
37 }
38
39 pub fn into_tokens(self) -> Vec<Token<'a>> {
41 self.tokens
42 }
43
44 pub fn len(&self) -> usize {
46 self.tokens.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.tokens.is_empty()
52 }
53}
54
55impl<'a> IntoIterator for LexedSource<'a> {
56 type IntoIter = std::vec::IntoIter<Token<'a>>;
57 type Item = Token<'a>;
58
59 fn into_iter(self) -> Self::IntoIter {
60 self.tokens.into_iter()
61 }
62}
63
64impl<'a, 's> IntoIterator for &'s LexedSource<'a> {
65 type IntoIter = std::slice::Iter<'s, Token<'a>>;
66 type Item = &'s Token<'a>;
67
68 fn into_iter(self) -> Self::IntoIter {
69 self.tokens.iter()
70 }
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
78pub struct Token<'a> {
79 kind: SyntaxKind,
80 text: &'a str,
81 range: Range<usize>,
82}
83
84impl<'a> Token<'a> {
85 pub fn kind(&self) -> SyntaxKind {
87 self.kind
88 }
89
90 pub fn text(&self) -> &'a str {
92 self.text
93 }
94
95 pub fn range(&self) -> Range<usize> {
97 self.range.clone()
98 }
99}
100
101#[derive(Clone, Copy, Debug, Logos, PartialEq)]
102enum RawTokenKind {
103 #[regex(r"[ \t\n\r]+")]
104 Whitespace,
105 #[regex(r#""([^"\\]|\\[\s\S])*""#)]
108 #[regex(r#"'([^'\\]|\\[\s\S])*'"#)]
109 String,
110 #[regex(r#""([^"\\\n\r]|\\[^\n\r])*"#, unterminated_string)]
111 #[regex(r#"'([^'\\\n\r]|\\[^\n\r])*"#, unterminated_string)]
112 UnterminatedString,
113 #[regex(r"//[^\n\r]*", allow_greedy = true)]
114 LineComment,
115 #[regex(r"/\*", block_comment)]
116 BlockComment,
117 #[regex(r"[0-9]+(\.[0-9]+)?_?(mm|cm|m|inch|in|ft|yd|deg|rad|\?)?")]
118 #[regex(r"\.[0-9]+_?(mm|cm|m|inch|in|ft|yd|deg|rad|\?)?")]
119 Number,
120 #[token("..<")]
121 DoublePeriodLessThan,
122 #[token("..")]
123 DoublePeriod,
124 #[token("::")]
125 DoubleColon,
126 #[regex(r"[\p{Alphabetic}_][\p{Alphabetic}0-9_]*")]
127 Word,
128 #[token(">=")]
129 GtEq,
130 #[token("<=")]
131 LtEq,
132 #[token("==")]
133 EqEq,
134 #[token("=>")]
135 FatArrow,
136 #[token("!=")]
137 BangEq,
138 #[token("|>")]
139 PipeGt,
140 #[token("*")]
141 Star,
142 #[token("+")]
143 Plus,
144 #[token("-")]
145 Minus,
146 #[token("/")]
147 Slash,
148 #[token("%")]
149 Percent,
150 #[token("=")]
151 Eq,
152 #[token("<")]
153 Lt,
154 #[token(">")]
155 Gt,
156 #[token("\\")]
157 Backslash,
158 #[token("^")]
159 Caret,
160 #[token("||")]
161 PipePipe,
162 #[token("&&")]
163 AmpAmp,
164 #[token("|")]
165 Pipe,
166 #[token("&")]
167 Amp,
168 #[token("(")]
169 OpenParen,
170 #[token(")")]
171 CloseParen,
172 #[token("{")]
173 OpenBrace,
174 #[token("}")]
175 CloseBrace,
176 #[token("[")]
177 OpenBracket,
178 #[token("]")]
179 CloseBracket,
180 #[token("#")]
181 Hash,
182 #[token("!")]
183 Bang,
184 #[token("$")]
185 Dollar,
186 #[token(",")]
187 Comma,
188 #[token(":")]
189 Colon,
190 #[token(".")]
191 Period,
192 #[token("?")]
193 QuestionMark,
194 #[token("@")]
195 At,
196 #[token(";")]
197 SemiColon,
198}
199
200pub fn lex(source: &str) -> LexedSource<'_> {
205 let mut lexer = RawTokenKind::lexer(source);
206 let mut tokens = Vec::new();
207
208 while let Some(raw_kind) = lexer.next() {
209 let range = lexer.span();
210 let text = &source[range.clone()];
211 let kind = match raw_kind {
212 Ok(RawTokenKind::Whitespace) => SyntaxKind::Whitespace,
213 Ok(RawTokenKind::String) => SyntaxKind::String,
214 Ok(RawTokenKind::UnterminatedString) => SyntaxKind::UnterminatedString,
215 Ok(RawTokenKind::LineComment) => SyntaxKind::LineComment,
216 Ok(RawTokenKind::BlockComment) if text.ends_with("*/") => SyntaxKind::BlockComment,
217 Ok(RawTokenKind::BlockComment) => SyntaxKind::UnterminatedBlockComment,
218 Ok(RawTokenKind::Number) => SyntaxKind::Number,
219 Ok(RawTokenKind::DoublePeriodLessThan) => SyntaxKind::DoublePeriodLessThan,
220 Ok(RawTokenKind::DoublePeriod) => SyntaxKind::DoublePeriod,
221 Ok(RawTokenKind::DoubleColon) => SyntaxKind::DoubleColon,
222 Ok(RawTokenKind::Word) => keyword_kind(text).unwrap_or(SyntaxKind::Word),
223 Ok(RawTokenKind::GtEq) => SyntaxKind::GtEq,
224 Ok(RawTokenKind::LtEq) => SyntaxKind::LtEq,
225 Ok(RawTokenKind::EqEq) => SyntaxKind::EqEq,
226 Ok(RawTokenKind::FatArrow) => SyntaxKind::FatArrow,
227 Ok(RawTokenKind::BangEq) => SyntaxKind::BangEq,
228 Ok(RawTokenKind::PipeGt) => SyntaxKind::PipeGt,
229 Ok(RawTokenKind::Star) => SyntaxKind::Star,
230 Ok(RawTokenKind::Plus) => SyntaxKind::Plus,
231 Ok(RawTokenKind::Minus) => SyntaxKind::Minus,
232 Ok(RawTokenKind::Slash) => SyntaxKind::Slash,
233 Ok(RawTokenKind::Percent) => SyntaxKind::Percent,
234 Ok(RawTokenKind::Eq) => SyntaxKind::Eq,
235 Ok(RawTokenKind::Lt) => SyntaxKind::Lt,
236 Ok(RawTokenKind::Gt) => SyntaxKind::Gt,
237 Ok(RawTokenKind::Backslash) => SyntaxKind::Backslash,
238 Ok(RawTokenKind::Caret) => SyntaxKind::Caret,
239 Ok(RawTokenKind::PipePipe) => SyntaxKind::PipePipe,
240 Ok(RawTokenKind::AmpAmp) => SyntaxKind::AmpAmp,
241 Ok(RawTokenKind::Pipe) => SyntaxKind::Pipe,
242 Ok(RawTokenKind::Amp) => SyntaxKind::Amp,
243 Ok(RawTokenKind::OpenParen) => SyntaxKind::OpenParen,
244 Ok(RawTokenKind::CloseParen) => SyntaxKind::CloseParen,
245 Ok(RawTokenKind::OpenBrace) => SyntaxKind::OpenBrace,
246 Ok(RawTokenKind::CloseBrace) => SyntaxKind::CloseBrace,
247 Ok(RawTokenKind::OpenBracket) => SyntaxKind::OpenBracket,
248 Ok(RawTokenKind::CloseBracket) => SyntaxKind::CloseBracket,
249 Ok(RawTokenKind::Hash) => SyntaxKind::Hash,
250 Ok(RawTokenKind::Bang) => SyntaxKind::Bang,
251 Ok(RawTokenKind::Dollar) => SyntaxKind::Dollar,
252 Ok(RawTokenKind::Comma) => SyntaxKind::Comma,
253 Ok(RawTokenKind::Colon) => SyntaxKind::Colon,
254 Ok(RawTokenKind::Period) => SyntaxKind::Period,
255 Ok(RawTokenKind::QuestionMark) => SyntaxKind::QuestionMark,
256 Ok(RawTokenKind::At) => SyntaxKind::At,
257 Ok(RawTokenKind::SemiColon) => SyntaxKind::SemiColon,
258 Err(()) => SyntaxKind::Unknown,
259 };
260 tokens.push(Token { kind, text, range });
261 }
262
263 LexedSource { source, tokens }
264}
265
266fn block_comment(lexer: &mut logos::Lexer<'_, RawTokenKind>) {
267 if let Some(end) = lexer.remainder().find("*/") {
268 lexer.bump(end + 2);
269 } else {
270 lexer.bump(lexer.remainder().len());
271 }
272}
273
274fn unterminated_string(lexer: &mut logos::Lexer<'_, RawTokenKind>) {
275 let until_line_end = lexer
276 .remainder()
277 .find(['\n', '\r'])
278 .unwrap_or_else(|| lexer.remainder().len());
279 lexer.bump(until_line_end);
280}