1use logos::{Logos, SpannedIter};
2
3use std::error::Error;
4
5#[derive(Debug)]
24pub struct ParseError;
25
26impl std::fmt::Display for ParseError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "ParseError")
29 }
30}
31
32impl Error for ParseError {}
33
34fn remove_quotes(lex: &mut logos::Lexer<Token>) -> String {
35 let slice = lex.slice();
36
37 if slice.len() <= 2 {
38 return String::new();
39 }
40
41 let quote = &slice[..1];
42
43 let escaped_quote_pattern = format!("\\{}", quote);
44 let escaped_newline_pattern = "\\n";
45
46 slice[1..slice.len() - 1]
47 .replace(&escaped_quote_pattern, quote)
48 .replace(escaped_newline_pattern, "\n")
49}
50
51#[derive(Logos, Debug, PartialEq, Clone)]
52pub enum Token {
53 #[token("=")]
54 Eq,
55 #[token("export")]
56 Export,
57 #[regex(r"\n\s*")]
58 NewLine,
59 #[regex(r#"[^\s='`"\#]+"#, |lex| lex.slice().parse())]
60 Ident(String),
61 #[regex(r"'[^']*'", remove_quotes)]
62 #[regex(r"`[^`]*`", remove_quotes)]
63 #[regex(r#""([^"]|\\")*""#, remove_quotes)]
64 QuotedString(String),
65 Eof,
66 #[error]
67 #[regex(r"#.*", logos::skip)]
68 #[regex(r"\s+", logos::skip)]
69 Error,
70}
71
72pub(crate) struct Lexer<'input> {
73 token_stream: SpannedIter<'input, Token>,
74 finished: bool,
75}
76
77impl<'input> Lexer<'input> {
78 pub fn new(input: &'input str) -> Self {
79 Self {
80 token_stream: Token::lexer(input).spanned(),
81 finished: false,
82 }
83 }
84
85 fn finish(&mut self) {
86 self.finished = true;
87 }
88
89 fn finished(&self) -> bool {
90 self.finished
91 }
92}
93
94impl<'input> Iterator for Lexer<'input> {
95 type Item = Result<(usize, Token, usize), ParseError>;
96
97 fn next(&mut self) -> Option<Self::Item> {
98 match self.token_stream.next() {
99 Some((token, span)) => match token {
100 Token::Error => Some(Err(ParseError)),
101 _ => Some(Ok((span.start, token, span.end))),
102 },
103 None => {
104 if self.finished() {
105 None
106 } else {
107 self.finish();
108 Some(Ok((0, Token::Eof, 0)))
109 }
110 }
111 }
112 }
113}