Skip to main content

tabnas/
lexer.rs

1// Copyright (c) 2013-2026 Richard Rodger, MIT License
2
3use crate::error::TabnasError;
4use crate::options::{LexCheck, LexCheckResult, MatchTokenMatcher, Options};
5use crate::token::{
6    Point, Token, TIN_BD, TIN_CM, TIN_LN, TIN_NR, TIN_SP, TIN_ST, TIN_TX, TIN_VL, TIN_ZZ,
7};
8use crate::value::Value;
9use regex::Regex;
10use std::panic::{catch_unwind, AssertUnwindSafe};
11use std::sync::Arc;
12
13pub struct Lexer<'a> {
14    src: &'a str,
15    chars: Vec<char>,
16    byte_indices: Vec<usize>,
17    char_len: usize,
18    idx: usize,
19    ri: usize,
20    ci: usize,
21    options: Arc<Options>,
22    ignore_tins: Vec<crate::Tin>,
23    char_sets: crate::text::CharSets,
24    err: Option<TabnasError>,
25    end_reached: bool,
26    /// `number.exclude`, compiled once per grammar and shared by the
27    /// parser that owns it; see [`compile_number_exclude`].
28    exclude_regex: Option<Arc<Regex>>,
29    want: Option<Vec<crate::Tin>>,
30    standalone: Option<(crate::Rule, crate::Context)>,
31}
32
33#[derive(Clone)]
34pub(crate) struct LexerState {
35    idx: usize,
36    ri: usize,
37    ci: usize,
38    err: Option<TabnasError>,
39    end_reached: bool,
40}
41
42/// Opaque snapshot returned by [`Lexer::relex_for_rule`]. Pass it to
43/// [`Lexer::unrelex`] if the caller later rejects the committed recut.
44#[derive(Clone)]
45pub struct RelexCheckpoint {
46    state: LexerState,
47    replay: std::collections::VecDeque<Token>,
48}
49
50enum CheckFlow {
51    Continue,
52    Skip,
53    Token(Box<Token>),
54}
55
56/// The result the lexer passes through its own internals.
57///
58/// `TabnasError` is 664 bytes, so `LexResult<Token>` is 664 bytes
59/// too: three times the token it carries, and moved on the SUCCESS path
60/// through every frame of the lexer chain. Boxing the error inside the
61/// engine makes those results the size of a token again, and costs an
62/// allocation only when there is an error to report, which is the path that
63/// is already building a 664-byte diagnostic.
64///
65/// The public entry points still hand back an unboxed `TabnasError`, so no
66/// caller of this crate sees the box.
67type LexResult<T> = Result<T, Box<TabnasError>>;
68
69/// The compiled form of `number.exclude`, or `None` when there is no
70/// pattern or it does not compile (an invalid pattern excludes nothing,
71/// as it always has).
72///
73/// Compiling a regex costs about 700K instructions, which is more than
74/// a small document costs to parse, so the parser compiles it once per
75/// grammar and hands every lexer the same automaton through the `Arc`.
76/// Cloning a `Regex` would not do: it shares the automaton but builds a
77/// fresh scratch-cache pool, and the first search from each clone fills
78/// it. Go and TypeScript compile the pattern once, at configuration time.
79pub(crate) fn compile_number_exclude(options: &Options) -> Option<Arc<Regex>> {
80    let pattern = options.number.exclude.as_deref()?;
81    Regex::new(pattern).ok().map(Arc::new)
82}
83
84impl<'a> Lexer<'a> {
85    pub fn new(src: &'a str, mut options: Options) -> Self {
86        // A lexer built directly may be handed options nobody has
87        // ordered yet. The parser's own lexer comes through
88        // `with_shared`, whose options were ordered when they were
89        // prepared, and whose exclude pattern was compiled then too.
90        options.sort_for_lexing();
91        let exclude_regex = compile_number_exclude(&options);
92        Self::with_shared(src, Arc::new(options), exclude_regex)
93    }
94
95    /// Lex against options the parser already owns and has ordered, with
96    /// the `number.exclude` pattern it compiled from them.
97    pub(crate) fn with_shared(
98        src: &'a str,
99        options: Arc<Options>,
100        exclude_regex: Option<Arc<Regex>>,
101    ) -> Self {
102        let mut chars = Vec::new();
103        let mut byte_indices = Vec::new();
104        for (b_idx, c) in src.char_indices() {
105            chars.push(c);
106            byte_indices.push(b_idx);
107        }
108        let char_len = chars.len();
109
110        Lexer {
111            src,
112            chars,
113            byte_indices,
114            char_len,
115            idx: 0,
116            ri: 1,
117            ci: 1,
118            ignore_tins: options.ignore_tins(),
119            char_sets: options.char_sets(),
120            options,
121            err: None,
122            end_reached: false,
123            exclude_regex,
124            want: None,
125            standalone: None,
126        }
127    }
128
129    fn current_point(&self) -> Point {
130        Point {
131            len: self.src.len(),
132            site: crate::Site {
133                si: self.byte_position(),
134                pos: self.idx,
135                ri: self.ri,
136                ci: self.ci,
137            },
138        }
139    }
140
141    /// The source from char index `start` up to `end`, clipped to the end
142    /// of the source: the span of a bad token, cut as TypeScript's
143    /// `lex.bad(why, pstart, pend)` cuts it (`ts/src/lexer.ts`).
144    ///
145    /// Only the END is clipped. The caller must supply `start <= end`
146    /// and `start <= self.char_len`, or the indexing panics. Every call
147    /// site below passes `esc_point.site.pos - 1`, which additionally
148    /// needs `1 <= pos`; that holds because `esc_point` is captured
149    /// after the escape character has been consumed. These three are
150    /// the preconditions `rs/verus/lexer_span.rs` ASSUMES: it proves the
151    /// span arithmetic is in bounds given them, and does not model the
152    /// call sites, so nothing machine-checks that they hold here. A
153    /// refactor that captured the point before the advance would still
154    /// pass `rs/verus/run.sh`.
155    fn source_span(&self, start: usize, end: usize) -> String {
156        self.chars[start..end.min(self.char_len)].iter().collect()
157    }
158
159    fn state(&self) -> LexerState {
160        LexerState {
161            idx: self.idx,
162            ri: self.ri,
163            ci: self.ci,
164            err: self.err.clone(),
165            end_reached: self.end_reached,
166        }
167    }
168
169    /// Full immutable source supplied to this lexer.
170    pub fn source(&self) -> &str {
171        self.src
172    }
173
174    /// Source remaining at the live cursor.
175    pub fn remaining(&self) -> &str {
176        &self.src[self.byte_position()..]
177    }
178
179    /// Return at most `max_chars` Unicode scalar values from the live cursor.
180    /// This is the Rust counterpart of the public `lex.fwd`/`Lex.Fwd` helper.
181    pub fn forward(&self, max_chars: usize) -> &str {
182        let remaining = self.remaining();
183        let end = remaining
184            .char_indices()
185            .nth(max_chars)
186            .map_or(remaining.len(), |(index, _)| index);
187        &remaining[..end]
188    }
189
190    /// Snapshot the live cursor for token construction.
191    pub fn point(&self) -> Point {
192        self.current_point()
193    }
194
195    /// Advance by Unicode scalar values. Returns false without moving when
196    /// the requested count extends beyond end-of-source.
197    pub fn advance_chars(&mut self, count: usize) -> bool {
198        if self.idx.saturating_add(count) > self.char_len {
199            return false;
200        }
201        for _ in 0..count {
202            self.advance();
203        }
204        true
205    }
206
207    /// Construct a token from a point captured before cursor advancement.
208    pub fn token(
209        &self,
210        name: impl AsRef<str>,
211        tin: crate::Tin,
212        value: Value,
213        source: impl Into<crate::TokenText>,
214        point: Point,
215    ) -> Token {
216        Token::new(name, tin, value, source, point)
217    }
218
219    /// Resolve or allocate a token identity in this lexer's configuration.
220    pub fn token_tin(&mut self, name: impl Into<String>) -> crate::Tin {
221        Arc::make_mut(&mut self.options).register_token(name)
222    }
223
224    /// Resolve a token identity back to its configured name.
225    pub fn token_name(&self, tin: crate::Tin) -> String {
226        self.options.token_name(tin)
227    }
228
229    /// Whether the options this lexer runs under enable `alt`: not when
230    /// `rule.exclude` names one of its groups, nor when `rule.include`
231    /// lists groups and it declares none of them. The parser skips such an
232    /// alternate, and TypeScript removes it from the rule spec
233    /// (`filterRules`) before anything reads the spec, so a custom matcher
234    /// that reads a rule's alternates, to tell a key position from a value
235    /// one, asks this to see the same alternates TypeScript does.
236    pub fn alt_enabled(&self, alt: &crate::AltSpec) -> bool {
237        crate::parser::groups_enabled(alt, &self.options)
238    }
239
240    /// Construct a bad token at the current cursor.
241    pub fn bad(&self, why: impl Into<String>) -> Token {
242        let point = self.current_point();
243        let source = self
244            .peek()
245            .map_or_else(String::new, |character| character.to_string());
246        let mut token = Token::new("#BD", TIN_BD, Value::Undefined, source, point);
247        token.err = crate::TokenCode::from(why.into());
248        token.why = token.err.clone();
249        token
250    }
251
252    /// Construct a bad token whose displayed source is a scalar-indexed span.
253    /// As in TypeScript, the diagnostic point remains the live cursor.
254    pub fn bad_span(&self, why: impl Into<String>, start: usize, end: usize) -> Token {
255        let point = self.current_point();
256        let source = if start <= end && end <= self.char_len {
257            let start_byte = self
258                .byte_indices
259                .get(start)
260                .copied()
261                .unwrap_or(self.src.len());
262            let end_byte = self
263                .byte_indices
264                .get(end)
265                .copied()
266                .unwrap_or(self.src.len());
267            self.src[start_byte..end_byte].to_string()
268        } else {
269            self.peek()
270                .map_or_else(String::new, |character| character.to_string())
271        };
272        let mut token = Token::new("#BD", TIN_BD, Value::Undefined, source, point);
273        token.err = crate::TokenCode::from(why.into());
274        token.why = token.err.clone();
275        token
276    }
277
278    fn byte_position(&self) -> usize {
279        self.byte_indices
280            .get(self.idx)
281            .copied()
282            .unwrap_or(self.src.len())
283    }
284
285    fn advance(&mut self) -> Option<char> {
286        if self.idx < self.char_len {
287            let c = self.chars[self.idx];
288            self.idx += 1;
289            if self.char_sets.row.contains(c) {
290                self.ri += 1;
291                self.ci = 1;
292            } else {
293                self.ci += 1;
294            }
295            Some(c)
296        } else {
297            None
298        }
299    }
300
301    fn peek(&self) -> Option<char> {
302        if self.idx < self.char_len {
303            Some(self.chars[self.idx])
304        } else {
305            None
306        }
307    }
308
309    fn peek_at(&self, offset: usize) -> Option<char> {
310        let i = self.idx + offset;
311        if i < self.char_len {
312            Some(self.chars[i])
313        } else {
314            None
315        }
316    }
317
318    fn wants(&self, tin: crate::Tin) -> bool {
319        self.want
320            .as_ref()
321            .is_none_or(|wanted| wanted.contains(&tin))
322    }
323
324    fn run_check(&mut self, check: Option<LexCheck>, point: Point) -> CheckFlow {
325        let Some(check) = check else {
326            return CheckFlow::Continue;
327        };
328        let remaining = &self.src[self.byte_position()..];
329        let result = check
330            .run_imperative(self)
331            .or_else(|| check.run(remaining))
332            .unwrap_or(LexCheckResult::Continue);
333        match result {
334            LexCheckResult::Continue => CheckFlow::Continue,
335            LexCheckResult::Skip => CheckFlow::Skip,
336            LexCheckResult::NativeToken(token) => CheckFlow::Token(token),
337            LexCheckResult::Token(token)
338                if !token.source.is_empty() && remaining.starts_with(&token.source) =>
339            {
340                let tin = if token.tin < 0 {
341                    self.options.token(&token.name).unwrap_or(token.tin)
342                } else {
343                    token.tin
344                };
345                if tin < 0 {
346                    return CheckFlow::Skip;
347                }
348                for _ in token.source.chars() {
349                    self.advance();
350                }
351                CheckFlow::Token(Box::new(Token::new(
352                    token.name,
353                    tin,
354                    token.value,
355                    token.source,
356                    point,
357                )))
358            }
359            LexCheckResult::Token(_) => CheckFlow::Skip,
360        }
361    }
362
363    /// Give any plugin matcher whose order is below `before` its turn.
364    ///
365    /// Nine sites in the lexer call this per token, once at each stage a
366    /// matcher is allowed to intervene. A grammar with no custom matcher,
367    /// which is most of them, was paying nine index lookups per token to be
368    /// told nine times that there is nothing to run. The guard is inline so
369    /// those sites skip the call itself; the walk stays out of line.
370    #[inline]
371    fn run_custom_matchers(
372        &mut self,
373        index: &mut usize,
374        before: f64,
375        point: Point,
376        plugin: &mut Option<(&mut crate::Rule, &mut crate::Context)>,
377    ) -> Option<Token> {
378        if *index >= self.options.lex.matchers.len() {
379            return None;
380        }
381        self.run_remaining_custom_matchers(index, before, point, plugin)
382    }
383
384    #[inline(never)]
385    fn run_remaining_custom_matchers(
386        &mut self,
387        index: &mut usize,
388        before: f64,
389        point: Point,
390        plugin: &mut Option<(&mut crate::Rule, &mut crate::Context)>,
391    ) -> Option<Token> {
392        while let Some(matcher) = self
393            .options
394            .lex
395            .matchers
396            .get_index(*index)
397            .map(|(_, matcher)| matcher)
398            .filter(|matcher| matcher.order < before)
399            .cloned()
400        {
401            *index += 1;
402            let remaining = &self.src[self.byte_position()..];
403            let saved = self.state();
404            let token = if let Some(callback) = matcher.imperative.as_ref() {
405                let Some((rule, context)) = plugin.as_mut() else {
406                    continue;
407                };
408                callback(self, rule, context)
409            } else {
410                matcher
411                    .matcher
412                    .as_ref()
413                    .and_then(|callback| callback(remaining))
414                    .filter(|token| {
415                        !token.source.is_empty() && remaining.starts_with(&token.source)
416                    })
417                    .map(|token| {
418                        Token::new(token.name, token.tin, token.value, token.source, point)
419                    })
420            };
421            let Some(mut token) = token else {
422                if self.want.is_some() {
423                    self.restore(saved);
424                }
425                continue;
426            };
427
428            // TypeScript and Go run opaque custom matchers speculatively for
429            // a negotiated cut. An unwanted result rolls back locally so a
430            // later matcher can still satisfy the request.
431            let tin = if token.tin < 0 {
432                self.options.token(&token.name).unwrap_or(token.tin)
433            } else {
434                token.tin
435            };
436            if tin < 0 || !self.wants(tin) {
437                self.restore(saved);
438                continue;
439            }
440            if matcher.imperative.is_none() {
441                for _ in token.src.chars() {
442                    self.advance();
443                }
444            }
445            token.tin = tin;
446            return Some(token);
447        }
448        None
449    }
450
451    fn is_text_delimiter_here(&self) -> bool {
452        self.is_text_delimiter_at(self.idx)
453    }
454
455    fn is_text_delimiter_at(&self, index: usize) -> bool {
456        let Some(ch) = self.chars.get(index).copied() else {
457            return true;
458        };
459        let remaining = &self.src[self.byte_indices[index]..];
460        (self.options.space.lex && self.char_sets.space.contains(ch))
461            || (self.options.fixed.lex
462                && self
463                    .options
464                    .fixed
465                    .tokens
466                    .values()
467                    .any(|token| !token.source.is_empty() && remaining.starts_with(&token.source)))
468            || (self.options.line.lex
469                && (self.char_sets.line_ends.contains(ch) || matches!(ch, '\u{2028}' | '\u{2029}')))
470            || (self.options.comment.lex
471                && self.options.comment.definitions.values().any(|definition| {
472                    definition.lex
473                        && !definition.start.is_empty()
474                        && remaining.starts_with(&definition.start)
475                }))
476            || self
477                .options
478                .ender
479                .iter()
480                .any(|ender| !ender.is_empty() && remaining.starts_with(ender))
481    }
482
483    /// Fetches the next non-IGNORE token (skipping spaces, lines, comments).
484    pub fn next_token(&mut self) -> Result<Token, TabnasError> {
485        let point = self.current_point();
486        let result = match catch_unwind(AssertUnwindSafe(|| {
487            if let Some(ref error) = self.err {
488                return Err(Box::new(error.clone()));
489            }
490
491            loop {
492                let token = self.next_raw(None)?;
493                if !self.ignore_tins.contains(&token.tin) {
494                    return Ok(token);
495                }
496            }
497        })) {
498            Ok(result) => result,
499            Err(payload) => self.record_panic(payload, "Lexer::next_token", point),
500        };
501        // The box is internal to the engine; a caller gets the error itself.
502        result.map_err(|error| *error)
503    }
504
505    /// Fetch the next token without discarding whitespace, line, or comment tokens.
506    pub fn next_raw_token(&mut self) -> Result<Token, TabnasError> {
507        let point = self.current_point();
508        let result = match catch_unwind(AssertUnwindSafe(|| self.next_raw(None))) {
509            Ok(result) => result,
510            Err(payload) => self.record_panic(payload, "Lexer::next_raw_token", point),
511        };
512        result.map_err(|error| *error)
513    }
514
515    /// Fetch one token for an imperative parser callback, preserving ignored
516    /// space/line/comment tokens just like TypeScript's public `lex.next`.
517    /// Replayed tokens produced by `Context::rewind` are served first.
518    pub fn next_raw_for_rule(
519        &mut self,
520        rule: &mut crate::Rule,
521        context: &mut crate::Context,
522    ) -> LexResult<Token> {
523        if let Some(token) = context.next_replay() {
524            Ok(token)
525        } else {
526            self.next_raw_with(None, Some((rule, context)))
527        }
528    }
529
530    /// Fetch the next non-ignored token for an imperative parser callback.
531    pub fn next_for_rule(
532        &mut self,
533        rule: &mut crate::Rule,
534        context: &mut crate::Context,
535    ) -> LexResult<Token> {
536        loop {
537            let token = self.next_raw_for_rule(rule, context)?;
538            if !self.ignore_tins.contains(&token.tin) {
539                return Ok(token);
540            }
541        }
542    }
543
544    /// Public negotiated-relex entry point for native parser callbacks.
545    /// A successful recut commits the lexer cursor and returns an opaque undo
546    /// checkpoint; a failed recut restores all lexer state before returning.
547    pub fn relex_for_rule(
548        &mut self,
549        from: &Token,
550        wanted: &[crate::Tin],
551        rule: &mut crate::Rule,
552        context: &mut crate::Context,
553    ) -> Option<(Token, RelexCheckpoint)> {
554        self.relex(from, wanted, rule, context)
555    }
556
557    /// Undo a committed [`Lexer::relex_for_rule`] operation, including the
558    /// pending tokens hidden while the replacement cut was negotiated.
559    pub fn unrelex(&mut self, checkpoint: RelexCheckpoint, context: &mut crate::Context) {
560        self.restore(checkpoint.state);
561        context.restore_replay(checkpoint.replay);
562    }
563
564    fn record_panic(
565        &mut self,
566        payload: Box<dyn std::any::Any + Send>,
567        api: &str,
568        point: Point,
569    ) -> LexResult<Token> {
570        let error = TabnasError::from_panic(
571            payload,
572            api,
573            self.src,
574            point.site.pos,
575            point.site.ri,
576            point.site.ci,
577            &self.options,
578        );
579        self.err = Some(error.clone());
580        Err(Box::new(error))
581    }
582
583    /// Fetch a raw token while restricting non-eager custom token matchers to
584    /// the exact tins accepted at the parser slot being filled. Builtin and
585    /// fixed-token matchers are unaffected by this gate.
586    pub(crate) fn next_rule_token(
587        &mut self,
588        expected_match_tins: &[crate::Tin],
589        rule: &mut crate::Rule,
590        context: &mut crate::Context,
591    ) -> LexResult<Token> {
592        self.next_raw_with(Some(expected_match_tins), Some((rule, context)))
593    }
594
595    /// Clear a recoverable lexer fault. Compound string faults resume at the
596    /// next line boundary so the remainder of the broken string cannot be
597    /// mistaken for a new token stream.
598    pub(crate) fn recover_after_error(&mut self, to_line_end: bool) {
599        if to_line_end {
600            while let Some(character) = self.peek() {
601                self.advance();
602                if matches!(character, '\n' | '\r' | '\u{2028}' | '\u{2029}') {
603                    break;
604                }
605            }
606        }
607        self.err = None;
608        if self.idx < self.char_len {
609            self.end_reached = false;
610        }
611    }
612
613    /// Re-cut an already buffered source span, constrained to the token
614    /// identities requested by one alternate. On success the cursor remains
615    /// after the new cut; the returned state can restore the original cut if
616    /// that alternate later fails.
617    pub(crate) fn relex(
618        &mut self,
619        from: &Token,
620        wanted: &[crate::Tin],
621        rule: &mut crate::Rule,
622        context: &mut crate::Context,
623    ) -> Option<(Token, RelexCheckpoint)> {
624        if from.src.is_empty() || from.site.pos > self.char_len || wanted.is_empty() {
625            return None;
626        }
627        let saved = self.state();
628        // TypeScript temporarily replaces the lexer's pending-token queue
629        // with an empty queue for a negotiated cut. Rust keeps that queue on
630        // Context, so hide it explicitly and preserve it in the checkpoint.
631        let replay = context.take_replay();
632        self.idx = from.site.pos;
633        self.ri = from.site.ri;
634        self.ci = from.site.ci;
635        self.err = None;
636        self.end_reached = false;
637        self.want = Some(wanted.to_vec());
638        let recut = self.next_raw_with(None, Some((rule, context))).ok();
639        self.want = None;
640        match recut.filter(|token| wanted.contains(&token.tin)) {
641            Some(mut token) => {
642                token.ignored = from.ignored.clone();
643                Some((
644                    token,
645                    RelexCheckpoint {
646                        state: saved,
647                        replay,
648                    },
649                ))
650            }
651            None => {
652                self.restore(saved);
653                // Discard any speculative replay generated by an imperative
654                // matcher and restore the queue that preceded the attempt.
655                context.restore_replay(replay);
656                None
657            }
658        }
659    }
660
661    pub(crate) fn restore(&mut self, state: LexerState) {
662        self.idx = state.idx;
663        self.ri = state.ri;
664        self.ci = state.ci;
665        self.err = state.err;
666        self.end_reached = state.end_reached;
667        self.want = None;
668    }
669
670    fn next_raw(&mut self, expected_match_tins: Option<&[crate::Tin]>) -> LexResult<Token> {
671        // Only a lexer being driven directly needs these, and building
672        // them costs a whole `Options` clone. A parse reaches the lexer
673        // through `next_rule_token`, which brings the real rule and
674        // context with it, so it never wants them at all.
675        let (mut rule, mut context) = match self.standalone.take() {
676            Some(pair) => pair,
677            None => (
678                crate::Rule::new("#NORULE", Value::Undefined),
679                crate::Context::new(
680                    self.options.rewind.history,
681                    self.src,
682                    Value::Undefined,
683                    Arc::clone(&self.options),
684                    crate::InstanceInfo::default(),
685                ),
686            ),
687        };
688        let result = self.next_raw_with(expected_match_tins, Some((&mut rule, &mut context)));
689        self.standalone = Some((rule, context));
690        result
691    }
692
693    fn modify_text_value(
694        &mut self,
695        mut value: Value,
696        plugin: &mut Option<(&mut crate::Rule, &mut crate::Context)>,
697    ) -> Value {
698        if self.options.text.modify.is_empty() {
699            return value;
700        }
701        let modifiers = self.options.text.modify.clone();
702        let options = self.options.clone();
703        let Some((rule, context)) = plugin.as_mut() else {
704            panic!("imperative text modifier requires an active lexer context");
705        };
706        for modifier in modifiers {
707            value = modifier.run(value, self, rule, context, &options);
708        }
709        value
710    }
711
712    fn next_raw_with(
713        &mut self,
714        expected_match_tins: Option<&[crate::Tin]>,
715        plugin: Option<(&mut crate::Rule, &mut crate::Context)>,
716    ) -> LexResult<Token> {
717        let result = self.next_raw_inner(expected_match_tins, plugin);
718        match result {
719            Ok(token) => Ok(token),
720            Err(mut error) => {
721                error.apply_options(&self.options);
722                self.err = Some((*error).clone());
723                Err(error)
724            }
725        }
726    }
727
728    fn next_raw_inner(
729        &mut self,
730        expected_match_tins: Option<&[crate::Tin]>,
731        mut plugin: Option<(&mut crate::Rule, &mut crate::Context)>,
732    ) -> LexResult<Token> {
733        if self.end_reached {
734            return Ok(Token::new(
735                "#ZZ",
736                TIN_ZZ,
737                Value::Undefined,
738                "",
739                self.current_point(),
740            ));
741        }
742
743        if self.idx >= self.char_len {
744            self.end_reached = true;
745            return Ok(Token::new(
746                "#ZZ",
747                TIN_ZZ,
748                Value::Undefined,
749                "",
750                self.current_point(),
751            ));
752        }
753
754        let pnt = self.current_point();
755        let c = self.peek().unwrap();
756        let mut custom_index = 0;
757
758        if let Some(token) =
759            self.run_custom_matchers(&mut custom_index, 1_000_000.0, pnt, &mut plugin)
760        {
761            return Ok(token);
762        }
763
764        // User-declared match tokens occupy the 1e6 matcher priority band.
765        let match_skipped = if self.options.match_lex
766            && (!self.options.match_values.is_empty()
767                || self
768                    .options
769                    .match_tokens
770                    .values()
771                    .any(|matcher| self.wants(matcher.tin)))
772        {
773            match self.run_check(self.options.match_check.clone(), pnt) {
774                CheckFlow::Continue => false,
775                CheckFlow::Skip => true,
776                CheckFlow::Token(token) => return Ok(*token),
777            }
778        } else {
779            false
780        };
781        let remaining = &self.src[self.byte_position()..];
782        let custom_value = (self.options.match_lex && !match_skipped && self.want.is_none())
783            .then(|| {
784                self.options
785                    .match_values
786                    .values()
787                    .find_map(|matcher| match &matcher.matcher {
788                        MatchTokenMatcher::Callback(callback) => callback(remaining)
789                            .filter(|result| {
790                                !result.source.is_empty() && remaining.starts_with(&result.source)
791                            })
792                            .map(|result| (result.source, result.value)),
793                        MatchTokenMatcher::Regex(regex) => {
794                            let captures = regex.captures(remaining)?;
795                            let found = captures
796                                .get(0)
797                                .filter(|found| found.start() == 0 && !found.as_str().is_empty())?;
798                            let source = found.as_str().to_string();
799                            let value = matcher.transform.as_ref().map_or_else(
800                                || {
801                                    matcher
802                                        .val
803                                        .clone()
804                                        .unwrap_or_else(|| Value::String(source.clone()))
805                                },
806                                |transform| {
807                                    let groups = captures
808                                        .iter()
809                                        .map(|capture| {
810                                            capture.map_or_else(String::new, |value| {
811                                                value.as_str().into()
812                                            })
813                                        })
814                                        .collect::<Vec<_>>();
815                                    transform(&groups)
816                                },
817                            );
818                            Some((source, value))
819                        }
820                    })
821            })
822            .flatten();
823        if let Some((source, value)) = custom_value {
824            for _ in source.chars() {
825                self.advance();
826            }
827            return Ok(Token::new("#VL", TIN_VL, value, source, pnt));
828        }
829
830        let remaining = &self.src[self.byte_position()..];
831        // With no custom matcher there is nothing for the band to do: both
832        // passes walk an empty table and yield nothing, and `fix_len` is
833        // read only by that walk. Most grammars register none, and every
834        // token fetch of theirs paid the eager pass's scan of the fixed
835        // table (one closure call per fixed literal) to arrive at the
836        // `None` this guard now hands over directly. TS `makeMatchMatcher`
837        // returns null on an empty table (ts/src/lexer.ts) and the band is
838        // never installed; Go reaches the same place by defaulting
839        // `MatchLex` off unless `Options.Match` is set. Rust defaults
840        // `match_lex` true as TS does, so the guard is the parity.
841        let custom = (self.options.match_lex
842            && !match_skipped
843            && !self.options.match_tokens.is_empty())
844        .then(|| {
845            // Two passes, position-expected before eager, as go/lexer.go
846            // matchMatch and ts/src/lexer.ts makeMatchMatcher both make.
847            // One tin-ordered pass in which eagerness merely bypassed the
848            // slot gate let an eager matcher EARLIER in tin order win over
849            // an expected one later: with `p = %x31-39` beside
850            // `d = %x30-39`, the `2` of `12` lexed as the narrower class
851            // the `*d` loop never asked for. Eagerness is for firing where
852            // the slot's list is narrower than the grammar, never for
853            // outbidding what the slot names.
854            //
855            // Under a want the alternate's own tin list is the sharper
856            // gate, so one filtered pass is the whole search. With no
857            // expected list at all (a standalone lexer, no rule) nothing
858            // constrains the caller and every matcher is eligible in the
859            // first pass.
860            // The longest FIXED literal this slot expects that matches
861            // here, or 0. Only the eager pass consults it: there, a
862            // literal the slot names beats an eager-only matcher that
863            // cuts no further than it does. Without this, a character
864            // class that CONTAINS a literal the grammar also uses
865            // swallows it wherever the class is eager (`num = "0" /
866            // posdigit *digit` beside `digit = %x30-39` rejected
867            // `0.0.0`). LENGTH decides, not mere existence, so a keyword
868            // literal cannot truncate a longer word: ties go to the
869            // literal, and an eager matcher that cuts further still
870            // wins. TS and Go do the same, in makeMatchMatcher and
871            // matchMatch.
872            //
873            // Computed once per fetch and only when a regex matcher in the
874            // eager pass has something to weigh against it, as TS
875            // `expectedFixedLen` does (`fixLen = -1` until asked). The
876            // scan is the whole fixed table against the slot's list; an
877            // expected matcher that wins in pass 0, or a fetch under a
878            // want, never needs it. Nothing the scan reads changes
879            // between the two passes, so lazy equals eager.
880            let mut fix_len: Option<usize> = None;
881            let compute_fix_len = || {
882                if self.want.is_none() && self.options.fixed.lex {
883                    expected_match_tins.map_or(0, |expected| {
884                        self.options
885                            .fixed
886                            .tokens
887                            .values()
888                            .filter(|token| {
889                                !token.source.is_empty()
890                                    && expected.contains(&token.tin)
891                                    && remaining.starts_with(&token.source)
892                            })
893                            .map(|token| token.source.len())
894                            .max()
895                            .unwrap_or(0)
896                    })
897                } else {
898                    0
899                }
900            };
901            let passes = if self.want.is_some() { 1 } else { 2 };
902            (0..passes).find_map(|pass| {
903                self.options.match_tokens.values().find_map(|matcher| {
904                    if !self.wants(matcher.tin) {
905                        return None;
906                    }
907                    if self.want.is_none() {
908                        let expected = expected_match_tins
909                            .is_none_or(|expected| expected.contains(&matcher.tin));
910                        if pass == 0 {
911                            if !expected {
912                                return None;
913                            }
914                        } else if expected || !matcher.eager {
915                            return None;
916                        }
917                    }
918                    let result = match &matcher.matcher {
919                        MatchTokenMatcher::Regex(regex) => regex
920                            .find(remaining)
921                            .filter(|found| found.start() == 0)
922                            // The eager pass yields to an expected
923                            // literal it cannot out-cut; the fixed
924                            // matcher (2e6) runs next and takes it. See
925                            // `fix_len` above.
926                            .filter(|found| {
927                                pass == 0 || {
928                                    let fix_len = *fix_len.get_or_insert_with(compute_fix_len);
929                                    fix_len == 0 || found.len() > fix_len
930                                }
931                            })
932                            .map(|found| {
933                                let source = found.as_str().to_string();
934                                (source.clone(), Value::String(source))
935                            }),
936                        MatchTokenMatcher::Callback(callback) => callback(remaining)
937                            .filter(|result| {
938                                !result.source.is_empty() && remaining.starts_with(&result.source)
939                            })
940                            .map(|result| (result.source, result.value)),
941                    };
942                    result.map(|(source, value)| (matcher.name.clone(), matcher.tin, source, value))
943                })
944            })
945        });
946        if let Some(Some((name, tin, matched, value))) = custom {
947            for _ in matched.chars() {
948                self.advance();
949            }
950            return Ok(Token::new(name, tin, value, matched, pnt));
951        }
952
953        if let Some(token) =
954            self.run_custom_matchers(&mut custom_index, 2_000_000.0, pnt, &mut plugin)
955        {
956            return Ok(token);
957        }
958
959        // Fixed literals occupy the 2e6 band and use longest-match wins.
960        let fixed_skipped = if self.options.fixed.lex {
961            match self.run_check(self.options.fixed.check.clone(), pnt) {
962                CheckFlow::Continue => false,
963                CheckFlow::Skip => true,
964                CheckFlow::Token(token) => return Ok(*token),
965            }
966        } else {
967            false
968        };
969        let remaining = &self.src[self.byte_position()..];
970        // The winner is carried out of the table as its position, not as a
971        // copy of its text. `Token::new` takes the name and the source text
972        // by reference and stores both inline, so the only owned copy the
973        // token needs is the one inside `Value::String`. Naming the match
974        // as three owned values cost three `String` allocations per fixed
975        // token, two of them freed again before the token was built.
976        // The first byte decides almost every entry. Asking `wants` and then
977        // `starts_with` of each fixed token in turn ran a tin lookup and a
978        // `memcmp` per token in the grammar per token in the input, and a
979        // grammar with fifty fixed tokens pays fifty of each to reject
980        // forty-nine. One byte answers the same question, and an empty
981        // source is kept out by its own check, which only entries that
982        // already matched the byte ever reach.
983        let first_byte = remaining.as_bytes().first().copied();
984        let fixed = (self.options.fixed.lex && !fixed_skipped)
985            .then(|| {
986                self.options
987                    .fixed
988                    .tokens
989                    .values()
990                    .enumerate()
991                    .filter(|(_, token)| {
992                        token.source.as_bytes().first().copied() == first_byte
993                            && !token.source.is_empty()
994                            && self.wants(token.tin)
995                            && remaining.starts_with(&token.source)
996                    })
997                    .max_by_key(|(_, token)| token.source.len())
998                    .map(|(index, token)| (index, token.source.chars().count()))
999            })
1000            .flatten();
1001        if let Some((index, source_chars)) = fixed {
1002            for _ in 0..source_chars {
1003                self.advance();
1004            }
1005            let (_, token) = self
1006                .options
1007                .fixed
1008                .tokens
1009                .get_index(index)
1010                .expect("index came from this table, which nothing writes to mid-parse");
1011            return Ok(Token::new(
1012                &token.name,
1013                token.tin,
1014                Value::String(token.source.clone()),
1015                token.source.as_str(),
1016                pnt,
1017            ));
1018        }
1019
1020        if let Some(token) =
1021            self.run_custom_matchers(&mut custom_index, 3_000_000.0, pnt, &mut plugin)
1022        {
1023            return Ok(token);
1024        }
1025
1026        // 1. Whitespace
1027        let space_skipped = if self.options.space.lex && self.wants(TIN_SP) {
1028            match self.run_check(self.options.space.check.clone(), pnt) {
1029                CheckFlow::Continue => false,
1030                CheckFlow::Skip => true,
1031                CheckFlow::Token(token) => return Ok(*token),
1032            }
1033        } else {
1034            false
1035        };
1036        if self.options.space.lex
1037            && !space_skipped
1038            && self.wants(TIN_SP)
1039            && self.char_sets.space.contains(c)
1040        {
1041            let mut src = String::new();
1042            while let Some(ch) = self.peek() {
1043                if self.char_sets.space.contains(ch) {
1044                    src.push(ch);
1045                    self.advance();
1046                } else {
1047                    break;
1048                }
1049            }
1050            return Ok(Token::new(
1051                "#SP",
1052                TIN_SP,
1053                Value::String(src.clone()),
1054                src,
1055                pnt,
1056            ));
1057        }
1058
1059        if let Some(token) =
1060            self.run_custom_matchers(&mut custom_index, 4_000_000.0, pnt, &mut plugin)
1061        {
1062            return Ok(token);
1063        }
1064
1065        // 2. Line ending
1066        let line_skipped = if self.options.line.lex && self.wants(TIN_LN) {
1067            match self.run_check(self.options.line.check.clone(), pnt) {
1068                CheckFlow::Continue => false,
1069                CheckFlow::Skip => true,
1070                CheckFlow::Token(token) => return Ok(*token),
1071            }
1072        } else {
1073            false
1074        };
1075        if self.options.line.lex
1076            && !line_skipped
1077            && self.wants(TIN_LN)
1078            && (self.char_sets.line_ends.contains(c))
1079        {
1080            let mut src = String::new();
1081            let mut seen = std::collections::HashSet::new();
1082            while let Some(ch) = self.peek() {
1083                if !self.char_sets.line_ends.contains(ch) {
1084                    break;
1085                }
1086                if self.options.line.single && !seen.insert(ch) {
1087                    break;
1088                }
1089                src.push(self.advance().expect("peeked character must advance"));
1090            }
1091            self.ci = 1;
1092            return Ok(Token::new(
1093                "#LN",
1094                TIN_LN,
1095                Value::String(src.clone()),
1096                src,
1097                pnt,
1098            ));
1099        }
1100
1101        if self.options.line.lex
1102            && !line_skipped
1103            && self.wants(TIN_LN)
1104            && (c == '\u{2028}' || c == '\u{2029}')
1105        {
1106            let bad_char = self.advance().expect("peeked character must advance");
1107            let err = TabnasError::new(
1108                "unexpected",
1109                bad_char.to_string(),
1110                self.src,
1111                pnt.site.pos,
1112                pnt.site.ri,
1113                pnt.site.ci,
1114            );
1115            self.err = Some(err.clone());
1116            return Err(Box::new(err));
1117        }
1118
1119        if let Some(token) =
1120            self.run_custom_matchers(&mut custom_index, 5_000_000.0, pnt, &mut plugin)
1121        {
1122            return Ok(token);
1123        }
1124
1125        // 3. Quoted strings. These precede comments in the canonical matcher
1126        // order, so an overlapping quote/comment opener is a string unless
1127        // string matching explicitly abandons the malformed candidate.
1128        let string_skipped = if self.options.string.lex && self.wants(TIN_ST) {
1129            match self.run_check(self.options.string.check.clone(), pnt) {
1130                CheckFlow::Continue => false,
1131                CheckFlow::Skip => true,
1132                CheckFlow::Token(token) => return Ok(*token),
1133            }
1134        } else {
1135            false
1136        };
1137        if self.options.string.lex
1138            && !string_skipped
1139            && self.wants(TIN_ST)
1140            && self.char_sets.string.contains(c)
1141        {
1142            let start = (self.idx, self.ri, self.ci);
1143            match self.match_string(c, pnt) {
1144                result @ Ok(_) => return result,
1145                Err(error) if !self.options.string.abandon => return Err(error),
1146                Err(_) => {
1147                    (self.idx, self.ri, self.ci) = start;
1148                    self.err = None;
1149                }
1150            }
1151        }
1152
1153        if let Some(token) =
1154            self.run_custom_matchers(&mut custom_index, 6_000_000.0, pnt, &mut plugin)
1155        {
1156            return Ok(token);
1157        }
1158
1159        // 4. Comments (longest opening marker wins; ties sort by name).
1160        let comment_skipped = if self.options.comment.lex && self.wants(TIN_CM) {
1161            match self.run_check(self.options.comment.check.clone(), pnt) {
1162                CheckFlow::Continue => false,
1163                CheckFlow::Skip => true,
1164                CheckFlow::Token(token) => return Ok(*token),
1165            }
1166        } else {
1167            false
1168        };
1169        if self.options.comment.lex && !comment_skipped && self.wants(TIN_CM) {
1170            if let Some(token) = self.match_comment(pnt)? {
1171                return Ok(token);
1172            }
1173        }
1174
1175        if let Some(token) =
1176            self.run_custom_matchers(&mut custom_index, 7_000_000.0, pnt, &mut plugin)
1177        {
1178            return Ok(token);
1179        }
1180
1181        // 5. Numbers
1182        let number_skipped = if self.options.number.lex && self.wants(TIN_NR) {
1183            match self.run_check(self.options.number.check.clone(), pnt) {
1184                CheckFlow::Continue => false,
1185                CheckFlow::Skip => true,
1186                CheckFlow::Token(token) => return Ok(*token),
1187            }
1188        } else {
1189            false
1190        };
1191        if self.options.number.lex
1192            && !number_skipped
1193            && self.wants(TIN_NR)
1194            && (c == '-' || c == '+' || c == '.' || c.is_ascii_digit())
1195        {
1196            if let Some(tkn) = self.match_number(pnt)? {
1197                return Ok(tkn);
1198            }
1199        }
1200
1201        if let Some(token) =
1202            self.run_custom_matchers(&mut custom_index, 8_000_000.0, pnt, &mut plugin)
1203        {
1204            return Ok(token);
1205        }
1206
1207        // 6. Text and named/regex values share the same delimited run.
1208        // Negotiated lexing gates this combined family by its primary token
1209        // identity (#TX), matching the TypeScript and Go dispatchers. Once
1210        // entered, an exact or regexp value definition may still produce
1211        // #VL; the caller rejects and rolls that cut back when #VL was not
1212        // requested.
1213        let text_matcher_wanted = self.wants(TIN_TX);
1214        let value_lex = self.options.value.lex && text_matcher_wanted;
1215        let text_lex = self.options.text.lex && text_matcher_wanted;
1216        let text_skipped = if text_lex || value_lex {
1217            match self.run_check(self.options.text.check.clone(), pnt) {
1218                CheckFlow::Continue => false,
1219                CheckFlow::Skip => true,
1220                CheckFlow::Token(token) => return Ok(*token),
1221            }
1222        } else {
1223            false
1224        };
1225        if (text_lex || value_lex) && !text_skipped && !self.is_text_delimiter_here() {
1226            let start = (self.idx, self.ri, self.ci);
1227            // Only a `value` definition declaring `consume` looks at the
1228            // rest of the document, and the JSON grammar has none -- but
1229            // this ran for every text token, copying the whole tail of the
1230            // input each time. `self.src` is borrowed from the caller for
1231            // `'a` and is never reassigned, so reading the reference out
1232            // before the scan below gives a slice that does not borrow
1233            // `self` and survives the `&mut self` the scan needs.
1234            let source: &'a str = self.src;
1235            let remaining = &source[self.byte_position()..];
1236            let mut src = String::new();
1237            while let Some(ch) = self.peek() {
1238                if self.is_text_delimiter_here() {
1239                    break;
1240                }
1241                src.push(ch);
1242                self.advance();
1243            }
1244
1245            let mut output = None;
1246            if value_lex {
1247                if let Some(definition) = self
1248                    .options
1249                    .value
1250                    .definitions
1251                    .get(&src)
1252                    .filter(|definition| definition.matcher.is_none())
1253                    .cloned()
1254                {
1255                    output = Some(Token::new(
1256                        "#VL",
1257                        TIN_VL,
1258                        definition
1259                            .val
1260                            .clone()
1261                            .unwrap_or_else(|| Value::String(src.clone())),
1262                        src.clone(),
1263                        pnt,
1264                    ));
1265                }
1266
1267                if output.is_none() {
1268                    let mut definitions: Vec<_> = self
1269                        .options
1270                        .value
1271                        .definitions
1272                        .iter()
1273                        .filter(|(_, definition)| definition.matcher.is_some())
1274                        .map(|(name, definition)| (name.clone(), definition.clone()))
1275                        .collect();
1276                    definitions.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b));
1277                    for (_, definition) in definitions {
1278                        let regex = definition.matcher.as_ref().expect("filtered matcher");
1279                        let target: &str = if definition.consume { remaining } else { &src };
1280                        let Some(captures) = regex.captures(target) else {
1281                            continue;
1282                        };
1283                        let Some(found) = captures.get(0).filter(|found| found.start() == 0) else {
1284                            continue;
1285                        };
1286                        if !definition.consume && found.end() != target.len() {
1287                            continue;
1288                        }
1289                        let matched = found.as_str().to_string();
1290                        let value = definition.transform.as_ref().map_or_else(
1291                            || {
1292                                definition
1293                                    .val
1294                                    .clone()
1295                                    .unwrap_or_else(|| Value::String(matched.clone()))
1296                            },
1297                            |transform| {
1298                                let groups = captures
1299                                    .iter()
1300                                    .map(|capture| {
1301                                        capture
1302                                            .map_or_else(String::new, |value| value.as_str().into())
1303                                    })
1304                                    .collect::<Vec<_>>();
1305                                transform(&groups)
1306                            },
1307                        );
1308                        if definition.consume {
1309                            (self.idx, self.ri, self.ci) = start;
1310                            for _ in matched.chars() {
1311                                self.advance();
1312                            }
1313                        }
1314                        output = Some(Token::new("#VL", TIN_VL, value, matched, pnt));
1315                        break;
1316                    }
1317                }
1318            }
1319
1320            if output.is_none() && (!text_lex || text_skipped) {
1321                (self.idx, self.ri, self.ci) = start;
1322            } else if output.is_none() {
1323                output = Some(Token::new(
1324                    "#TX",
1325                    TIN_TX,
1326                    Value::String(src.clone()),
1327                    src,
1328                    pnt,
1329                ));
1330            }
1331
1332            if let Some(mut token) = output {
1333                let value = std::mem::replace(&mut token.val, Value::Undefined);
1334                token.val = self.modify_text_value(value, &mut plugin);
1335                return Ok(token);
1336            }
1337        }
1338
1339        if let Some(token) =
1340            self.run_custom_matchers(&mut custom_index, f64::INFINITY, pnt, &mut plugin)
1341        {
1342            return Ok(token);
1343        }
1344
1345        // 7. Unclaimed character -> Error: unexpected
1346        let bad_char = self.advance().unwrap();
1347        let err = TabnasError::new(
1348            "unexpected",
1349            bad_char.to_string(),
1350            self.src,
1351            pnt.site.pos,
1352            pnt.site.ri,
1353            pnt.site.ci,
1354        );
1355        self.err = Some(err.clone());
1356        Err(Box::new(err))
1357    }
1358
1359    fn match_comment(&mut self, pnt: Point) -> LexResult<Option<Token>> {
1360        let remaining = &self.src[self.byte_position()..];
1361        let mut definitions: Vec<_> = self
1362            .options
1363            .comment
1364            .definitions
1365            .iter()
1366            .filter(|(_, definition)| {
1367                // Same first-byte test as the fixed-token scan above.
1368                definition.start.as_bytes().first().copied()
1369                    == remaining.as_bytes().first().copied()
1370                    && !definition.start.is_empty()
1371                    && definition.lex
1372                    && remaining.starts_with(&definition.start)
1373            })
1374            .collect();
1375        definitions.sort_by(|(name_a, a), (name_b, b)| {
1376            b.start
1377                .len()
1378                .cmp(&a.start.len())
1379                .then_with(|| name_a.cmp(name_b))
1380        });
1381        let Some((_, definition)) = definitions.first() else {
1382            return Ok(None);
1383        };
1384        let definition = (*definition).clone();
1385        let mut src = String::new();
1386        for _ in definition.start.chars() {
1387            src.push(self.advance().expect("comment marker must advance"));
1388        }
1389
1390        let mut terminated_by_suffix = false;
1391        let mut closed = definition.line;
1392        loop {
1393            let remainder = &self.src[self.byte_position()..];
1394            let suffix = definition
1395                .suffixes
1396                .iter()
1397                .filter(|suffix| !suffix.is_empty() && remainder.starts_with(*suffix))
1398                .max_by_key(|suffix| suffix.len())
1399                .cloned();
1400            let suffix = suffix.or_else(|| {
1401                let matcher = definition.suffix_matcher.as_ref()?;
1402                let effect = matcher.run(remainder);
1403                if effect.is_some() {
1404                    return effect;
1405                }
1406                let saved = self.state();
1407                let wanted = self.want.clone();
1408                let token = matcher.run_imperative(self);
1409                self.restore(saved);
1410                self.want = wanted;
1411                token.map(|token| token.src.to_string())
1412            });
1413            let remainder = &self.src[self.byte_position()..];
1414            let suffix =
1415                suffix.filter(|suffix| !suffix.is_empty() && remainder.starts_with(suffix));
1416            if let Some(suffix) = suffix {
1417                for _ in suffix.chars() {
1418                    src.push(self.advance().expect("comment suffix must advance"));
1419                }
1420                terminated_by_suffix = true;
1421                closed = true;
1422                break;
1423            }
1424            if !definition.line
1425                && !definition.end.is_empty()
1426                && remainder.starts_with(&definition.end)
1427            {
1428                for _ in definition.end.chars() {
1429                    src.push(self.advance().expect("comment end must advance"));
1430                }
1431                closed = true;
1432                break;
1433            }
1434            let Some(ch) = self.peek() else {
1435                break;
1436            };
1437            if definition.line && (self.char_sets.line_ends.contains(ch)) {
1438                break;
1439            }
1440            src.push(self.advance().expect("comment body must advance"));
1441        }
1442
1443        if !closed {
1444            let err = TabnasError::new(
1445                "unterminated_comment",
1446                src,
1447                self.src,
1448                pnt.site.pos,
1449                pnt.site.ri,
1450                pnt.site.ci,
1451            );
1452            self.err = Some(err.clone());
1453            return Err(Box::new(err));
1454        }
1455
1456        if definition.eat_line && !terminated_by_suffix {
1457            while let Some(ch) = self.peek() {
1458                if !self.char_sets.line_ends.contains(ch) {
1459                    break;
1460                }
1461                src.push(self.advance().expect("comment line tail must advance"));
1462            }
1463        }
1464
1465        Ok(Some(Token::new(
1466            "#CM",
1467            TIN_CM,
1468            Value::String(src.clone()),
1469            src,
1470            pnt,
1471        )))
1472    }
1473
1474    fn match_number(&mut self, pnt: Point) -> LexResult<Option<Token>> {
1475        let start_idx = self.idx;
1476        let mut src = String::new();
1477
1478        // Optional sign.
1479        if matches!(self.peek(), Some('-' | '+')) {
1480            src.push(self.advance().unwrap());
1481        }
1482
1483        // Base-prefixed integers are complete at the final valid digit.
1484        if self.peek() == Some('0') {
1485            if let Some(prefix) = self.peek_at(1) {
1486                let radix = match prefix {
1487                    'x' | 'X' if self.options.number.hex => Some(16),
1488                    'o' | 'O' if self.options.number.oct => Some(8),
1489                    'b' | 'B' if self.options.number.bin => Some(2),
1490                    _ => None,
1491                };
1492                if let Some(radix) = radix {
1493                    src.push(self.advance().expect("peeked zero"));
1494                    src.push(self.advance().expect("peeked base prefix"));
1495                    let mut saw_digit = false;
1496                    while let Some(ch) = self.peek() {
1497                        if ch.is_digit(radix) {
1498                            saw_digit = true;
1499                            src.push(self.advance().expect("peeked base digit"));
1500                        } else if self
1501                            .options
1502                            .number
1503                            .sep
1504                            .as_ref()
1505                            .is_some_and(|separator| separator.contains(ch))
1506                        {
1507                            src.push(self.advance().expect("peeked base digit"));
1508                        } else {
1509                            break;
1510                        }
1511                    }
1512                    if saw_digit && self.is_text_delimiter_here() {
1513                        if self
1514                            .exclude_regex
1515                            .as_ref()
1516                            .is_some_and(|regex| regex.is_match(&src))
1517                        {
1518                            self.reset_number(start_idx, pnt);
1519                            return Ok(None);
1520                        }
1521                        if self.options.value.lex {
1522                            if let Some(definition) = self
1523                                .options
1524                                .value
1525                                .definitions
1526                                .get(&src)
1527                                .filter(|definition| definition.matcher.is_none())
1528                            {
1529                                return Ok(Some(Token::new(
1530                                    "#VL",
1531                                    TIN_VL,
1532                                    definition
1533                                        .val
1534                                        .clone()
1535                                        .unwrap_or_else(|| Value::String(src.clone())),
1536                                    src,
1537                                    pnt,
1538                                )));
1539                            }
1540                        }
1541                        // The digits of the literal, prefix, sign and any
1542                        // separators removed, folded as they are read. The
1543                        // fold keeps a bounded head, a digit count and a
1544                        // sticky bit, so a literal of any length costs the
1545                        // same handful of bytes: buffering the digits
1546                        // instead would let one long token multiply the
1547                        // memory the source already holds.
1548                        let mut fold = DigitFold::new(radix.trailing_zeros());
1549                        for ch in src.chars().skip_while(|ch| matches!(ch, '-' | '+')).skip(2) {
1550                            if self
1551                                .options
1552                                .number
1553                                .sep
1554                                .as_ref()
1555                                .is_some_and(|separator| separator.contains(ch))
1556                            {
1557                                continue;
1558                            }
1559                            fold.push(ch.to_digit(radix).expect("validated base digit"));
1560                        }
1561                        let mut value = fold.finish();
1562                        if src.starts_with('-') {
1563                            value = -value;
1564                        }
1565                        return Ok(Some(Token::new(
1566                            "#NR",
1567                            TIN_NR,
1568                            Value::Number(value),
1569                            src,
1570                            pnt,
1571                        )));
1572                    }
1573                    self.reset_number(start_idx, pnt);
1574                    return Ok(None);
1575                }
1576            }
1577        }
1578
1579        let Some(ch) = self.peek() else {
1580            self.reset_number(start_idx, pnt);
1581            return Ok(None);
1582        };
1583        if ch == '.' {
1584            if !self.peek_at(1).is_some_and(|next| next.is_ascii_digit()) {
1585                self.reset_number(start_idx, pnt);
1586                return Ok(None);
1587            }
1588            src.push(self.advance().expect("peeked leading decimal point"));
1589        } else if !ch.is_ascii_digit() {
1590            self.reset_number(start_idx, pnt);
1591            return Ok(None);
1592        }
1593
1594        let (has_digits, edge_separator) = self.scan_number_digits(&mut src);
1595        if !has_digits || edge_separator {
1596            self.reset_number(start_idx, pnt);
1597            return Ok(None);
1598        }
1599
1600        // The canonical regexp admits a trailing decimal point and an
1601        // exponent after it (`2.e3`), but declines `0.a` as one text run.
1602        if self.peek() == Some('.') {
1603            let next = self.peek_at(1);
1604            let exponent_after_dot = matches!(next, Some('e' | 'E'))
1605                && match self.peek_at(2) {
1606                    Some('+' | '-') => self.peek_at(3).is_some_and(|ch| ch.is_ascii_digit()),
1607                    Some(ch) => ch.is_ascii_digit(),
1608                    None => false,
1609                };
1610            if next.is_some_and(|ch| ch.is_ascii_digit()) {
1611                src.push(self.advance().expect("peeked decimal point"));
1612                let (_, edge_separator) = self.scan_number_digits(&mut src);
1613                if edge_separator {
1614                    self.reset_number(start_idx, pnt);
1615                    return Ok(None);
1616                }
1617            } else if next.is_some()
1618                && !self.is_text_delimiter_at(self.idx + 1)
1619                && next != Some('.')
1620                && !exponent_after_dot
1621            {
1622                self.reset_number(start_idx, pnt);
1623                return Ok(None);
1624            } else {
1625                src.push(self.advance().expect("peeked trailing decimal point"));
1626            }
1627        }
1628
1629        if matches!(self.peek(), Some('e' | 'E')) {
1630            let exponent_start = self.idx;
1631            let source_len = src.len();
1632            src.push(self.advance().expect("peeked exponent marker"));
1633            if matches!(self.peek(), Some('+' | '-')) {
1634                src.push(self.advance().expect("peeked exponent sign"));
1635            }
1636            let (has_exponent_digits, edge_separator) = self.scan_number_digits(&mut src);
1637            if edge_separator {
1638                self.reset_number(start_idx, pnt);
1639                return Ok(None);
1640            }
1641            if !has_exponent_digits {
1642                self.idx = exponent_start;
1643                src.truncate(source_len);
1644            }
1645        }
1646
1647        if !self.is_text_delimiter_here() {
1648            self.reset_number(start_idx, pnt);
1649            return Ok(None);
1650        }
1651
1652        // Check exclusion regex (e.g. ^00+)
1653        if let Some(ref re) = self.exclude_regex {
1654            if re.is_match(&src) {
1655                // Number is excluded, backtrack
1656                self.reset_number(start_idx, pnt);
1657                return Ok(None);
1658            }
1659        }
1660
1661        if self.options.value.lex {
1662            if let Some(definition) = self
1663                .options
1664                .value
1665                .definitions
1666                .get(&src)
1667                .filter(|definition| definition.matcher.is_none())
1668            {
1669                return Ok(Some(Token::new(
1670                    "#VL",
1671                    TIN_VL,
1672                    definition
1673                        .val
1674                        .clone()
1675                        .unwrap_or_else(|| Value::String(src.clone())),
1676                    src,
1677                    pnt,
1678                )));
1679            }
1680        }
1681
1682        // Parse float
1683        let parse_src = self.options.number.sep.as_ref().map_or_else(
1684            || src.clone(),
1685            |separator| src.chars().filter(|ch| !separator.contains(*ch)).collect(),
1686        );
1687        match parse_src.parse::<f64>() {
1688            Ok(num) => Ok(Some(Token::new(
1689                "#NR",
1690                TIN_NR,
1691                Value::Number(num),
1692                src,
1693                pnt,
1694            ))),
1695            Err(_) => {
1696                self.reset_number(start_idx, pnt);
1697                Ok(None)
1698            }
1699        }
1700    }
1701
1702    fn reset_number(&mut self, start_idx: usize, pnt: Point) {
1703        self.idx = start_idx;
1704        self.ri = pnt.site.ri;
1705        self.ci = pnt.site.ci;
1706    }
1707
1708    /// Consume a decimal digit/separator run. Separators are legal only
1709    /// between digits; a leading or trailing separator makes the whole run
1710    /// fall through to text, matching the TypeScript regexp and Go scanner.
1711    fn scan_number_digits(&mut self, src: &mut String) -> (bool, bool) {
1712        // The run is measured before any of it is consumed. Advancing
1713        // as it goes would hold `&mut self` across a read of
1714        // `self.options.number.sep`, and the way that used to be settled
1715        // was to clone the separator — an allocation and a free for
1716        // every number in the input, for a value that cannot change
1717        // while one number is being scanned.
1718        let run_start = self.idx;
1719        let mut saw_digit = false;
1720        let mut last_was_separator = false;
1721        let mut end = run_start;
1722        {
1723            let separator = self.options.number.sep.as_deref();
1724            while let Some(ch) = self.chars.get(end).copied() {
1725                if ch.is_ascii_digit() {
1726                    saw_digit = true;
1727                    last_was_separator = false;
1728                } else if separator.is_some_and(|separator| separator.contains(ch)) {
1729                    last_was_separator = true;
1730                } else {
1731                    break;
1732                }
1733                end += 1;
1734            }
1735        }
1736        while self.idx < end {
1737            src.push(self.advance().expect("scanned number character"));
1738        }
1739        let starts_with_separator = self.idx > run_start
1740            && self.options.number.sep.as_deref().is_some_and(|separator| {
1741                self.chars[run_start..self.idx]
1742                    .first()
1743                    .is_some_and(|ch| separator.contains(*ch))
1744            });
1745        (saw_digit, starts_with_separator || last_was_separator)
1746    }
1747
1748    fn match_string(&mut self, quote: char, pnt: Point) -> LexResult<Token> {
1749        let quote_char = self.advance().unwrap();
1750        let mut out_str = String::new();
1751        let mut raw_src = String::new();
1752        raw_src.push(quote_char);
1753
1754        let mut pending_high_surrogate: Option<u16> = None;
1755
1756        while let Some(c) = self.peek() {
1757            if c == quote {
1758                raw_src.push(self.advance().unwrap());
1759                // Rust strings cannot represent a lone UTF-16 surrogate, so
1760                // preserve the Go-port behavior and fold it to U+FFFD.
1761                self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
1762                return Ok(Token::new(
1763                    "#ST",
1764                    TIN_ST,
1765                    Value::String(out_str),
1766                    raw_src,
1767                    pnt,
1768                ));
1769            }
1770
1771            if let Some(replacement) = self.options.string.replace.get(&c).cloned() {
1772                raw_src.push(self.advance().expect("peeked character must advance"));
1773                self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
1774                out_str.push_str(&replacement);
1775                continue;
1776            }
1777
1778            if self.char_sets.line.contains(c) {
1779                if self.options.string.multi_chars.contains(quote) {
1780                    raw_src.push(self.advance().expect("peeked character must advance"));
1781                    out_str.push(c);
1782                    continue;
1783                }
1784                // Sited ON the line character, as TypeScript does
1785                // (`pnt.sI = sI; pnt.cI = cI` before its `bad()` call,
1786                // ts/src/lexer.ts) and as the control-character branch
1787                // below already does. `pnt` is the opening quote, and
1788                // reporting that put every embedded newline at the start
1789                // of its string.
1790                let site = self.current_point().site;
1791                let err = TabnasError::new(
1792                    "unprintable",
1793                    c.to_string(),
1794                    self.src,
1795                    site.pos,
1796                    site.ri,
1797                    site.ci,
1798                );
1799                self.err = Some(err.clone());
1800                return Err(Box::new(err));
1801            }
1802
1803            // Check for unprintable unescaped control characters in string (< 32)
1804            if (c as u32) < 32 && !self.options.string.allow_control {
1805                let err = TabnasError::new(
1806                    "unprintable",
1807                    c.to_string(),
1808                    self.src,
1809                    self.current_point().site.pos,
1810                    self.current_point().site.ri,
1811                    self.current_point().site.ci,
1812                );
1813                self.err = Some(err.clone());
1814                return Err(Box::new(err));
1815            }
1816
1817            if c == self.options.string.escape_char {
1818                raw_src.push(self.advance().unwrap());
1819                let esc_point = self.current_point();
1820                if let Some(esc) = self.advance() {
1821                    raw_src.push(esc);
1822                    if let Some(replacement) = self.options.string.escape.get(&esc).cloned() {
1823                        self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
1824                        out_str.push_str(&replacement);
1825                        continue;
1826                    }
1827                    match esc {
1828                        'u' => {
1829                            // Unicode escape: \uXXXX or \u{X...}. An
1830                            // invalid one is reported on the backslash
1831                            // with the span TypeScript cuts: six source
1832                            // characters for the fixed-width form, four
1833                            // for `\x`, and through the closing brace
1834                            // (or to the end of the source) for the
1835                            // braced form -- clipped, never padded, so a
1836                            // truncated escape at end of input reports
1837                            // exactly the characters that are there.
1838                            if self.peek() == Some('{') && !self.options.string.escape_strict {
1839                                raw_src.push(self.advance().unwrap()); // '{'
1840                                let mut hex = String::new();
1841                                let mut closed = false;
1842                                while let Some(h) = self.peek() {
1843                                    if h == '}' {
1844                                        raw_src.push(self.advance().unwrap());
1845                                        closed = true;
1846                                        break;
1847                                    }
1848                                    raw_src.push(self.advance().unwrap());
1849                                    hex.push(h);
1850                                }
1851
1852                                if !closed
1853                                    || hex.is_empty()
1854                                    || hex.len() > 6
1855                                    || !hex.chars().all(|ch| ch.is_ascii_hexdigit())
1856                                {
1857                                    let err = TabnasError::new(
1858                                        "invalid_unicode",
1859                                        self.source_span(esc_point.site.pos - 1, self.idx),
1860                                        self.src,
1861                                        esc_point.site.pos - 1,
1862                                        esc_point.site.ri,
1863                                        esc_point.site.ci - 1,
1864                                    );
1865                                    self.err = Some(err.clone());
1866                                    return Err(Box::new(err));
1867                                }
1868
1869                                let cp = match u32::from_str_radix(&hex, 16) {
1870                                    Ok(val) if val <= 0x10FFFF => val,
1871                                    _ => {
1872                                        let err = TabnasError::new(
1873                                            "invalid_unicode",
1874                                            self.source_span(esc_point.site.pos - 1, self.idx),
1875                                            self.src,
1876                                            esc_point.site.pos - 1,
1877                                            esc_point.site.ri,
1878                                            esc_point.site.ci - 1,
1879                                        );
1880                                        self.err = Some(err.clone());
1881                                        return Err(Box::new(err));
1882                                    }
1883                                };
1884
1885                                self.emit_unicode_escape(
1886                                    cp,
1887                                    &mut pending_high_surrogate,
1888                                    &mut out_str,
1889                                );
1890                            } else {
1891                                // Exactly 4 hex digits: \uXXXX
1892                                let mut hex = String::new();
1893                                for _ in 0..4 {
1894                                    if let Some(h) = self.peek() {
1895                                        if h.is_ascii_hexdigit() {
1896                                            raw_src.push(self.advance().unwrap());
1897                                            hex.push(h);
1898                                        } else {
1899                                            break;
1900                                        }
1901                                    } else {
1902                                        break;
1903                                    }
1904                                }
1905
1906                                if hex.len() != 4 {
1907                                    let err = TabnasError::new(
1908                                        "invalid_unicode",
1909                                        self.source_span(
1910                                            esc_point.site.pos - 1,
1911                                            esc_point.site.pos + 5,
1912                                        ),
1913                                        self.src,
1914                                        esc_point.site.pos - 1,
1915                                        esc_point.site.ri,
1916                                        esc_point.site.ci - 1,
1917                                    );
1918                                    self.err = Some(err.clone());
1919                                    return Err(Box::new(err));
1920                                }
1921
1922                                let cp = u16::from_str_radix(&hex, 16).map_err(|_| {
1923                                    let err = TabnasError::new(
1924                                        "invalid_unicode",
1925                                        self.source_span(
1926                                            esc_point.site.pos - 1,
1927                                            esc_point.site.pos + 5,
1928                                        ),
1929                                        self.src,
1930                                        esc_point.site.pos - 1,
1931                                        esc_point.site.ri,
1932                                        esc_point.site.ci - 1,
1933                                    );
1934                                    self.err = Some(err.clone());
1935                                    err
1936                                })?;
1937
1938                                self.emit_unicode_escape(
1939                                    u32::from(cp),
1940                                    &mut pending_high_surrogate,
1941                                    &mut out_str,
1942                                );
1943                            }
1944                        }
1945                        'x' if !self.options.string.escape_strict => {
1946                            let mut hex = String::new();
1947                            for _ in 0..2 {
1948                                if let Some(h) = self.peek() {
1949                                    if h.is_ascii_hexdigit() {
1950                                        raw_src.push(
1951                                            self.advance().expect("peeked character must advance"),
1952                                        );
1953                                        hex.push(h);
1954                                    }
1955                                }
1956                            }
1957                            if hex.len() != 2 {
1958                                let err = TabnasError::new(
1959                                    "invalid_ascii",
1960                                    self.source_span(
1961                                        esc_point.site.pos - 1,
1962                                        esc_point.site.pos + 3,
1963                                    ),
1964                                    self.src,
1965                                    esc_point.site.pos - 1,
1966                                    esc_point.site.ri,
1967                                    esc_point.site.ci - 1,
1968                                );
1969                                self.err = Some(err.clone());
1970                                return Err(Box::new(err));
1971                            }
1972                            let byte = u8::from_str_radix(&hex, 16).expect("validated ASCII hex");
1973                            self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
1974                            out_str.push(char::from(byte));
1975                        }
1976                        other => {
1977                            if !self.options.string.allow_unknown {
1978                                // Sited on the escape CHARACTER with a
1979                                // one-character span, as TypeScript
1980                                // (`pnt.sI = sI; pnt.cI = cI; lex.bad(
1981                                // S.unexpected, sI, sI + 1)`) and Go do;
1982                                // the other escape errors sit on the
1983                                // backslash and span the construct.
1984                                let err = TabnasError::new(
1985                                    "unexpected",
1986                                    other.to_string(),
1987                                    self.src,
1988                                    esc_point.site.pos,
1989                                    esc_point.site.ri,
1990                                    esc_point.site.ci,
1991                                );
1992                                self.err = Some(err.clone());
1993                                return Err(Box::new(err));
1994                            }
1995                            self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
1996                            out_str.push(other);
1997                        }
1998                    }
1999                } else {
2000                    let err = TabnasError::new(
2001                        "unterminated_string",
2002                        raw_src,
2003                        self.src,
2004                        pnt.site.pos,
2005                        pnt.site.ri,
2006                        pnt.site.ci,
2007                    );
2008                    self.err = Some(err.clone());
2009                    return Err(Box::new(err));
2010                }
2011            } else {
2012                self.flush_surrogate(&mut pending_high_surrogate, &mut out_str);
2013                raw_src.push(self.advance().unwrap());
2014                out_str.push(c);
2015            }
2016        }
2017
2018        let err = TabnasError::new(
2019            "unterminated_string",
2020            raw_src,
2021            self.src,
2022            pnt.site.pos,
2023            pnt.site.ri,
2024            pnt.site.ci,
2025        );
2026        self.err = Some(err.clone());
2027        Err(Box::new(err))
2028    }
2029
2030    fn flush_surrogate(&self, pending: &mut Option<u16>, out: &mut String) {
2031        if pending.take().is_some() {
2032            out.push('\u{FFFD}');
2033        }
2034    }
2035
2036    /// Emit one decoded Unicode escape while pairing UTF-16 surrogate code
2037    /// units across both `\\uXXXX` and `\\u{...}` spellings.
2038    fn emit_unicode_escape(&self, cp: u32, pending: &mut Option<u16>, out: &mut String) {
2039        if (0xD800..=0xDBFF).contains(&cp) {
2040            self.flush_surrogate(pending, out);
2041            *pending = Some(cp as u16);
2042        } else if (0xDC00..=0xDFFF).contains(&cp) {
2043            if let Some(high) = pending.take() {
2044                let scalar = 0x10000 + (((u32::from(high)) - 0xD800) << 10) + (cp - 0xDC00);
2045                out.push(char::from_u32(scalar).expect("paired surrogates form a Unicode scalar"));
2046            } else {
2047                out.push('\u{FFFD}');
2048            }
2049        } else {
2050            self.flush_surrogate(pending, out);
2051            out.push(char::from_u32(cp).expect("validated escape is a Unicode scalar"));
2052        }
2053    }
2054}
2055
2056// ---------------------------------------------------------------------------
2057// Base-prefixed integer literals.
2058//
2059// A `0x`, `0o` or `0b` literal is read as an EXACT integer and rounded to
2060// a double ONCE. The obvious fold -- `value = value * radix + digit` in
2061// `f64` -- rounds at every digit, and past the 53-bit exact integer range
2062// those roundings accumulate: `0Xa6f2f78f4f9bf44` came out as
2063// `43a4de5ef1e9f37e` where canonical TypeScript and the Go port both
2064// answer `43a4de5ef1e9f37f`, one unit in the last place low. That is
2065// silently altered data, not a formatting difference.
2066//
2067// TypeScript coerces the literal with unary `+`, whose StringNumericValue
2068// is the exact mathematical value of the digits rounded once, half to
2069// even; Go reads it through `big.Int` and `big.Float.Float64()`, which is
2070// the same rule. These reproduce it. Only the VALUE is affected: which
2071// literals are accepted, and the token they become, are settled by
2072// `match_number` before any of this runs.
2073//
2074// The decimal path needs none of it -- `str::parse::<f64>` is correctly
2075// rounded for a digit string of any length.
2076// ---------------------------------------------------------------------------
2077
2078/// `2^k` for a non-negative `k`, exactly, saturating to infinity above the
2079/// double range. A repeated multiply would round on the way up.
2080fn pow2(k: i64) -> f64 {
2081    debug_assert!(k >= 0, "only non-negative exponents arise here");
2082    if k > 1023 {
2083        f64::INFINITY
2084    } else {
2085        f64::from_bits(((k + 1023) as u64) << 52)
2086    }
2087}
2088
2089/// Folds the digits of a base-prefixed literal into the NEAREST double,
2090/// rounding half to even, without holding the digits.
2091///
2092/// `bits` is the width of one digit, so the base is a power of two: 1 for
2093/// binary, 3 for octal, 4 for hexadecimal. Those are the only bases
2094/// `match_number` reads, which is what lets a `u128` head plus a sticky
2095/// bit stand in for arbitrary-precision arithmetic.
2096///
2097/// Only three things about a literal can change the answer: the top
2098/// `128 / bits` significant digits, how many digits follow them, and
2099/// whether any of those is non-zero. This keeps exactly those, so the
2100/// space it costs does not grow with the literal, however long an
2101/// untrusted document makes one.
2102struct DigitFold {
2103    bits: u32,
2104    /// The significant digits packed so far, at most `head_len` of them.
2105    head: u128,
2106    /// How many significant digits have been pushed, head and tail alike.
2107    len: usize,
2108    /// Whether any digit past the head was non-zero.
2109    sticky: bool,
2110    /// Whether a non-zero digit has been seen. Leading zeros carry no
2111    /// value, and dropping them is what makes the head wider than the 54
2112    /// significant bits the rounding needs.
2113    started: bool,
2114}
2115
2116impl DigitFold {
2117    fn new(bits: u32) -> Self {
2118        debug_assert!(
2119            (1..=4).contains(&bits),
2120            "only the power-of-two bases the lexer reads"
2121        );
2122        DigitFold {
2123            bits,
2124            head: 0,
2125            len: 0,
2126            sticky: false,
2127            started: false,
2128        }
2129    }
2130
2131    /// A `u128` holds exactly this many digits of the base.
2132    fn head_len(&self) -> usize {
2133        (128 / self.bits) as usize
2134    }
2135
2136    fn push(&mut self, digit: u32) {
2137        if !self.started {
2138            if 0 == digit {
2139                return;
2140            }
2141            self.started = true;
2142        }
2143        if self.len < self.head_len() {
2144            self.head = (self.head << self.bits) | u128::from(digit);
2145        } else if 0 != digit {
2146            self.sticky = true;
2147        }
2148        self.len += 1;
2149    }
2150
2151    fn finish(&self) -> f64 {
2152        if !self.started {
2153            return 0.0;
2154        }
2155        let head_len = self.head_len();
2156        if self.len <= head_len {
2157            // A `u128` to `f64` cast rounds to nearest, ties to even, which
2158            // is the rule the canonical runtime follows.
2159            return self.head as f64;
2160        }
2161
2162        // Longer than a u128: the head holds the top `head_len` digits and
2163        // `sticky` remembers whether anything below them was set. Those two
2164        // are all the rounding can depend on. The leading digit is
2165        // non-zero, so the head is at least 121 bits wide in every base
2166        // here and `shift` is comfortably positive.
2167        let dropped = i64::from(self.bits) * (self.len - head_len) as i64;
2168        let shift = 128 - self.head.leading_zeros() - 53;
2169
2170        let mut mantissa = (self.head >> shift) as u64;
2171        let half = (self.head >> (shift - 1)) & 1 == 1;
2172        let sticky = self.head & ((1u128 << (shift - 1)) - 1) != 0 || self.sticky;
2173        if half && (sticky || mantissa & 1 == 1) {
2174            // At most 2^53, which is still an exact double.
2175            mantissa += 1;
2176        }
2177        // The mantissa carries at most 53 significant bits, so the scaling
2178        // is exact inside the double range and overflows to infinity
2179        // outside it.
2180        mantissa as f64 * pow2(dropped + i64::from(shift))
2181    }
2182}