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 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(); 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];