gml_log_parser/
lexer.rs

1use chompy::{
2    lex::{CharStream, Lex, LexError, Tok},
3    utils::*,
4};
5
6use crate::TokKind;
7
8pub struct Lexer {
9    source: &'static str,
10    char_stream: CharStream,
11    file_id: usize,
12}
13
14impl Lexer {
15    /// Creates a new Lexer, taking a string of fog source.
16    pub fn new(source: &'static str, file_id: FileId) -> Self {
17        Self {
18            source,
19            char_stream: CharStream::new(source),
20            file_id,
21        }
22    }
23}
24
25impl Iterator for Lexer {
26    type Item = std::result::Result<Tok<TokKind>, LexError>;
27
28    fn next(&mut self) -> Option<Self::Item> {
29        match self.lex() {
30            Ok(Some(item)) => Some(Ok(item)),
31            Err(e) => Some(Err(e)),
32            _ => None,
33        }
34    }
35}
36
37impl Lex<Tok<TokKind>, TokKind> for Lexer {
38    fn source(&self) -> &'static str {
39        self.source
40    }
41
42    fn file_id(&self) -> FileId {
43        self.file_id
44    }
45
46    fn char_stream(&mut self) -> &mut CharStream {
47        &mut self.char_stream
48    }
49
50    fn construct_ident(&mut self) -> Option<&'static str> {
51        let test = |c: char| -> bool { c.is_alphanumeric() };
52        self.char_stream()
53            .match_peek_with(test)
54            .then(|| self.construct(test))
55    }
56
57    fn lex(&mut self) -> std::result::Result<Option<Tok<TokKind>>, LexError> {
58        let start_pos = self.char_stream.position();
59
60        let kind = if let Some(ident) = self.construct_ident() {
61            match ident {
62                "gml" if self.chomp_pattern("_Object_") => TokKind::ObjectMarker,
63                "gml" if self.chomp_pattern("_Script_") => TokKind::ScriptMarker,
64                lexeme
65                    if GML_EVENTS.contains(&lexeme)
66                        && self.char_stream.match_peek('_')
67                        && self.char_stream.peek_while(|v| v.is_numeric()) =>
68                {
69                    self.char_stream.chomp_peeks();
70                    TokKind::EventName(
71                        self.char_stream
72                            .slice(start_pos..self.char_stream.position()),
73                    )
74                }
75                lexeme => TokKind::Ident(lexeme),
76            }
77        } else {
78            let Some(chr) = self.chomp() else {
79                return Ok(None);
80            };
81            match chr {
82                '_' => TokKind::Underscore,
83                ':' => {
84                    let number = self.construct_integer(false).unwrap();
85                    TokKind::LineNumber(number as u64)
86                }
87                '(' if self.chomp_pattern("line ") => {
88                    let number = self.construct_integer(false).unwrap();
89                    self.chomp(); // )
90                    TokKind::LineNumber(number as u64)
91                }
92                chr if chr.is_whitespace() => return self.lex(),
93                invalid => TokKind::Invalid(invalid),
94            }
95        };
96
97        Ok(Some(Tok::new(
98            kind,
99            Location::new(
100                self.file_id,
101                Span::new(start_pos, self.char_stream.position()),
102            ),
103        )))
104    }
105}
106
107const GML_EVENTS: &[&str] = &[
108    "Alarm",
109    "CleanUp",
110    "Create",
111    "Destroy",
112    "Draw",
113    "Gesture",
114    "Keyboard",
115    "KeyRelease",
116    "Other",
117    "Step",
118];