Skip to main content

rucc_lex/
lexer.rs

1//! Phase 3: bytes to preprocessing tokens.
2//!
3//! Design: `spec/05-preprocessor.md` sections 5.1 and 5.2.
4//!
5//! The scanner is a loop over the dispatch table in [`crate::class`]. It never copies the
6//! input, never allocates for a token whose spelling is contiguous in the file, and interns
7//! identifiers as it goes rather than in a second pass, which `spec/06-lexer-and-parser.md`
8//! section 6.1 asks for so that no part of the compiler after this one compares identifier
9//! text.
10
11use rucc_base::{Interner, Symbol};
12use rucc_diag::{BytePos, Diagnostic, Span};
13use rucc_session::Std;
14
15use crate::class::{CLASS, Class, is_ident_continue};
16use crate::cursor::Cursor;
17use crate::token::{PpToken, PpTokenKind, Punct, TokenFlags};
18
19/// The dialect knobs phase 1 cares about.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct Options {
23    /// Replace trigraphs, which `-trigraphs` turns on.
24    ///
25    /// Off by default because C23 removed them, and because leaving them on means `??!`
26    /// inside a string literal silently becomes `|`, which has caught out every project that
27    /// ever wrote a question mark next to a punctuator.
28    pub trigraphs: bool,
29    /// Whether `//` starts a comment, which C99 took from C++ and which gcc offers in gnu89 as
30    /// an extension. Off means the two slashes are two punctuators, which is what `-std=c89`
31    /// says and is a syntax error wherever a real line comment was written.
32    pub line_comments: bool,
33    /// Whether `'` between digits is part of the number, which is C23 and is not a GNU
34    /// extension. Off means `1'000'000` is a number, a multi-character character constant and
35    /// another number, which is how gcc reads it before C23 and why it does not parse.
36    pub digit_separators: bool,
37}
38
39impl Options {
40    /// The defaults, which are what `-std=gnu23` implies.
41    #[must_use]
42    pub fn new() -> Options {
43        Options { trigraphs: false, line_comments: true, digit_separators: true }
44    }
45
46    /// What a dialect implies, which is the form the driver wants.
47    #[must_use]
48    pub fn for_dialect(std: Std, gnu: bool) -> Options {
49        Options {
50            trigraphs: false,
51            line_comments: std >= Std::C99 || gnu,
52            digit_separators: std >= Std::C23,
53        }
54    }
55}
56
57impl Default for Options {
58    fn default() -> Options {
59        Options::new()
60    }
61}
62
63/// A scanner over one file.
64#[derive(Debug)]
65pub struct Lexer<'a> {
66    cursor: Cursor<'a>,
67    /// Where this file begins in the flat coordinate space `rucc-diag` describes, so that a
68    /// span is comparable across files without carrying a file id.
69    file_start: BytePos,
70    at_line_start: bool,
71    leading_space: bool,
72    /// Where the token currently being scanned began, so that the first interruption in its
73    /// spelling can copy everything before itself in one go.
74    token_start: u32,
75    /// Reused between tokens. Only touched for a token whose spelling is interrupted by a
76    /// splice or a trigraph, which is a small fraction of a real file.
77    scratch: Vec<u8>,
78    /// Whether the token being scanned has needed `scratch`.
79    unclean: bool,
80    /// The dialect knobs, for the two questions phase 3 asks about a spelling.
81    options: Options,
82    /// Whether the dialect has already been told about a `//`, since gcc says it once a file
83    /// and a file written with line comments has one on every other line.
84    reported_line_comment: bool,
85    diagnostics: Vec<Diagnostic>,
86}
87
88impl<'a> Lexer<'a> {
89    /// A scanner over `src`, whose first byte sits at `file_start`.
90    #[must_use]
91    pub fn new(src: &'a [u8], file_start: BytePos, opts: Options) -> Lexer<'a> {
92        Lexer {
93            cursor: Cursor::new(src, opts.trigraphs),
94            file_start,
95            at_line_start: true,
96            leading_space: false,
97            token_start: 0,
98            scratch: Vec::new(),
99            unclean: false,
100            options: opts,
101            reported_line_comment: false,
102            diagnostics: Vec::new(),
103        }
104    }
105
106    /// Everything the scan has complained about so far.
107    #[must_use]
108    pub fn diagnostics(&self) -> &[Diagnostic] {
109        &self.diagnostics
110    }
111
112    /// Takes the diagnostics, leaving the scanner able to carry on.
113    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
114        std::mem::take(&mut self.diagnostics)
115    }
116
117    /// The next preprocessing token, or an end of file token at the end.
118    pub fn next_token(&mut self, interner: &mut Interner) -> PpToken {
119        let token = self.scan(interner);
120        self.report_loose_splices();
121        token
122    }
123
124    /// Turns the splices the cursor flagged into warnings.
125    ///
126    /// GCC warns about a backslash with whitespace before the line ending and splices anyway.
127    /// Both halves matter: a good deal of real code has a trailing space after a backslash in
128    /// a macro definition and expects it to keep working, and the space is invisible in an
129    /// editor, so the one time it does change the meaning nobody can see why.
130    fn report_loose_splices(&mut self) {
131        for at in self.cursor.take_loose_splices() {
132            let span = Span::new(self.file_start + at, self.file_start + at + 1);
133            self.diagnostics.push(Diagnostic::warning(
134                "backslash and line ending separated by whitespace",
135                span,
136            ));
137        }
138    }
139
140    fn scan(&mut self, interner: &mut Interner) -> PpToken {
141        self.skip_trivia();
142
143        let start = self.cursor.pos();
144        let flags = self.take_flags();
145
146        if self.cursor.at_end() {
147            return PpToken {
148                kind: PpTokenKind::Eof,
149                flags,
150                value: None,
151                span: Span::empty_at(self.file_start + start),
152            };
153        }
154
155        self.token_start = start;
156        self.unclean = false;
157
158        let b = self.cursor.first();
159        let kind = match CLASS[b as usize] {
160            Class::IdentStart => self.ident_or_prefixed_literal(b, start),
161            Class::Digit => self.pp_number(),
162            Class::Dot if CLASS[self.cursor.nth(1) as usize] == Class::Digit => self.pp_number(),
163            Class::Quote => self.literal(b'"', start, PpTokenKind::StringLit),
164            Class::Apostrophe => self.literal(b'\'', start, PpTokenKind::CharConst),
165            Class::Backslash => {
166                // A backslash that survived phase 2 either begins a universal character name,
167                // which is a way of spelling an identifier character, or is a stray.
168                if matches!(self.cursor.nth(1), b'u' | b'U') {
169                    self.identifier()
170                } else {
171                    self.eat();
172                    PpTokenKind::Other
173                }
174            }
175            Class::Dot | Class::Slash | Class::Punct => match self.punctuator(start, flags) {
176                Some(token) => return token,
177                None => {
178                    self.eat();
179                    PpTokenKind::Other
180                }
181            },
182            Class::Space | Class::Newline | Class::Other => {
183                self.eat();
184                PpTokenKind::Other
185            }
186        };
187
188        let end = self.cursor.pos();
189        let value = Some(self.intern_spelling(interner, kind, start, end));
190        let mut flags = flags;
191        if self.unclean {
192            flags = flags.with(TokenFlags::SPLICED);
193        }
194        let span = Span::new(self.file_start + start, self.file_start + end);
195        PpToken { kind, flags, value, span }
196    }
197
198    /// Scans a header name, which only a `#include` line can ask for.
199    ///
200    /// Returns `None` when the line does not begin with `<` or `"`, which is the computed
201    /// include case: the directive has to macro expand the line and try again. The scanner
202    /// cannot make this call itself, because `<stdio.h>` and a pair of comparisons are the
203    /// same bytes and only the directive knows which one is possible here.
204    pub fn header_name(&mut self, interner: &mut Interner) -> Option<PpToken> {
205        let token = self.scan_header_name(interner);
206        self.report_loose_splices();
207        token
208    }
209
210    fn scan_header_name(&mut self, interner: &mut Interner) -> Option<PpToken> {
211        self.skip_horizontal();
212        let start = self.cursor.pos();
213        let close = match self.cursor.first() {
214            b'<' => b'>',
215            b'"' => b'"',
216            _ => return None,
217        };
218        let flags = self.take_flags();
219        self.token_start = start;
220        self.unclean = false;
221        self.eat();
222        loop {
223            if self.cursor.at_end() || self.cursor.first() == b'\n' {
224                let span = Span::new(self.file_start + start, self.file_start + self.cursor.pos());
225                self.diagnostics
226                    .push(Diagnostic::error("missing terminating character in header name", span));
227                break;
228            }
229            if self.eat() == close {
230                break;
231            }
232        }
233        let end = self.cursor.pos();
234        let value = Some(self.intern_spelling(interner, PpTokenKind::HeaderName, start, end));
235        let mut flags = flags;
236        if self.unclean {
237            flags = flags.with(TokenFlags::SPLICED);
238        }
239        let span = Span::new(self.file_start + start, self.file_start + end);
240        Some(PpToken { kind: PpTokenKind::HeaderName, flags, value, span })
241    }
242
243    /// The flags for the token about to be scanned, and resets them for the next one.
244    fn take_flags(&mut self) -> TokenFlags {
245        let mut flags = TokenFlags::EMPTY;
246        if self.at_line_start {
247            flags = flags.with(TokenFlags::START_OF_LINE);
248        }
249        if self.leading_space {
250            flags = flags.with(TokenFlags::LEADING_SPACE);
251        }
252        self.at_line_start = false;
253        self.leading_space = false;
254        flags
255    }
256
257    /// Consumes one logical byte, keeping the spelling buffer correct.
258    fn eat(&mut self) -> u8 {
259        let before = self.cursor.pos();
260        // Every caller has already established there is a byte here, through `at_end` or
261        // through a lookahead that returned a non-zero byte.
262        let (b, clean) = self.cursor.bump().expect("eat called at end of file");
263        if self.unclean {
264            self.scratch.push(b);
265        } else if !clean {
266            // The first interruption in this token. Everything before it was contiguous, so
267            // it copies as one slice, and only from here on does the scan pay per byte.
268            let from = self.token_start as usize;
269            let bytes = self.cursor.bytes();
270            self.scratch.clear();
271            self.scratch.extend_from_slice(&bytes[from..before as usize]);
272            self.scratch.push(b);
273            self.unclean = true;
274        }
275        b
276    }
277
278    /// Interns the spelling of the token that ran from `start` to `end`.
279    ///
280    /// A spelling that is not UTF-8 is kept as its bytes when the token is a literal and is an
281    /// error anywhere else. The body of a string literal is bytes and does not have to be text:
282    /// `"\xff"` may be written as the byte itself, and gcc takes the file and gives the array
283    /// one element. Replacing the byte would give it three, since the replacement character is
284    /// three bytes of UTF-8, so the object would not be the one that was written even where the
285    /// diagnostic is ignored. Everywhere else a byte that is not part of a character is a
286    /// mistake, an identifier included, which is where gcc draws the same line.
287    fn intern_spelling(
288        &mut self,
289        interner: &mut Interner,
290        kind: PpTokenKind,
291        start: u32,
292        end: u32,
293    ) -> Symbol {
294        let bytes: &[u8] = if self.unclean {
295            &self.scratch
296        } else {
297            &self.cursor.bytes()[start as usize..end as usize]
298        };
299        if std::str::from_utf8(bytes).is_ok() {
300            return interner.intern_bytes(bytes);
301        }
302        if matches!(kind, PpTokenKind::StringLit | PpTokenKind::CharConst) {
303            return interner.intern_bytes(bytes);
304        }
305        let symbol = interner.intern_bytes(bytes);
306        let span = Span::new(self.file_start + start, self.file_start + end);
307        // Not fatal, so that one bad byte does not stop the run before the errors the user
308        // came for.
309        self.diagnostics.push(Diagnostic::error("source is not valid UTF-8 here", span));
310        symbol
311    }
312
313    /// Whitespace, newlines and comments, all of which become one space.
314    fn skip_trivia(&mut self) {
315        while !self.cursor.at_end() {
316            // Indentation first, in one jump. This is the single hottest thing the lexer does,
317            // because every line of every header starts with some and none of it says anything.
318            if self.cursor.skip_blanks() {
319                self.leading_space = true;
320                continue;
321            }
322            match CLASS[self.cursor.first() as usize] {
323                Class::Space => {
324                    self.cursor.bump();
325                    self.leading_space = true;
326                }
327                Class::Newline => {
328                    self.cursor.bump();
329                    self.at_line_start = true;
330                    self.leading_space = false;
331                }
332                Class::Slash => match self.cursor.nth(1) {
333                    b'/' => self.line_comment(),
334                    b'*' => self.block_comment(),
335                    _ => return,
336                },
337                _ => return,
338            }
339        }
340    }
341
342    /// Spaces and block comments but not newlines, for scanning inside a directive line.
343    fn skip_horizontal(&mut self) {
344        while !self.cursor.at_end() {
345            if self.cursor.skip_blanks() {
346                self.leading_space = true;
347                continue;
348            }
349            let b = self.cursor.first();
350            if CLASS[b as usize] == Class::Space {
351                self.cursor.bump();
352                self.leading_space = true;
353            } else if b == b'/' && self.cursor.nth(1) == b'*' {
354                self.block_comment();
355            } else {
356                return;
357            }
358        }
359    }
360
361    fn line_comment(&mut self) {
362        if !self.options.line_comments && !self.reported_line_comment {
363            // Reported and then skipped anyway, because a file that writes one writes hundreds
364            // and every token after the first slash would otherwise be a second complaint about
365            // the same line. gcc says it once and reads the rest of the line as a comment too.
366            self.reported_line_comment = true;
367            let at = self.cursor.pos();
368            let span = Span::new(self.file_start + at, self.file_start + at + 2);
369            self.diagnostics
370                .push(Diagnostic::error("C++ style comments are not allowed in ISO C90", span));
371        }
372        while !self.cursor.at_end() && self.cursor.first() != b'\n' {
373            // Nothing in the body means anything, so the only bytes worth stopping on are the
374            // ones that could end it: the newline, and a backslash or trigraph that splices the
375            // comment onto the next line instead. Everything between goes past unread.
376            self.cursor.skip_plain(&[]);
377            if self.cursor.at_end() || self.cursor.first() == b'\n' {
378                break;
379            }
380            self.cursor.bump();
381        }
382        // A comment becomes one space. The newline that ends it is left for the trivia loop,
383        // so the next token still knows it starts a line.
384        self.leading_space = true;
385    }
386
387    fn block_comment(&mut self) {
388        let start = self.cursor.pos();
389        self.cursor.bump();
390        self.cursor.bump();
391        let mut spans_lines = false;
392        loop {
393            // Same trade as the line comment, with `*` added because that is what can end this
394            // one. A license block is a couple of thousand bytes of nothing, and this walks it
395            // in a few dozen steps rather than a few thousand.
396            self.cursor.skip_plain(b"*");
397            if self.cursor.at_end() {
398                let span = Span::new(self.file_start + start, self.file_start + self.cursor.pos());
399                self.diagnostics.push(Diagnostic::error("unterminated comment", span));
400                break;
401            }
402            let b = self.cursor.first();
403            if b == b'\n' {
404                spans_lines = true;
405            }
406            if b == b'*' && self.cursor.nth(1) == b'/' {
407                self.cursor.bump();
408                self.cursor.bump();
409                break;
410            }
411            self.cursor.bump();
412        }
413        self.leading_space = true;
414        if spans_lines {
415            // A comment is whitespace, so a `#` after a comment that crossed a newline is
416            // still the first thing on its line and is still a directive. GCC agrees, and
417            // real headers write directives this way.
418            self.at_line_start = true;
419        }
420    }
421
422    fn ident_or_prefixed_literal(&mut self, b: u8, start: u32) -> PpTokenKind {
423        // `L"x"` is one token rather than an identifier followed by a string, so the prefixes
424        // are checked before the identifier scan rather than unwound afterwards.
425        let (n1, n2) = (self.cursor.nth(1), self.cursor.nth(2));
426        match b {
427            b'L' | b'u' | b'U' if n1 == b'"' => {
428                self.eat();
429                self.literal(b'"', start, PpTokenKind::StringLit)
430            }
431            b'L' | b'u' | b'U' if n1 == b'\'' => {
432                self.eat();
433                self.literal(b'\'', start, PpTokenKind::CharConst)
434            }
435            // `u8"s"` is C11. `u8'c'` is C23.
436            b'u' if n1 == b'8' && (n2 == b'"' || n2 == b'\'') => {
437                self.eat();
438                self.eat();
439                let kind = if n2 == b'"' { PpTokenKind::StringLit } else { PpTokenKind::CharConst };
440                self.literal(n2, start, kind)
441            }
442            _ => self.identifier(),
443        }
444    }
445
446    fn identifier(&mut self) -> PpTokenKind {
447        while !self.cursor.at_end() {
448            let b = self.cursor.first();
449            if is_ident_continue(b) {
450                self.eat();
451            } else if b == b'\\' && matches!(self.cursor.nth(1), b'u' | b'U') {
452                // A universal character name spells an identifier character. Whether the
453                // character it names is allowed in an identifier is a phase 7 question,
454                // because the answer depends on `-std=`.
455                self.eat();
456                self.eat();
457            } else {
458                break;
459            }
460        }
461        PpTokenKind::Ident
462    }
463
464    fn pp_number(&mut self) -> PpTokenKind {
465        // The pp-number grammar is deliberately looser than the constant grammar, so `1.2.3`
466        // and `0x1p+3` are both one token here. Rejecting the first belongs to phase 7, and
467        // doing it here would break `##` pasting that builds a number out of pieces.
468        self.eat();
469        while !self.cursor.at_end() {
470            let b = self.cursor.first();
471            let n1 = self.cursor.nth(1);
472            if matches!(b, b'e' | b'E' | b'p' | b'P') && matches!(n1, b'+' | b'-') {
473                self.eat();
474                self.eat();
475            } else if is_ident_continue(b) || b == b'.' {
476                self.eat();
477            } else if b == b'\'' && is_ident_continue(n1) && self.options.digit_separators {
478                // C23 digit separators. `1'000'000` is one pp-number, and the apostrophe only
479                // separates when an identifier character follows, so `1'a'` still ends the
480                // number where a character constant begins. Before C23 there is no such thing,
481                // and gcc reads the same spelling as a number next to a character constant.
482                self.eat();
483                self.eat();
484            } else if b == b'\\' && matches!(n1, b'u' | b'U') {
485                self.eat();
486                self.eat();
487            } else {
488                break;
489            }
490        }
491        PpTokenKind::Number
492    }
493
494    fn literal(&mut self, quote: u8, start: u32, kind: PpTokenKind) -> PpTokenKind {
495        self.eat();
496        loop {
497            if self.cursor.at_end() || self.cursor.first() == b'\n' {
498                // A literal does not cross a line. Reporting it here and stopping at the
499                // newline is what keeps one missing quote from swallowing the rest of the
500                // file and turning into a hundred nonsense errors.
501                let span = Span::new(self.file_start + start, self.file_start + self.cursor.pos());
502                let what = if quote == b'"' { "string literal" } else { "character constant" };
503                self.diagnostics
504                    .push(Diagnostic::error(format!("missing terminating quote in {what}"), span));
505                break;
506            }
507            let b = self.eat();
508            if b == quote {
509                break;
510            }
511            if b == b'\\' && !self.cursor.at_end() && self.cursor.first() != b'\n' {
512                // What the escape means is phase 5's problem. All this needs to know is that
513                // the next byte cannot end the literal.
514                self.eat();
515            }
516        }
517        kind
518    }
519
520    /// Scans a punctuator, or returns `None` without consuming anything when the byte begins
521    /// no punctuator at all.
522    fn punctuator(&mut self, start: u32, flags: TokenFlags) -> Option<PpToken> {
523        let (punct, len, digraph) = self.punctuator_kind()?;
524        for _ in 0..len {
525            self.eat();
526        }
527        let end = self.cursor.pos();
528        let mut flags = flags;
529        if digraph {
530            flags = flags.with(TokenFlags::DIGRAPH);
531        }
532        if self.unclean {
533            flags = flags.with(TokenFlags::SPLICED);
534        }
535        let span = Span::new(self.file_start + start, self.file_start + end);
536        Some(PpToken { kind: PpTokenKind::Punct(punct), flags, value: None, span })
537    }
538
539    /// Longest match over the punctuator set, measured in logical bytes.
540    fn punctuator_kind(&self) -> Option<(Punct, usize, bool)> {
541        let one = self.cursor.first();
542        let two = self.cursor.nth(1);
543        let three = self.cursor.nth(2);
544        let four = self.cursor.nth(3);
545        let found = match one {
546            b'[' => (Punct::LBracket, 1, false),
547            b']' => (Punct::RBracket, 1, false),
548            b'(' => (Punct::LParen, 1, false),
549            b')' => (Punct::RParen, 1, false),
550            b'{' => (Punct::LBrace, 1, false),
551            b'}' => (Punct::RBrace, 1, false),
552            b'~' => (Punct::Tilde, 1, false),
553            b'?' => (Punct::Question, 1, false),
554            b';' => (Punct::Semi, 1, false),
555            b',' => (Punct::Comma, 1, false),
556            b'.' if two == b'.' && three == b'.' => (Punct::Ellipsis, 3, false),
557            b'.' => (Punct::Dot, 1, false),
558            b'-' => match two {
559                b'>' => (Punct::Arrow, 2, false),
560                b'-' => (Punct::MinusMinus, 2, false),
561                b'=' => (Punct::MinusEq, 2, false),
562                _ => (Punct::Minus, 1, false),
563            },
564            b'+' => match two {
565                b'+' => (Punct::PlusPlus, 2, false),
566                b'=' => (Punct::PlusEq, 2, false),
567                _ => (Punct::Plus, 1, false),
568            },
569            b'&' => match two {
570                b'&' => (Punct::AmpAmp, 2, false),
571                b'=' => (Punct::AmpEq, 2, false),
572                _ => (Punct::Amp, 1, false),
573            },
574            b'|' => match two {
575                b'|' => (Punct::PipePipe, 2, false),
576                b'=' => (Punct::PipeEq, 2, false),
577                _ => (Punct::Pipe, 1, false),
578            },
579            b'*' if two == b'=' => (Punct::StarEq, 2, false),
580            b'*' => (Punct::Star, 1, false),
581            b'/' if two == b'=' => (Punct::SlashEq, 2, false),
582            b'/' => (Punct::Slash, 1, false),
583            b'!' if two == b'=' => (Punct::Ne, 2, false),
584            b'!' => (Punct::Bang, 1, false),
585            b'^' if two == b'=' => (Punct::CaretEq, 2, false),
586            b'^' => (Punct::Caret, 1, false),
587            b'=' if two == b'=' => (Punct::EqEq, 2, false),
588            b'=' => (Punct::Eq, 1, false),
589            b':' => match two {
590                b'>' => (Punct::RBracket, 2, true),
591                b':' => (Punct::ColonColon, 2, false),
592                _ => (Punct::Colon, 1, false),
593            },
594            b'<' => match two {
595                b'<' if three == b'=' => (Punct::ShlEq, 3, false),
596                b'<' => (Punct::Shl, 2, false),
597                b'=' => (Punct::Le, 2, false),
598                b':' => (Punct::LBracket, 2, true),
599                b'%' => (Punct::LBrace, 2, true),
600                _ => (Punct::Lt, 1, false),
601            },
602            b'>' => match two {
603                b'>' if three == b'=' => (Punct::ShrEq, 3, false),
604                b'>' => (Punct::Shr, 2, false),
605                b'=' => (Punct::Ge, 2, false),
606                _ => (Punct::Gt, 1, false),
607            },
608            b'%' => match two {
609                b'=' => (Punct::PercentEq, 2, false),
610                b'>' => (Punct::RBrace, 2, true),
611                b':' if three == b'%' && four == b':' => (Punct::HashHash, 4, true),
612                b':' => (Punct::Hash, 2, true),
613                _ => (Punct::Percent, 1, false),
614            },
615            b'#' if two == b'#' => (Punct::HashHash, 2, false),
616            b'#' => (Punct::Hash, 1, false),
617            _ => return None,
618        };
619        Some(found)
620    }
621}
622
623/// Scans `src` to the end and returns every preprocessing token and every complaint.
624///
625/// The end of file token is included, because everything downstream wants somewhere to point
626/// when a construct runs off the end of the file.
627pub fn tokenize(
628    src: &[u8],
629    file_start: BytePos,
630    opts: Options,
631    interner: &mut Interner,
632) -> (Vec<PpToken>, Vec<Diagnostic>) {
633    let mut lexer = Lexer::new(src, file_start, opts);
634    let mut out = Vec::new();
635    loop {
636        let token = lexer.next_token(interner);
637        let done = token.is_eof();
638        out.push(token);
639        if done {
640            break;
641        }
642    }
643    let diagnostics = lexer.take_diagnostics();
644    (out, diagnostics)
645}