glsl_lang_pp/parser/
syntax.rs

1use rowan::Checkpoint;
2
3use lang_util::{SmolStr, TextRange};
4
5use crate::lexer;
6
7use super::{ErrorKind, ExpectAny, ParserRun, SyntaxKind::*};
8
9type InputToken = lexer::Token;
10
11pub fn file(parser: &mut ParserRun) {
12    loop {
13        // We need to buffer trivia before we can detect a control line
14        parser.buffer_trivia();
15
16        if let Some(token) = parser.peek() {
17            match *token {
18                InputToken::HASH => if_section_or_control_line(parser),
19                InputToken::NEWLINE => {
20                    // If we encounter a newline (which is not trivia because of the pp), bump
21                    // it on its own and restart
22                    parser.eat_trivia();
23                    parser.bump();
24                }
25                _ => {
26                    parser.eat_trivia();
27
28                    while let Some(token) = parser.peek() {
29                        // Bump all tokens, including the newline
30                        parser.bump();
31
32                        if *token == InputToken::NEWLINE {
33                            // A newline completes the token sequence
34                            break;
35                        }
36                    }
37                }
38            }
39        } else {
40            parser.eat_trivia();
41            break;
42        }
43    }
44}
45
46pub fn define_body(parser: &mut ParserRun) {
47    // Consume trivia first
48    parser.eat_trivia();
49
50    parser.start_node(PP_DEFINE_BODY);
51    pp_tokens(parser);
52    parser.finish_node();
53
54    // Finish eating trivia
55    parser.eat_trivia();
56}
57
58/// Parse a control line
59fn if_section_or_control_line(parser: &mut ParserRun) {
60    let checkpoint = parser.checkpoint();
61
62    parser.eat_trivia();
63
64    // #
65    parser.bump();
66
67    // Whitespace
68    parser.skip_trivia();
69
70    // Find directive type
71    let mut pp_type_name = None;
72    let pp_type = if let Some(token) = parser.peek() {
73        if *token == InputToken::NEWLINE {
74            // Empty, don't bump newline
75            Some(PP_EMPTY)
76        } else {
77            let mut error = None;
78            let name = SmolStr::from(parser.text(token));
79            pp_type_name = Some(name.clone());
80            let result = match name.as_ref() {
81                "include" => {
82                    parser.bump();
83                    pp_include(parser);
84                    Some(PP_INCLUDE)
85                }
86                "define" => {
87                    parser.bump();
88                    pp_define(parser);
89                    Some(PP_DEFINE)
90                }
91                "undef" => {
92                    parser.bump();
93                    pp_if_ident(parser);
94                    Some(PP_UNDEF)
95                }
96                "line" => {
97                    parser.bump();
98                    pp_line(parser);
99                    Some(PP_LINE)
100                }
101                "error" => {
102                    parser.bump();
103                    pp_error(parser);
104                    Some(PP_ERROR)
105                }
106                "pragma" => {
107                    parser.bump();
108                    pp_pragma(parser);
109                    Some(PP_PRAGMA)
110                }
111                "version" => {
112                    parser.bump();
113                    pp_version(parser);
114                    Some(PP_VERSION)
115                }
116                "if" => {
117                    parser.bump();
118                    pp_if_expr(parser);
119                    Some(PP_IF)
120                }
121                "ifdef" => {
122                    parser.bump();
123                    pp_if_ident(parser);
124                    Some(PP_IFDEF)
125                }
126                "ifndef" => {
127                    parser.bump();
128                    pp_if_ident(parser);
129                    Some(PP_IFNDEF)
130                }
131                "elif" => {
132                    parser.bump();
133                    // Also parses an expr
134                    pp_if_expr(parser);
135                    Some(PP_ELIF)
136                }
137                "else" => {
138                    parser.bump();
139                    // Nothing to parse
140                    Some(PP_ELSE)
141                }
142                "endif" => {
143                    parser.bump();
144                    // Nothing to parse
145                    Some(PP_ENDIF)
146                }
147                "extension" => {
148                    parser.bump();
149                    pp_extension(parser);
150                    Some(PP_EXTENSION)
151                }
152                "(" => {
153                    parser.bump();
154                    match pp_rs_ident(parser, token) {
155                        Ok(()) => {
156                            // Abort parsing a control directive, we parsed an rs_ident
157                            return;
158                        }
159                        Err(err) => {
160                            error = err.into();
161                        }
162                    }
163                    None
164                }
165                other => {
166                    error = Some((
167                        ErrorKind::UnknownPreprocessorDirective { name: other.into() },
168                        token.range,
169                    ));
170                    None
171                }
172            };
173
174            if let Some(error) = error {
175                parser.push_error(error.0, error.1);
176            }
177
178            result
179        }
180    } else {
181        None
182    };
183
184    // Consume trivia before checking we're at a newline
185    parser.skip_trivia();
186
187    // Consume the newline, or EOI
188    match parser.peek() {
189        Some(token) if *token == InputToken::NEWLINE => {
190            parser.bump();
191        }
192        None => {
193            // Nothing to bump here
194        }
195        Some(other) => {
196            // Anything else is an error
197            let mut start = other.range;
198
199            // Bump until newline into an ERROR
200            parser.start_node(ERROR);
201
202            while let Some(token) = parser.peek() {
203                // Break when we encounter the newline
204                if *token == InputToken::NEWLINE {
205                    break;
206                }
207
208                // Else, extend the range
209                start = TextRange::new(start.start(), token.range.end());
210                parser.bump();
211            }
212
213            // Finish the error node
214            parser.finish_node();
215
216            // Bump the remaining newline
217            if let Some(InputToken::NEWLINE) = parser.peek().as_deref() {
218                parser.bump();
219            }
220
221            // Note the error, unless it's an unknown directive: it's unknown, so don't notify an
222            // error twice
223            if pp_type.is_some() {
224                parser.push_error(
225                    ErrorKind::ExtraTokensInPreprocessorDirective {
226                        name: pp_type_name.unwrap(),
227                    },
228                    start,
229                );
230            }
231        }
232    }
233
234    // Finish parsing
235    match pp_type {
236        Some(t) => {
237            parser.start_node_at(checkpoint, t);
238            parser.finish_node();
239        }
240        None => {
241            parser.start_node_at(checkpoint, ERROR);
242            parser.finish_node();
243        }
244    }
245}
246
247fn pp_rs_ident(
248    parser: &mut ParserRun,
249    token: lexer::TextToken,
250) -> Result<(), (ErrorKind, TextRange)> {
251    let mut level = 1;
252    while level > 0 {
253        // Skip whitespace
254        parser.skip_trivia();
255
256        let peeked = parser.peek().map(|tt| SmolStr::from(parser.text(tt)));
257        match peeked.as_deref() {
258            Some("(") => {
259                level += 1;
260                parser.bump();
261            }
262            Some(")") => {
263                level -= 1;
264                parser.bump();
265            }
266            Some(_) => {
267                parser.bump();
268            }
269            None => {
270                return Err((
271                    ErrorKind::EndOfInput {
272                        expected: Box::new([lexer::Token::RPAREN]),
273                    },
274                    token.range,
275                ));
276            }
277        }
278    }
279
280    Ok(())
281}
282
283fn pp_include(parser: &mut ParserRun) {
284    // We're about to parse a path
285    parser.input.set_expect_angle_string(true);
286
287    parser.skip_trivia();
288
289    // Consume include path
290    parser.start_node(PP_INCLUDE_PATH);
291    pp_tokens(parser);
292    parser.finish_node();
293
294    parser.eat_trivia();
295}
296
297fn pp_define(parser: &mut ParserRun) {
298    parser.skip_trivia();
299
300    // Define name
301    ident(parser);
302
303    // Is it a function define or an object one?
304    if let Some(InputToken::LPAREN) = parser.peek().as_deref() {
305        // Immediate LPAREN: function-like
306        let mut checkpoint = Some(parser.checkpoint());
307
308        // Bump LPAREN
309        parser.bump();
310
311        // Read ident, comma sequence
312        loop {
313            parser.skip_trivia();
314
315            let arg_checkpoint = parser.checkpoint();
316            match parser.expect_any(
317                &[InputToken::IDENT_KW, InputToken::RPAREN],
318                &[InputToken::NEWLINE],
319            ) {
320                ExpectAny::Found(found) => {
321                    match *found {
322                        InputToken::IDENT_KW => {
323                            // Ident already bumped by expect_any
324                            parser.start_node_at(arg_checkpoint, PP_DEFINE_ARG);
325                            parser.finish_node();
326                        }
327                        InputToken::RPAREN => {
328                            // We're done, LPAREN followed by RPAREN is an empty arg list
329                            break;
330                        }
331                        _ => {
332                            unreachable!()
333                        }
334                    }
335                }
336
337                ExpectAny::Unexpected(other) => {
338                    // Something else, propagate error
339                    if let Some(checkpoint) = checkpoint.take() {
340                        parser.start_node_at(checkpoint, ERROR);
341                    }
342
343                    if *other == InputToken::NEWLINE {
344                        break;
345                    }
346                }
347
348                ExpectAny::EndOfInput => {
349                    if let Some(checkpoint) = checkpoint.take() {
350                        parser.start_node_at(checkpoint, ERROR);
351                    }
352
353                    break;
354                }
355            }
356
357            parser.skip_trivia();
358
359            match parser.expect_any(
360                &[InputToken::COMMA, InputToken::RPAREN],
361                &[InputToken::NEWLINE],
362            ) {
363                ExpectAny::Found(found) => {
364                    match *found {
365                        InputToken::COMMA => {
366                            // More identifiers to come
367                        }
368                        InputToken::RPAREN => {
369                            // We're done
370                            break;
371                        }
372                        _ => {
373                            unreachable!()
374                        }
375                    }
376                }
377
378                ExpectAny::Unexpected(other) => {
379                    // Something else, propagate error
380                    if let Some(checkpoint) = checkpoint.take() {
381                        parser.start_node_at(checkpoint, ERROR);
382                    }
383
384                    if *other == InputToken::NEWLINE {
385                        break;
386                    }
387                }
388
389                ExpectAny::EndOfInput => {
390                    if let Some(checkpoint) = checkpoint.take() {
391                        parser.start_node_at(checkpoint, ERROR);
392                    }
393
394                    break;
395                }
396            }
397        }
398
399        // Finish the checkpointed node
400        if let Some(checkpoint) = checkpoint.take() {
401            parser.start_node_at(checkpoint, PP_DEFINE_ARGS);
402        }
403
404        parser.finish_node();
405    } else {
406        // Something else: object-like
407    }
408
409    // Skip trivia after args/object-like name
410    parser.skip_trivia();
411
412    // Consume define body
413    parser.start_node(PP_DEFINE_BODY);
414    pp_tokens(parser);
415    parser.finish_node();
416
417    // Finish eating trivia, not part of body
418    parser.eat_trivia();
419}
420
421fn pp_line(parser: &mut ParserRun) {
422    parser.skip_trivia();
423
424    // Consume line body
425    parser.start_node(PP_LINE_BODY);
426    pp_tokens(parser);
427    parser.finish_node();
428
429    // Finish eating trivia, not part of body
430    parser.eat_trivia();
431}
432
433fn pp_error(parser: &mut ParserRun) {
434    parser.skip_trivia();
435
436    // Consume define body
437    parser.start_node(PP_ERROR_BODY);
438    pp_tokens(parser);
439    parser.finish_node();
440
441    // Finish eating trivia, not part of body
442    parser.eat_trivia();
443}
444
445fn pp_pragma(parser: &mut ParserRun) {
446    parser.skip_trivia();
447
448    // Consume define body
449    parser.start_node(PP_PRAGMA_BODY);
450    pp_tokens(parser);
451    parser.finish_node();
452
453    // Finish eating trivia, not part of body
454    parser.eat_trivia();
455}
456
457fn pp_version(parser: &mut ParserRun) {
458    parser.skip_trivia();
459
460    // Version
461    parser.start_node(PP_VERSION_NUMBER);
462    digits(parser);
463    parser.finish_node();
464
465    parser.skip_trivia();
466
467    // Profile, if any
468    if let Some(InputToken::IDENT_KW) = parser.peek().as_deref() {
469        parser.start_node(PP_VERSION_PROFILE);
470        parser.bump();
471        parser.finish_node();
472    }
473}
474
475fn pp_if_expr(parser: &mut ParserRun) {
476    parser.skip_trivia();
477
478    // Consume if expr
479    // We can't parse it yet since it might need preprocessing
480    parser.start_node(PP_IF_EXPR);
481    pp_tokens(parser);
482    parser.finish_node();
483
484    parser.eat_trivia();
485}
486
487fn pp_if_ident(parser: &mut ParserRun) {
488    parser.skip_trivia();
489
490    parser.start_node(PP_IDENT);
491    ident(parser);
492    parser.finish_node();
493}
494
495fn pp_extension(parser: &mut ParserRun) {
496    parser.skip_trivia();
497
498    // Extension name
499    ident(parser);
500
501    parser.skip_trivia();
502
503    if let ExpectAny::Found(_) = parser.expect_any(&[InputToken::COLON], &[InputToken::NEWLINE]) {
504        parser.skip_trivia();
505
506        // Extension behavior
507        ident(parser);
508    } else {
509        // Let the main pp parser deal with the error
510    }
511}
512
513fn digits(parser: &mut ParserRun) {
514    let checkpoint = parser.checkpoint();
515
516    match parser.expect_one(InputToken::DIGITS) {
517        ExpectAny::Found(_) => {}
518        ExpectAny::Unexpected(_) | ExpectAny::EndOfInput => {
519            parser.start_node_at(checkpoint, ERROR);
520            parser.finish_node();
521        }
522    }
523}
524
525fn ident(parser: &mut ParserRun) {
526    let checkpoint = parser.checkpoint();
527
528    match parser.expect_one(InputToken::IDENT_KW) {
529        ExpectAny::Found(_) => {}
530        ExpectAny::Unexpected(_) | ExpectAny::EndOfInput => {
531            parser.start_node_at(checkpoint, ERROR);
532            parser.finish_node();
533        }
534    }
535}
536
537fn pp_concat(parser: &mut ParserRun, checkpoint: Checkpoint) {
538    // Start the concat node
539    parser.start_node_at(checkpoint, PP_CONCAT);
540
541    // We know there's a ## pending
542    parser.bump();
543
544    // Then, loop until the next non-trivial node
545    loop {
546        parser.buffer_trivia();
547
548        if let Some(current) = parser.peek() {
549            match *current {
550                InputToken::NEWLINE => {
551                    // End of directive
552                    break;
553                }
554                InputToken::PP_CONCAT => {
555                    // "nested" concatenation
556                    parser.eat_trivia();
557                    let checkpoint = parser.checkpoint();
558                    pp_concat(parser, checkpoint);
559                }
560                _ => {
561                    // Since we buffered trivia, this is a non-trivial token
562                    parser.eat_trivia();
563                    parser.bump();
564                }
565            }
566        }
567    }
568
569    // Finish the concat node
570    parser.finish_node();
571}
572
573fn pp_tokens(parser: &mut ParserRun) {
574    // The replacement body is everything until the new-line
575
576    // Checkpoint for maybe wrapping in a PP_CONCAT node
577    let mut checkpoint = parser.checkpoint();
578
579    loop {
580        // Consume all trivia first
581        parser.buffer_trivia();
582
583        // Check if there are tokens left
584        if let Some(current) = parser.peek() {
585            match *current {
586                InputToken::NEWLINE => {
587                    // Newline terminates body
588                    break;
589                }
590                InputToken::PP_CONCAT => {
591                    // Consume trivia first
592                    parser.eat_trivia();
593
594                    // ## op, turn this into a node, consuming the checkpoint
595                    let checkpoint = std::mem::replace(&mut checkpoint, parser.checkpoint());
596                    pp_concat(parser, checkpoint);
597                }
598                _ => {
599                    // Anything else: include buffered trivia in the body, and include the
600                    // new non-trivial token
601                    parser.eat_trivia();
602
603                    // Update checkpoint
604                    checkpoint = parser.checkpoint();
605
606                    parser.bump();
607                }
608            }
609        } else {
610            // TODO: EOI is an error for preprocessor directives?
611            break;
612        }
613    }
614}