glsl_lang_pp/lexer/
glue.rs

1//! Last stage lexer declaration
2
3use arrayvec::ArrayVec;
4
5use crate::{
6    lexer::{PreLexer, PreTextToken, PreToken as InputToken},
7    util::LineMap,
8};
9
10mod token;
11use lang_util::TextRange;
12pub use token::Token;
13
14pub type TextToken = crate::util::TextToken<token::Token>;
15
16/// Final stage lexer.
17///
18/// This lexer wraps earlier stages and glues punctuation together to form (longest)
19/// multi-character operator tokens. This is the entry point for lexing a pre-processor token
20/// stream for the GLSL language.
21#[derive(Debug, Clone)]
22pub struct Lexer<'i> {
23    input: PreLexer<'i>,
24    /// Unglued token buffer. Since we're pasting at most 3 tokens together, and we always return
25    /// one, we only need space for storing 2 pending tokens.
26    buffer: ArrayVec<PreTextToken, 2>,
27}
28
29impl<'i> Lexer<'i> {
30    pub fn new(input: &'i str) -> Self {
31        Self {
32            input: PreLexer::new(input),
33            buffer: ArrayVec::new(),
34        }
35    }
36
37    /// Get a reference to the input slice
38    pub fn input(&self) -> &'i str {
39        self.input.input()
40    }
41
42    /// Get a reference to the line map
43    pub fn line_map(&self) -> &LineMap {
44        self.input.line_map()
45    }
46
47    /// Consume this lexer and return the line map
48    pub fn into_line_map(self) -> LineMap {
49        self.input.into_line_map()
50    }
51
52    /// Notify the lexer we are parsing an #include directive, and it should expect the next `<`
53    /// token to start an angle-quoted string.
54    ///
55    /// # Parameters
56    ///
57    /// * `expect_angle_string`: true if the lexer should expect a string, false otherwise
58    pub fn set_expect_angle_string(&mut self, expect_angle_string: bool) {
59        self.input.set_expect_angle_string(expect_angle_string)
60    }
61
62    fn next(&mut self) -> Option<PreTextToken> {
63        self.buffer.pop().or_else(|| self.input.next())
64    }
65
66    fn maybe_concat(
67        &mut self,
68        token: PreTextToken,
69        next: impl FnOnce(InputToken) -> Option<Token>,
70    ) -> Option<TextToken> {
71        if let Some(next_token) = self.next() {
72            let result = next(*next_token);
73
74            if let Some(result) = result {
75                return Some(TextToken::new(
76                    result,
77                    TextRange::new(token.range.start(), next_token.range.end()),
78                ));
79            } else {
80                // Put the token back into the buffer, it couldn't be combined
81                self.buffer.push(next_token);
82            }
83        }
84
85        Some(token.transmute())
86    }
87
88    fn maybe_concat2(
89        &mut self,
90        token: PreTextToken,
91        next: impl FnOnce(InputToken) -> Option<Token>,
92        after: impl FnOnce((InputToken, InputToken)) -> Option<Token>,
93    ) -> Option<TextToken> {
94        if let Some(next_token) = self.next() {
95            // We have a 2nd token
96
97            if let Some(after_token) = self.next() {
98                // We have a 3rd token, try to combine the 3
99                let result = after((*next_token, *after_token));
100
101                if let Some(result) = result {
102                    // We combined three tokens
103                    return Some(TextToken::new(
104                        result,
105                        TextRange::new(token.range.start(), after_token.range.end()),
106                    ));
107                } else {
108                    // We failed to combine three tokens, try to combine two and buffer the rest
109                    self.buffer.push(after_token);
110                    self.buffer.push(next_token);
111
112                    return self.maybe_concat(token, next);
113                }
114            } else {
115                // End of input, only 2 tokens
116                // Push the token back into the buffer so maybe_concat can use it
117                self.buffer.push(next_token);
118                return self.maybe_concat(token, next);
119            }
120        }
121
122        Some(token.transmute())
123    }
124}
125
126impl<'i> Iterator for Lexer<'i> {
127    type Item = TextToken;
128
129    fn next(&mut self) -> Option<Self::Item> {
130        use Token::*;
131
132        let token = self.next();
133        match token {
134            Some(token) => match *token {
135                InputToken::PLUS => self.maybe_concat(token, |input| match input {
136                    InputToken::PLUS => Some(INC_OP),
137                    InputToken::EQUAL => Some(ADD_ASSIGN),
138                    _ => None,
139                }),
140                InputToken::DASH => self.maybe_concat(token, |input| match input {
141                    InputToken::DASH => Some(DEC_OP),
142                    InputToken::EQUAL => Some(SUB_ASSIGN),
143                    _ => None,
144                }),
145                InputToken::SLASH => self.maybe_concat(token, |input| match input {
146                    InputToken::EQUAL => Some(DIV_ASSIGN),
147                    _ => None,
148                }),
149                InputToken::ASTERISK => self.maybe_concat(token, |input| match input {
150                    InputToken::EQUAL => Some(MUL_ASSIGN),
151                    _ => None,
152                }),
153                InputToken::PERCENT => self.maybe_concat(token, |input| match input {
154                    InputToken::EQUAL => Some(MOD_ASSIGN),
155                    _ => None,
156                }),
157                InputToken::LANGLE => self.maybe_concat2(
158                    token,
159                    |input| match input {
160                        InputToken::LANGLE => Some(LEFT_OP),
161                        InputToken::EQUAL => Some(LE_OP),
162                        _ => None,
163                    },
164                    |input| match input {
165                        (InputToken::LANGLE, InputToken::EQUAL) => Some(LEFT_ASSIGN),
166                        _ => None,
167                    },
168                ),
169                InputToken::RANGLE => self.maybe_concat2(
170                    token,
171                    |input| match input {
172                        InputToken::RANGLE => Some(RIGHT_OP),
173                        InputToken::EQUAL => Some(GE_OP),
174                        _ => None,
175                    },
176                    |input| match input {
177                        (InputToken::RANGLE, InputToken::EQUAL) => Some(RIGHT_ASSIGN),
178                        _ => None,
179                    },
180                ),
181                InputToken::CARET => self.maybe_concat(token, |input| match input {
182                    InputToken::CARET => Some(XOR_OP),
183                    InputToken::EQUAL => Some(XOR_ASSIGN),
184                    _ => None,
185                }),
186                InputToken::BAR => self.maybe_concat(token, |input| match input {
187                    InputToken::BAR => Some(OR_OP),
188                    InputToken::EQUAL => Some(OR_ASSIGN),
189                    _ => None,
190                }),
191                InputToken::AMPERSAND => self.maybe_concat(token, |input| match input {
192                    InputToken::AMPERSAND => Some(AND_OP),
193                    InputToken::EQUAL => Some(AND_ASSIGN),
194                    _ => None,
195                }),
196                InputToken::EQUAL => self.maybe_concat(token, |input| match input {
197                    InputToken::EQUAL => Some(EQ_OP),
198                    _ => None,
199                }),
200                InputToken::BANG => self.maybe_concat(token, |input| match input {
201                    InputToken::EQUAL => Some(NE_OP),
202                    _ => None,
203                }),
204                InputToken::HASH => self.maybe_concat(token, |input| match input {
205                    InputToken::HASH => Some(PP_CONCAT),
206                    _ => None,
207                }),
208                InputToken::PERIOD => self.maybe_concat(token, |input| match input {
209                    InputToken::DIGITS => Some(DIGITS),
210                    _ => None,
211                }),
212                _ => Some(token.transmute()),
213            },
214            None => None,
215        }
216    }
217}