Skip to main content

antlr4_runtime/
lexer.rs

1use std::cell::RefCell;
2use std::collections::{BTreeSet, HashMap};
3use std::hash::BuildHasherDefault;
4use std::rc::Rc;
5
6use crate::atn::Atn;
7use crate::char_stream::{CharStream, TextInterval};
8use crate::int_stream::EOF;
9use crate::prediction::PredictionFxHasher;
10use crate::recognizer::{Recognizer, RecognizerData};
11use crate::token::{CommonToken, CommonTokenFactory, TokenFactory, TokenSourceError, TokenSpec};
12
13#[allow(clippy::disallowed_types)]
14type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PredictionFxHasher>>;
15
16pub const SKIP: i32 = -3;
17pub const MORE: i32 = -2;
18pub const DEFAULT_MODE: i32 = 0;
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub struct LexerMode(pub i32);
22
23/// Grammar-specific lexer action reached on the accepted ATN path.
24///
25/// ANTLR serializes embedded lexer actions as `(rule_index, action_index)`
26/// pairs. The runtime also records the input position where the action was
27/// reached so generated code can evaluate templates such as `Text()` at the
28/// same point as a generated ANTLR lexer, not only at the token end.
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct LexerCustomAction {
31    rule_index: i32,
32    action_index: i32,
33    position: usize,
34}
35
36impl LexerCustomAction {
37    /// Creates a custom lexer action event from serialized ATN metadata.
38    pub const fn new(rule_index: i32, action_index: i32, position: usize) -> Self {
39        Self {
40            rule_index,
41            action_index,
42            position,
43        }
44    }
45
46    /// Lexer rule index that owns the embedded action.
47    pub const fn rule_index(self) -> i32 {
48        self.rule_index
49    }
50
51    /// Per-rule action index assigned by ANTLR serialization.
52    pub const fn action_index(self) -> i32 {
53        self.action_index
54    }
55
56    /// Character-stream position at which the action transition was reached.
57    pub const fn position(self) -> usize {
58        self.position
59    }
60}
61
62/// Grammar-specific lexer predicate reached while exploring an ATN path.
63#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub struct LexerPredicate {
65    rule_index: usize,
66    pred_index: usize,
67    position: usize,
68}
69
70impl LexerPredicate {
71    /// Creates a lexer predicate event from serialized ATN metadata.
72    pub const fn new(rule_index: usize, pred_index: usize, position: usize) -> Self {
73        Self {
74            rule_index,
75            pred_index,
76            position,
77        }
78    }
79
80    /// Lexer rule index that owns the predicate transition.
81    pub const fn rule_index(self) -> usize {
82        self.rule_index
83    }
84
85    /// Per-rule predicate index assigned by ANTLR serialization.
86    pub const fn pred_index(self) -> usize {
87        self.pred_index
88    }
89
90    /// Character-stream position at which the predicate is evaluated.
91    pub const fn position(self) -> usize {
92        self.position
93    }
94}
95
96/// Lexer reference held by [`LexerSemCtx`]. A semantic *predicate* is evaluated
97/// speculatively and gets a shared borrow; a *custom action* runs on the
98/// committed path and gets a mutable borrow so a hook can change lexer state
99/// (mode stack), matching the closure-based `custom_action` API.
100#[derive(Debug)]
101enum LexerRef<'a, I, F>
102where
103    I: CharStream,
104    F: TokenFactory,
105{
106    Shared(&'a BaseLexer<I, F>),
107    Mut(&'a mut BaseLexer<I, F>),
108}
109
110impl<I, F> LexerRef<'_, I, F>
111where
112    I: CharStream,
113    F: TokenFactory,
114{
115    const fn get(&self) -> &BaseLexer<I, F> {
116        match self {
117            LexerRef::Shared(lexer) => lexer,
118            LexerRef::Mut(lexer) => lexer,
119        }
120    }
121}
122
123/// Runtime view passed to lexer semantic hooks.
124#[derive(Debug)]
125pub struct LexerSemCtx<'a, I, F = CommonTokenFactory>
126where
127    I: CharStream,
128    F: TokenFactory,
129{
130    lexer: LexerRef<'a, I, F>,
131    rule_index: usize,
132    coordinate_index: usize,
133    position: usize,
134}
135
136impl<'a, I, F> LexerSemCtx<'a, I, F>
137where
138    I: CharStream,
139    F: TokenFactory,
140{
141    pub(crate) const fn new(
142        lexer: &'a BaseLexer<I, F>,
143        rule_index: usize,
144        coordinate_index: usize,
145        position: usize,
146    ) -> Self {
147        Self {
148            lexer: LexerRef::Shared(lexer),
149            rule_index,
150            coordinate_index,
151            position,
152        }
153    }
154
155    /// Builds a context with a mutable lexer borrow, for a custom-action hook
156    /// that may change lexer state (mode stack). See [`Self::push_mode`] etc.
157    pub(crate) const fn new_mut(
158        lexer: &'a mut BaseLexer<I, F>,
159        rule_index: usize,
160        coordinate_index: usize,
161        position: usize,
162    ) -> Self {
163        Self {
164            lexer: LexerRef::Mut(lexer),
165            rule_index,
166            coordinate_index,
167            position,
168        }
169    }
170
171    /// Lexer rule index that owns the predicate/action coordinate.
172    #[must_use]
173    pub const fn rule_index(&self) -> usize {
174        self.rule_index
175    }
176
177    /// Predicate/action index inside the owning lexer rule.
178    #[must_use]
179    pub const fn coordinate_index(&self) -> usize {
180        self.coordinate_index
181    }
182
183    /// Absolute input position where the predicate/action transition fired.
184    #[must_use]
185    pub const fn position(&self) -> usize {
186        self.position
187    }
188
189    /// Lexer mode at this coordinate.
190    #[must_use]
191    pub fn mode(&self) -> i32 {
192        self.lexer.get().mode()
193    }
194
195    /// Current source column.
196    #[must_use]
197    pub const fn column(&self) -> usize {
198        self.lexer.get().column()
199    }
200
201    /// Source column at [`Self::position`].
202    #[must_use]
203    pub fn position_column(&self) -> usize {
204        self.lexer.get().column_at(self.position)
205    }
206
207    /// Column captured at the current token start.
208    #[must_use]
209    pub const fn token_start_column(&self) -> usize {
210        self.lexer.get().token_start_column()
211    }
212
213    /// Text matched from token start to this coordinate.
214    #[must_use]
215    pub fn text_so_far(&self) -> String {
216        self.lexer.get().token_text_until(self.position)
217    }
218
219    /// Sets the current lexer mode. Available only from a custom-action hook
220    /// (the mutable-borrow context); a no-op with a warning path for the
221    /// speculative predicate context, where mutating lexer state is invalid.
222    ///
223    /// Returns `true` if the mutation was applied (action context), `false` if
224    /// it was ignored (predicate context).
225    pub fn set_mode(&mut self, mode: i32) -> bool {
226        match &mut self.lexer {
227            LexerRef::Mut(lexer) => {
228                lexer.set_mode(mode);
229                true
230            }
231            LexerRef::Shared(_) => false,
232        }
233    }
234
235    /// Pushes the current mode and switches to `mode`. Action context only; see
236    /// [`Self::set_mode`] for the return value.
237    pub fn push_mode(&mut self, mode: i32) -> bool {
238        match &mut self.lexer {
239            LexerRef::Mut(lexer) => {
240                lexer.push_mode(mode);
241                true
242            }
243            LexerRef::Shared(_) => false,
244        }
245    }
246
247    /// Pops the mode stack, restoring the previous mode. Action context only;
248    /// returns the popped mode (`None` if the stack was empty or this is a
249    /// predicate context).
250    pub fn pop_mode(&mut self) -> Option<i32> {
251        match &mut self.lexer {
252            LexerRef::Mut(lexer) => lexer.pop_mode(),
253            LexerRef::Shared(_) => None,
254        }
255    }
256}
257
258pub trait Lexer: Recognizer {
259    fn mode(&self) -> i32;
260    fn set_mode(&mut self, mode: i32);
261    fn push_mode(&mut self, mode: i32);
262    fn pop_mode(&mut self) -> Option<i32>;
263}
264
265#[derive(Clone, Debug)]
266pub struct BaseLexer<I, F = CommonTokenFactory> {
267    input: I,
268    data: RecognizerData,
269    factory: F,
270    mode: i32,
271    mode_stack: Vec<i32>,
272    token_start: usize,
273    token_start_line: usize,
274    token_start_column: usize,
275    line: usize,
276    column: usize,
277    hit_eof: bool,
278    force_interpreted: bool,
279    errors: RefCell<Vec<TokenSourceError>>,
280    semantic_error_coordinates: RefCell<BTreeSet<(u8, usize, usize, usize)>>,
281    dfa_cache: Rc<RefCell<LexerDfaCache>>,
282}
283
284/// Learned lexer DFA: the input-independent state/transition tables built up
285/// by ATN simulation.
286///
287/// Semantic-predicate-dependent states are stored flagged and every consumer
288/// re-simulates them instead of trusting their cached data, so the cache can
289/// be shared across lexer instances (and inputs) for the same ATN — see
290/// [`BaseLexer::with_shared_dfa`].
291#[derive(Clone, Debug, Default)]
292struct LexerDfaCache {
293    state_numbers: FxHashMap<LexerDfaKey, usize>,
294    accept_predictions: FxHashMap<usize, i32>,
295    /// `showDFA` edge trace. Lives with the tables it describes, so a lexer
296    /// on a shared cache reports the accumulated DFA — matching the reference
297    /// runtimes, whose static shared DFA is what `showDFA` prints.
298    edges: BTreeSet<LexerDfaEdge>,
299    /// Dense by DFA state number (states are numbered contiguously from 0).
300    cached_states: Vec<Option<Rc<LexerDfaCachedState>>>,
301    /// Per-source-state edge rows for symbols in `0..DENSE_EDGE_SYMBOLS`,
302    /// allocated lazily on the first cached transition out of a state. The
303    /// per-character lookup is then one bounds check and an array index —
304    /// the same scheme as Go's `edges[t-MinDFAEdge]`.
305    dense_edges: Vec<Option<Box<DenseEdgeRow>>>,
306    /// Transitions on symbols outside the dense range (supplementary planes).
307    sparse_edges: FxHashMap<(usize, i32), LexerDfaCachedTransition>,
308    mode_starts: FxHashMap<i32, usize>,
309}
310
311/// Dense-row width: ASCII, matching the reference runtimes' DFA edge arrays.
312const DENSE_EDGE_SYMBOLS: usize = 128;
313
314type DenseEdgeRow = [LexerDfaCachedTransition; DENSE_EDGE_SYMBOLS];
315
316/// Sentinel for an empty dense-row slot; no real transition targets it
317/// because DFA state numbers are assigned contiguously from 0.
318const EMPTY_DENSE_EDGE: LexerDfaCachedTransition = LexerDfaCachedTransition {
319    target_state: usize::MAX,
320    position_delta: 0,
321};
322
323thread_local! {
324    /// Learned lexer DFAs shared across lexer instances, keyed by a generated
325    /// lexer's static ATN identity (mirrors the parser's shared decision DFAs).
326    static SHARED_LEXER_DFA_CACHES: RefCell<HashMap<usize, Rc<RefCell<LexerDfaCache>>>> =
327        RefCell::new(HashMap::new());
328}
329
330/// Normalized lexer ATN config-set identity used for observed DFA traces.
331#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
332pub(crate) struct LexerDfaKey {
333    configs: Vec<LexerDfaConfigKey>,
334}
335
336impl LexerDfaKey {
337    pub(crate) fn new(mut configs: Vec<LexerDfaConfigKey>) -> Self {
338        configs.sort_unstable();
339        Self { configs }
340    }
341}
342
343/// One lexer ATN config identity with the absolute input position removed.
344#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
345pub(crate) struct LexerDfaConfigKey {
346    pub(crate) state: usize,
347    pub(crate) alt_rule_index: Option<usize>,
348    pub(crate) consumed_eof: bool,
349    pub(crate) passed_non_greedy: bool,
350    pub(crate) stack: Vec<usize>,
351    pub(crate) actions: Vec<LexerDfaActionKey>,
352}
353
354#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
355pub(crate) struct LexerDfaActionKey {
356    pub(crate) action_index: usize,
357    pub(crate) position_delta: usize,
358    pub(crate) rule_index: usize,
359}
360
361impl LexerDfaConfigKey {
362    pub(crate) const fn new(
363        state: usize,
364        alt_rule_index: Option<usize>,
365        consumed_eof: bool,
366        passed_non_greedy: bool,
367        stack: Vec<usize>,
368        actions: Vec<LexerDfaActionKey>,
369    ) -> Self {
370        Self {
371            state,
372            alt_rule_index,
373            consumed_eof,
374            passed_non_greedy,
375            stack,
376            actions,
377        }
378    }
379}
380
381#[derive(Clone, Copy, Debug)]
382pub(crate) struct LexerDfaCachedTransition {
383    pub(crate) target_state: usize,
384    pub(crate) position_delta: usize,
385}
386
387#[derive(Clone, Debug)]
388pub(crate) struct LexerDfaCachedAccept {
389    pub(crate) position_delta: usize,
390    pub(crate) rule_index: usize,
391    pub(crate) consumed_eof: bool,
392    pub(crate) actions: Vec<LexerDfaActionKey>,
393}
394
395#[derive(Clone, Debug)]
396pub(crate) struct LexerDfaCachedState {
397    pub(crate) has_semantic_context: bool,
398    pub(crate) configs: Vec<LexerDfaConfigKey>,
399    pub(crate) accept: Option<LexerDfaCachedAccept>,
400}
401
402/// One printable lexer DFA edge keyed so repeated matches keep deterministic
403/// output order.
404#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
405struct LexerDfaEdge {
406    from: usize,
407    symbol: i32,
408    to: usize,
409}
410
411impl<I> BaseLexer<I>
412where
413    I: CharStream,
414{
415    /// Creates a lexer base using `CommonTokenFactory`.
416    pub fn new(input: I, data: RecognizerData) -> Self {
417        Self::with_factory(input, data, CommonTokenFactory)
418    }
419}
420
421impl<I, F> BaseLexer<I, F>
422where
423    I: CharStream,
424    F: TokenFactory,
425{
426    /// Creates a lexer base with a custom token factory.
427    pub fn with_factory(input: I, data: RecognizerData, factory: F) -> Self {
428        Self {
429            input,
430            data,
431            factory,
432            mode: DEFAULT_MODE,
433            mode_stack: Vec::new(),
434            token_start: 0,
435            token_start_line: 1,
436            token_start_column: 0,
437            line: 1,
438            column: 0,
439            hit_eof: false,
440            force_interpreted: false,
441            errors: RefCell::new(Vec::new()),
442            semantic_error_coordinates: RefCell::new(BTreeSet::new()),
443            dfa_cache: Rc::new(RefCell::new(LexerDfaCache::default())),
444        }
445    }
446
447    /// Switches this lexer to the thread-shared learned DFA for `atn`.
448    ///
449    /// Generated lexers create a fresh instance per parse; without sharing,
450    /// every instance relearns the same DFA through ATN simulation. The shared
451    /// cache is keyed by the generated lexer's `&'static Atn` identity and
452    /// holds only input-independent data, so it stays valid across inputs.
453    /// The `showDFA` edge trace lives in the cache too, so it reports the
454    /// accumulated DFA — the same view the reference runtimes print from
455    /// their static shared DFA.
456    #[must_use]
457    pub fn with_shared_dfa(mut self, atn: &'static Atn) -> Self {
458        let ptr: *const Atn = atn;
459        let key = ptr as usize;
460        self.dfa_cache = SHARED_LEXER_DFA_CACHES
461            .with(|caches| Rc::clone(caches.borrow_mut().entry(key).or_insert_with(Rc::default)));
462        self
463    }
464
465    pub const fn input(&self) -> &I {
466        &self.input
467    }
468
469    pub const fn input_mut(&mut self) -> &mut I {
470        &mut self.input
471    }
472
473    /// Captures the input index and source position for the token currently
474    /// being matched.
475    pub fn begin_token(&mut self) {
476        self.semantic_error_coordinates.get_mut().clear();
477        self.token_start = self.input.index();
478        self.token_start_line = self.line;
479        self.token_start_column = self.column;
480    }
481
482    /// Returns the absolute character index where the current token began.
483    pub const fn token_start(&self) -> usize {
484        self.token_start
485    }
486
487    /// Returns the source line captured at the start of the current token.
488    pub const fn token_start_line(&self) -> usize {
489        self.token_start_line
490    }
491
492    /// Returns the source column captured at the start of the current token.
493    pub const fn token_start_column(&self) -> usize {
494        self.token_start_column
495    }
496
497    /// Consumes one character from the input stream and updates lexer line and
498    /// column counters.
499    ///
500    /// The input stream is indexed by Unicode scalar values. Newline handling
501    /// follows ANTLR's default convention of incrementing the line and resetting
502    /// the column after `\n`.
503    pub fn consume_char(&mut self) {
504        let la = self.input.la(1);
505        if la == EOF {
506            return;
507        }
508        self.input.consume();
509        if char::from_u32(la.cast_unsigned()) == Some('\n') {
510            self.line += 1;
511            self.column = 0;
512        } else {
513            self.column += 1;
514        }
515    }
516
517    /// Rewinds or advances the input cursor to a token accept boundary.
518    ///
519    /// Some generated lexers intentionally accept a longer path to disambiguate
520    /// a token, then emit only the prefix and leave the suffix for the next
521    /// token. Recomputing line/column from `token_start` keeps the visible lexer
522    /// position consistent after moving the cursor backwards.
523    pub fn reset_accept_position(&mut self, index: usize) {
524        let target = index.max(self.token_start);
525        self.input.seek(self.token_start);
526        self.line = self.token_start_line;
527        self.column = self.token_start_column;
528        while self.input.index() < target && self.input.la(1) != EOF {
529            self.consume_char();
530        }
531    }
532
533    /// Builds a token spanning from the current token start to the character
534    /// before the input cursor.
535    ///
536    /// When generated or interpreted lexer code does not supply explicit text,
537    /// the base lexer captures the matched source interval so downstream token
538    /// streams and parse trees can render token text without retaining a source
539    /// pair object.
540    pub fn emit(&self, token_type: i32, channel: i32, text: Option<String>) -> CommonToken {
541        let stop = self.input.index().checked_sub(1).unwrap_or(usize::MAX);
542        self.emit_with_stop(token_type, channel, stop, text)
543    }
544
545    /// Builds a token with an explicit stop index.
546    ///
547    /// EOF-matching lexer rules do not consume a Unicode scalar value, so their
548    /// stop index can be one before the current input index. The caller passes
549    /// `usize::MAX` to represent ANTLR's `-1` stop index at empty input.
550    pub fn emit_with_stop(
551        &self,
552        token_type: i32,
553        channel: i32,
554        stop: usize,
555        text: Option<String>,
556    ) -> CommonToken {
557        let text = text.or_else(|| {
558            if stop == usize::MAX {
559                Some("<EOF>".to_owned())
560            } else {
561                None
562            }
563        });
564        let source_interval = if text.is_none() && stop != usize::MAX && self.token_start <= stop {
565            self.input
566                .text_source_interval(TextInterval::new(self.token_start, stop))
567        } else {
568            None
569        };
570        let source_text = source_interval
571            .as_ref()
572            .and_then(|(input, start_byte, stop_byte)| {
573                Some(crate::token::TokenSourceText {
574                    input: Rc::clone(input),
575                    start_byte: u32::try_from(*start_byte).ok()?,
576                    stop_byte: u32::try_from(*stop_byte).ok()?,
577                })
578            });
579        let source_byte_span = source_text
580            .as_ref()
581            .map(|source_text| (source_text.start_byte, source_text.stop_byte));
582        let text = text.or_else(|| {
583            source_text
584                .is_none()
585                .then(|| self.input.text(TextInterval::new(self.token_start, stop)))
586        });
587        let mut token = self.factory.create(TokenSpec {
588            token_type,
589            channel,
590            start: self.token_start,
591            stop,
592            line: self.token_start_line,
593            column: self.token_start_column,
594            text,
595            source_text,
596            source_name: self.input.source_name(),
597        });
598        if let Some((start_byte, stop_byte)) =
599            source_byte_span.or_else(|| self.token_byte_span(stop))
600        {
601            token = token.with_byte_span(start_byte, stop_byte);
602        }
603        token
604    }
605
606    /// Returns the current token text from the token start through the input
607    /// cursor.
608    pub fn token_text(&self) -> String {
609        self.token_text_until(self.input.index())
610    }
611
612    /// Returns the current token text from the token start through
613    /// `stop_exclusive`.
614    ///
615    /// Lexer custom actions can occur before the accepted token is complete.
616    /// The action event records the position where the transition fired, and
617    /// generated action code uses this helper to render ANTLR's `Text()`
618    /// template at that exact point.
619    pub fn token_text_until(&self, stop_exclusive: usize) -> String {
620        if stop_exclusive <= self.token_start {
621            return String::new();
622        }
623        self.input
624            .text(TextInterval::new(self.token_start, stop_exclusive - 1))
625    }
626
627    /// Computes the zero-based source column at an absolute input position
628    /// reached during prediction of the current token.
629    pub fn column_at(&self, position: usize) -> usize {
630        let mut column = self.token_start_column;
631        if position <= self.token_start {
632            return column;
633        }
634        for ch in self
635            .input
636            .text(TextInterval::new(self.token_start, position - 1))
637            .chars()
638        {
639            if ch == '\n' {
640                column = 0;
641            } else {
642                column += 1;
643            }
644        }
645        column
646    }
647
648    /// Builds the synthetic EOF token at the current input cursor.
649    pub fn eof_token(&self) -> CommonToken {
650        let token = CommonToken::eof(
651            self.input.source_name(),
652            self.input.index(),
653            self.line,
654            self.column,
655        );
656        match self.eof_byte_offset() {
657            Some(byte_offset) => token.with_byte_span(byte_offset, byte_offset),
658            None => token,
659        }
660    }
661
662    fn eof_byte_offset(&self) -> Option<u32> {
663        self.byte_offset_at(self.input.index())
664    }
665
666    fn token_byte_span(&self, stop: usize) -> Option<(u32, u32)> {
667        if stop != usize::MAX && self.token_start <= stop {
668            let (_, start_byte, stop_byte) = self
669                .input
670                .text_source_interval(TextInterval::new(self.token_start, stop))?;
671            return Some((
672                u32::try_from(start_byte).ok()?,
673                u32::try_from(stop_byte).ok()?,
674            ));
675        }
676        let byte_offset = self.byte_offset_at(self.token_start)?;
677        Some((byte_offset, byte_offset))
678    }
679
680    fn byte_offset_at(&self, index: usize) -> Option<u32> {
681        let byte_offset = if index == 0 {
682            0
683        } else {
684            let previous = TextInterval::new(index - 1, index - 1);
685            self.input.text_source_interval(previous)?.2
686        };
687        u32::try_from(byte_offset).ok()
688    }
689}
690
691impl<I, F> Recognizer for BaseLexer<I, F>
692where
693    I: CharStream,
694    F: TokenFactory,
695{
696    fn data(&self) -> &RecognizerData {
697        &self.data
698    }
699
700    fn data_mut(&mut self) -> &mut RecognizerData {
701        &mut self.data
702    }
703}
704
705impl<I, F> Lexer for BaseLexer<I, F>
706where
707    I: CharStream,
708    F: TokenFactory,
709{
710    fn mode(&self) -> i32 {
711        self.mode
712    }
713
714    fn set_mode(&mut self, mode: i32) {
715        self.mode = mode;
716    }
717
718    fn push_mode(&mut self, mode: i32) {
719        self.mode_stack.push(self.mode);
720        self.mode = mode;
721    }
722
723    fn pop_mode(&mut self) -> Option<i32> {
724        let mode = self.mode_stack.pop()?;
725        self.mode = mode;
726        Some(mode)
727    }
728}
729
730impl<I, F> BaseLexer<I, F>
731where
732    I: CharStream,
733    F: TokenFactory,
734{
735    pub const fn line(&self) -> usize {
736        self.line
737    }
738
739    pub const fn column(&self) -> usize {
740        self.column
741    }
742
743    pub fn source_name(&self) -> &str {
744        self.input.source_name()
745    }
746
747    pub const fn hit_eof(&self) -> bool {
748        self.hit_eof
749    }
750
751    pub const fn set_hit_eof(&mut self, hit_eof: bool) {
752        self.hit_eof = hit_eof;
753    }
754
755    /// Routes every token through ATN interpretation even when the generated
756    /// lexer carries an ahead-of-time compiled DFA.
757    ///
758    /// Interpretation is what learns the replayable DFA that
759    /// [`Self::lexer_dfa_string`] reports, so harnesses asserting on the
760    /// observed-DFA trace (ANTLR's `showDFA` descriptors) enable this before
761    /// lexing.
762    pub const fn set_force_interpreted(&mut self, force_interpreted: bool) {
763        self.force_interpreted = force_interpreted;
764    }
765
766    /// Whether compiled-DFA entry points must fall back to interpretation.
767    pub const fn force_interpreted(&self) -> bool {
768        self.force_interpreted
769    }
770
771    /// Buffers a lexer diagnostic until the token stream consumer is ready to
772    /// emit errors in parser-compatible order.
773    pub fn record_error(&self, line: usize, column: usize, message: impl Into<String>) {
774        self.errors
775            .borrow_mut()
776            .push(TokenSourceError::new(line, column, message));
777    }
778
779    /// Records one fail-loud semantic-hook miss per coordinate and token start.
780    pub fn record_semantic_error(
781        &self,
782        action: bool,
783        rule_index: usize,
784        coordinate_index: usize,
785    ) {
786        let kind = u8::from(action);
787        if !self.semantic_error_coordinates.borrow_mut().insert((
788            kind,
789            rule_index,
790            coordinate_index,
791            self.token_start,
792        )) {
793            return;
794        }
795        let label = if action { "action" } else { "predicate" };
796        self.record_error(
797            self.token_start_line,
798            self.token_start_column,
799            format!(
800                "unhandled lexer semantic {label}: rule={rule_index} index={coordinate_index}"
801            ),
802        );
803    }
804
805    /// Returns and clears lexer diagnostics produced while fetching tokens.
806    pub fn drain_errors(&mut self) -> Vec<TokenSourceError> {
807        std::mem::take(self.errors.get_mut())
808    }
809
810    /// Returns the stable state number for a normalized lexer DFA config set,
811    /// creating one if this input path has not reached it before.
812    pub(crate) fn lexer_dfa_state(
813        &self,
814        key: LexerDfaKey,
815        accept_prediction: Option<i32>,
816    ) -> usize {
817        let mut cache = self.dfa_cache.borrow_mut();
818        let next = cache.state_numbers.len();
819        let state = *cache.state_numbers.entry(key).or_insert(next);
820        if let Some(prediction) = accept_prediction {
821            cache.accept_predictions.insert(state, prediction);
822        }
823        state
824    }
825
826    /// Records a visible lexer DFA edge unless it was already observed.
827    pub fn record_lexer_dfa_edge(&self, from: usize, symbol: i32, to: usize) {
828        self.dfa_cache
829            .borrow_mut()
830            .edges
831            .insert(LexerDfaEdge { from, symbol, to });
832    }
833
834    pub(crate) fn cached_lexer_dfa_transition(
835        &self,
836        state: usize,
837        symbol: i32,
838    ) -> Option<LexerDfaCachedTransition> {
839        let cache = self.dfa_cache.borrow();
840        if let Ok(sym) = usize::try_from(symbol)
841            && sym < DENSE_EDGE_SYMBOLS
842        {
843            let transition = cache.dense_edges.get(state)?.as_ref()?[sym];
844            return (transition.target_state != usize::MAX).then_some(transition);
845        }
846        cache.sparse_edges.get(&(state, symbol)).copied()
847    }
848
849    pub(crate) fn cache_lexer_dfa_transition(
850        &self,
851        state: usize,
852        symbol: i32,
853        transition: LexerDfaCachedTransition,
854    ) {
855        let mut cache = self.dfa_cache.borrow_mut();
856        if let Ok(sym) = usize::try_from(symbol)
857            && sym < DENSE_EDGE_SYMBOLS
858        {
859            if cache.dense_edges.len() <= state {
860                cache.dense_edges.resize_with(state + 1, || None);
861            }
862            let row = cache.dense_edges[state]
863                .get_or_insert_with(|| Box::new([EMPTY_DENSE_EDGE; DENSE_EDGE_SYMBOLS]));
864            // First write wins, matching the previous map `entry().or_insert`.
865            if row[sym].target_state == usize::MAX {
866                row[sym] = transition;
867            }
868            return;
869        }
870        cache
871            .sparse_edges
872            .entry((state, symbol))
873            .or_insert(transition);
874    }
875
876    pub(crate) fn cached_lexer_dfa_state(&self, state: usize) -> Option<Rc<LexerDfaCachedState>> {
877        self.dfa_cache
878            .borrow()
879            .cached_states
880            .get(state)
881            .cloned()
882            .flatten()
883    }
884
885    pub(crate) fn cache_lexer_dfa_state(&self, state: usize, cached_state: LexerDfaCachedState) {
886        let mut cache = self.dfa_cache.borrow_mut();
887        if cache.cached_states.len() <= state {
888            cache.cached_states.resize_with(state + 1, || None);
889        }
890        cache.cached_states[state].get_or_insert_with(|| Rc::new(cached_state));
891    }
892
893    pub(crate) fn cached_lexer_mode_start(&self, mode: i32) -> Option<usize> {
894        self.dfa_cache.borrow().mode_starts.get(&mode).copied()
895    }
896
897    pub(crate) fn cache_lexer_mode_start(&self, mode: i32, state: usize) {
898        self.dfa_cache
899            .borrow_mut()
900            .mode_starts
901            .entry(mode)
902            .or_insert(state);
903    }
904
905    /// Serializes the observed default-mode lexer DFA in ANTLR's text shape.
906    pub fn lexer_dfa_string(&self) -> String {
907        let mut out = String::new();
908        let cache = self.dfa_cache.borrow();
909        for edge in &cache.edges {
910            let Some(label) = lexer_dfa_edge_label(edge.symbol) else {
911                continue;
912            };
913            out.push_str(&self.lexer_dfa_state_string(edge.from));
914            out.push('-');
915            out.push_str(&label);
916            out.push_str("->");
917            out.push_str(&self.lexer_dfa_state_string(edge.to));
918            out.push('\n');
919        }
920        out
921    }
922
923    fn lexer_dfa_state_string(&self, state: usize) -> String {
924        self.dfa_cache
925            .borrow()
926            .accept_predictions
927            .get(&state)
928            .map_or_else(
929                || format!("s{state}"),
930                |prediction| format!(":s{state}=>{prediction}"),
931            )
932    }
933}
934
935fn lexer_dfa_edge_label(symbol: i32) -> Option<String> {
936    char::from_u32(symbol.cast_unsigned()).map(|ch| format!("'{ch}'"))
937}
938
939#[cfg(test)]
940mod tests {
941    use super::*;
942    use crate::char_stream::InputStream;
943    use crate::recognizer::RecognizerData;
944    use crate::token::{DEFAULT_CHANNEL, Token};
945    use crate::vocabulary::Vocabulary;
946
947    #[test]
948    fn eof_token_uses_utf8_byte_offset_after_non_ascii_input() {
949        let data = RecognizerData::new(
950            "T",
951            Vocabulary::new(
952                std::iter::empty::<Option<&str>>(),
953                std::iter::empty::<Option<&str>>(),
954                std::iter::empty::<Option<&str>>(),
955            ),
956        );
957        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
958        lexer.consume_char();
959
960        let token = lexer.eof_token();
961
962        assert_eq!(token.start(), 1);
963        assert_eq!(token.stop(), 0);
964        assert_eq!(token.text(), "<EOF>");
965        assert_eq!(token.byte_span(), 2..2);
966    }
967
968    #[test]
969    fn eof_rule_token_uses_utf8_byte_offset_after_non_ascii_input() {
970        let data = RecognizerData::new(
971            "T",
972            Vocabulary::new(
973                std::iter::empty::<Option<&str>>(),
974                std::iter::empty::<Option<&str>>(),
975                std::iter::empty::<Option<&str>>(),
976            ),
977        );
978        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
979        lexer.consume_char();
980        lexer.begin_token();
981
982        let token = lexer.emit_with_stop(1, DEFAULT_CHANNEL, 0, Some("<EOF>".to_owned()));
983
984        assert_eq!(token.start(), 1);
985        assert_eq!(token.stop(), 0);
986        assert_eq!(token.text(), "<EOF>");
987        assert_eq!(token.byte_span(), 2..2);
988    }
989
990    #[test]
991    fn emit_implicit_text_uses_utf8_byte_span_for_non_ascii_input() {
992        let data = RecognizerData::new(
993            "T",
994            Vocabulary::new(
995                std::iter::empty::<Option<&str>>(),
996                std::iter::empty::<Option<&str>>(),
997                std::iter::empty::<Option<&str>>(),
998            ),
999        );
1000        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1001        lexer.begin_token();
1002        lexer.consume_char();
1003
1004        let token = lexer.emit(1, DEFAULT_CHANNEL, None);
1005
1006        assert_eq!(token.start(), 0);
1007        assert_eq!(token.stop(), 0);
1008        assert_eq!(token.text(), "β");
1009        assert_eq!(token.byte_span(), 0..2);
1010    }
1011
1012    #[test]
1013    fn semantic_hook_errors_are_deduplicated_per_token_coordinate() {
1014        let data = RecognizerData::new(
1015            "T",
1016            Vocabulary::new(
1017                std::iter::empty::<Option<&str>>(),
1018                std::iter::empty::<Option<&str>>(),
1019                std::iter::empty::<Option<&str>>(),
1020            ),
1021        );
1022        let mut lexer = BaseLexer::new(InputStream::new("a"), data);
1023        lexer.begin_token();
1024        lexer.record_semantic_error(false, 3, 7);
1025        lexer.record_semantic_error(false, 3, 7);
1026
1027        let errors = lexer.drain_errors();
1028        assert_eq!(errors.len(), 1);
1029        assert_eq!(
1030            errors[0].message,
1031            "unhandled lexer semantic predicate: rule=3 index=7"
1032        );
1033
1034        lexer.begin_token();
1035        lexer.record_semantic_error(false, 3, 7);
1036        assert_eq!(
1037            lexer.drain_errors().len(),
1038            1,
1039            "deduplication resets at every token boundary, even after rewinding"
1040        );
1041    }
1042}