Skip to main content

antlr4_runtime/
lexer.rs

1use std::cell::{RefCell, RefMut};
2use std::collections::{BTreeSet, HashMap, VecDeque};
3use std::hash::BuildHasherDefault;
4use std::rc::Rc;
5
6use crate::atn::LexerAtn;
7use crate::char_stream::{CharStream, TextInterval};
8use crate::int_stream::EOF;
9use crate::prediction::{
10    ContextArena, ContextId, EMPTY_CONTEXT, PredictionFxHasher, PredictionWorkspace,
11};
12use crate::recognizer::{Recognizer, RecognizerData};
13use crate::token::{
14    DEFAULT_CHANNEL, INVALID_TOKEN_TYPE, TokenId, TokenSink, TokenSourceError, TokenSpec,
15    TokenStoreError,
16};
17
18#[allow(clippy::disallowed_types)]
19type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PredictionFxHasher>>;
20
21pub const SKIP: i32 = -3;
22pub const MORE: i32 = -2;
23pub const DEFAULT_MODE: i32 = 0;
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub struct LexerMode(pub i32);
27
28/// Grammar-specific lexer action reached on the accepted ATN path.
29///
30/// ANTLR serializes embedded lexer actions as `(rule_index, action_index)`
31/// pairs. The runtime also records the input position where the action was
32/// reached so generated code can evaluate templates such as `Text()` at the
33/// same point as a generated ANTLR lexer, not only at the token end.
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub struct LexerCustomAction {
36    rule_index: i32,
37    action_index: i32,
38    position: usize,
39}
40
41impl LexerCustomAction {
42    /// Creates a custom lexer action event from serialized ATN metadata.
43    pub const fn new(rule_index: i32, action_index: i32, position: usize) -> Self {
44        Self {
45            rule_index,
46            action_index,
47            position,
48        }
49    }
50
51    /// Lexer rule index that owns the embedded action.
52    pub const fn rule_index(self) -> i32 {
53        self.rule_index
54    }
55
56    /// Per-rule action index assigned by ANTLR serialization.
57    pub const fn action_index(self) -> i32 {
58        self.action_index
59    }
60
61    /// Character-stream position at which the action transition was reached.
62    pub const fn position(self) -> usize {
63        self.position
64    }
65}
66
67/// Grammar-specific lexer predicate reached while exploring an ATN path.
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub struct LexerPredicate {
70    rule_index: usize,
71    pred_index: usize,
72    position: usize,
73}
74
75impl LexerPredicate {
76    /// Creates a lexer predicate event from serialized ATN metadata.
77    pub const fn new(rule_index: usize, pred_index: usize, position: usize) -> Self {
78        Self {
79            rule_index,
80            pred_index,
81            position,
82        }
83    }
84
85    /// Lexer rule index that owns the predicate transition.
86    pub const fn rule_index(self) -> usize {
87        self.rule_index
88    }
89
90    /// Per-rule predicate index assigned by ANTLR serialization.
91    pub const fn pred_index(self) -> usize {
92        self.pred_index
93    }
94
95    /// Character-stream position at which the predicate is evaluated.
96    pub const fn position(self) -> usize {
97        self.position
98    }
99}
100
101/// Lexer reference held by [`LexerSemCtx`]. A semantic *predicate* is evaluated
102/// speculatively and gets a shared borrow; a *custom action* runs on the
103/// committed path and gets a mutable borrow so a hook can change lexer state
104/// and pending token emission, matching the closure-based `custom_action` API.
105#[derive(Debug)]
106enum LexerRef<'a, I>
107where
108    I: CharStream,
109{
110    Shared(&'a BaseLexer<I>),
111    Mut(&'a mut BaseLexer<I>),
112}
113
114impl<I> LexerRef<'_, I>
115where
116    I: CharStream,
117{
118    const fn get(&self) -> &BaseLexer<I> {
119        match self {
120            LexerRef::Shared(lexer) => lexer,
121            LexerRef::Mut(lexer) => lexer,
122        }
123    }
124}
125
126/// Runtime view passed to lexer semantic hooks.
127#[derive(Debug)]
128pub struct LexerSemCtx<'a, I>
129where
130    I: CharStream,
131{
132    lexer: LexerRef<'a, I>,
133    rule_index: usize,
134    coordinate_index: usize,
135    position: usize,
136}
137
138impl<'a, I> LexerSemCtx<'a, I>
139where
140    I: CharStream,
141{
142    pub(crate) const fn new(
143        lexer: &'a BaseLexer<I>,
144        rule_index: usize,
145        coordinate_index: usize,
146        position: usize,
147    ) -> Self {
148        Self {
149            lexer: LexerRef::Shared(lexer),
150            rule_index,
151            coordinate_index,
152            position,
153        }
154    }
155
156    /// Builds a context with a mutable lexer borrow, for a custom-action hook
157    /// that may change lexer and pending-token state.
158    pub(crate) const fn new_mut(
159        lexer: &'a mut BaseLexer<I>,
160        rule_index: usize,
161        coordinate_index: usize,
162        position: usize,
163    ) -> Self {
164        Self {
165            lexer: LexerRef::Mut(lexer),
166            rule_index,
167            coordinate_index,
168            position,
169        }
170    }
171
172    /// Lexer rule index that owns the predicate/action coordinate.
173    #[must_use]
174    pub const fn rule_index(&self) -> usize {
175        self.rule_index
176    }
177
178    /// Predicate/action index inside the owning lexer rule.
179    #[must_use]
180    pub const fn coordinate_index(&self) -> usize {
181        self.coordinate_index
182    }
183
184    /// Absolute input position where the predicate/action transition fired.
185    #[must_use]
186    pub const fn position(&self) -> usize {
187        self.position
188    }
189
190    /// Lexer mode at this coordinate.
191    #[must_use]
192    pub fn mode(&self) -> i32 {
193        self.lexer.get().mode()
194    }
195
196    /// Current source column.
197    #[must_use]
198    pub const fn column(&self) -> usize {
199        self.lexer.get().column()
200    }
201
202    /// Source column at [`Self::position`].
203    #[must_use]
204    pub fn position_column(&self) -> usize {
205        self.lexer.get().column_at(self.position)
206    }
207
208    /// Column captured at the current token start.
209    #[must_use]
210    pub const fn token_start_column(&self) -> usize {
211        self.lexer.get().token_start_column()
212    }
213
214    /// Text matched from token start to this coordinate.
215    #[must_use]
216    pub fn text_so_far(&self) -> String {
217        self.lexer.get().token_text_until(self.position)
218    }
219
220    /// Character at a one-based lookahead/lookbehind offset.
221    ///
222    /// Predicates read relative to their speculative ATN coordinate. Actions
223    /// read relative to the committed input cursor, including characters
224    /// consumed by an earlier action.
225    pub fn la(&mut self, offset: isize) -> i32 {
226        match &mut self.lexer {
227            LexerRef::Shared(lexer) => lexer.lookahead_at(self.position, offset),
228            LexerRef::Mut(lexer) => lexer.input_mut().la(offset),
229        }
230    }
231
232    /// Absolute source index where the current token begins.
233    #[must_use]
234    pub const fn token_start(&self) -> usize {
235        self.lexer.get().token_start()
236    }
237
238    /// Pending type of the token being matched.
239    #[must_use]
240    pub const fn token_type(&self) -> i32 {
241        self.lexer.get().token_type()
242    }
243
244    /// Pending channel of the token being matched.
245    #[must_use]
246    pub const fn channel(&self) -> i32 {
247        self.lexer.get().channel()
248    }
249
250    /// Sets the pending emitted token type. Action context only; see
251    /// [`Self::set_mode`] for the return value.
252    pub const fn set_type(&mut self, token_type: i32) -> bool {
253        match &mut self.lexer {
254            LexerRef::Mut(lexer) => {
255                lexer.set_type(token_type);
256                true
257            }
258            LexerRef::Shared(_) => false,
259        }
260    }
261
262    /// Sets the pending emitted token channel. Action context only; see
263    /// [`Self::set_mode`] for the return value.
264    pub const fn set_channel(&mut self, channel: i32) -> bool {
265        match &mut self.lexer {
266            LexerRef::Mut(lexer) => {
267                lexer.set_channel(channel);
268                true
269            }
270            LexerRef::Shared(_) => false,
271        }
272    }
273
274    /// Consumes one input character and updates source position tracking.
275    /// Action context only; returns whether the operation was available.
276    pub fn consume(&mut self) -> bool {
277        match &mut self.lexer {
278            LexerRef::Mut(lexer) => {
279                lexer.consume_char();
280                true
281            }
282            LexerRef::Shared(_) => false,
283        }
284    }
285
286    /// Marks the current match as skipped. Action context only.
287    pub const fn skip(&mut self) -> bool {
288        self.set_type(SKIP)
289    }
290
291    /// Extends the current token with another lexer-rule match. Action context
292    /// only.
293    pub const fn more(&mut self) -> bool {
294        self.set_type(MORE)
295    }
296
297    /// Repositions the committed accept cursor. Action context only.
298    pub fn reset_accept_position(&mut self, index: usize) -> bool {
299        match &mut self.lexer {
300            LexerRef::Mut(lexer) => {
301                lexer.reset_accept_position(index);
302                true
303            }
304            LexerRef::Shared(_) => false,
305        }
306    }
307
308    /// Moves the current token start forward within the committed match.
309    ///
310    /// This is used after queueing a prefix token so automatic emission covers
311    /// only the remaining suffix. Returns `false` for predicate contexts or an
312    /// index outside the current token span.
313    pub fn set_token_start(&mut self, index: usize) -> bool {
314        match &mut self.lexer {
315            LexerRef::Mut(lexer) => lexer.set_token_start(index),
316            LexerRef::Shared(_) => false,
317        }
318    }
319
320    /// Queues an additional token on the current channel.
321    ///
322    /// The queued token spans the current token start through `stop`
323    /// (inclusive) and is returned before the match's automatically emitted
324    /// token. Action context only.
325    pub fn enqueue_token(&mut self, token_type: i32, stop: usize) -> bool {
326        let channel = self.channel();
327        self.enqueue_token_with_channel(token_type, channel, stop)
328    }
329
330    /// Queues an additional token on an explicit channel. See
331    /// [`Self::enqueue_token`].
332    pub fn enqueue_token_with_channel(
333        &mut self,
334        token_type: i32,
335        channel: i32,
336        stop: usize,
337    ) -> bool {
338        match &mut self.lexer {
339            LexerRef::Mut(lexer) => {
340                lexer.enqueue_token(token_type, channel, stop, None);
341                true
342            }
343            LexerRef::Shared(_) => false,
344        }
345    }
346
347    /// Sets the current lexer mode. Available only from a custom-action hook
348    /// (the mutable-borrow context); a no-op with a warning path for the
349    /// speculative predicate context, where mutating lexer state is invalid.
350    ///
351    /// Returns `true` if the mutation was applied (action context), `false` if
352    /// it was ignored (predicate context).
353    pub fn set_mode(&mut self, mode: i32) -> bool {
354        match &mut self.lexer {
355            LexerRef::Mut(lexer) => {
356                lexer.set_mode(mode);
357                true
358            }
359            LexerRef::Shared(_) => false,
360        }
361    }
362
363    /// Pushes the current mode and switches to `mode`. Action context only; see
364    /// [`Self::set_mode`] for the return value.
365    pub fn push_mode(&mut self, mode: i32) -> bool {
366        match &mut self.lexer {
367            LexerRef::Mut(lexer) => {
368                lexer.push_mode(mode);
369                true
370            }
371            LexerRef::Shared(_) => false,
372        }
373    }
374
375    /// Pops the mode stack, restoring the previous mode. Action context only;
376    /// returns the popped mode (`None` if the stack was empty or this is a
377    /// predicate context).
378    pub fn pop_mode(&mut self) -> Option<i32> {
379        match &mut self.lexer {
380            LexerRef::Mut(lexer) => lexer.pop_mode(),
381            LexerRef::Shared(_) => None,
382        }
383    }
384}
385
386/// Mutable lexer state exposed at lifecycle boundaries that have no ATN
387/// semantic coordinate.
388///
389/// The context is used before a token request starts matching, after an
390/// accepted path has applied its actions but before emission, and while a
391/// lexer is reset for reuse. [`Self::accept_position`] is present only at the
392/// post-accept boundary.
393#[derive(Debug)]
394pub struct LexerLifecycleCtx<'a, I>
395where
396    I: CharStream,
397{
398    lexer: &'a mut BaseLexer<I>,
399    accept_position: Option<usize>,
400}
401
402impl<'a, I> LexerLifecycleCtx<'a, I>
403where
404    I: CharStream,
405{
406    pub(crate) const fn new(lexer: &'a mut BaseLexer<I>, accept_position: Option<usize>) -> Self {
407        Self {
408            lexer,
409            accept_position,
410        }
411    }
412
413    /// Original input boundary selected by the accepted ATN path.
414    ///
415    /// A post-accept hook may move the committed cursor away from this
416    /// boundary with [`Self::reset_accept_position`].
417    #[must_use]
418    pub const fn accept_position(&self) -> Option<usize> {
419        self.accept_position
420    }
421
422    /// Current committed input position.
423    #[must_use]
424    pub fn input_position(&self) -> usize {
425        self.lexer.input().index()
426    }
427
428    /// Current lexer mode.
429    #[must_use]
430    pub const fn mode(&self) -> i32 {
431        self.lexer.mode
432    }
433
434    /// Current source line.
435    #[must_use]
436    pub const fn line(&self) -> usize {
437        self.lexer.line()
438    }
439
440    /// Current source column.
441    #[must_use]
442    pub const fn column(&self) -> usize {
443        self.lexer.column()
444    }
445
446    /// Absolute source index where the current token begins.
447    #[must_use]
448    pub const fn token_start(&self) -> usize {
449        self.lexer.token_start()
450    }
451
452    /// Source line captured at the current token start.
453    #[must_use]
454    pub const fn token_start_line(&self) -> usize {
455        self.lexer.token_start_line()
456    }
457
458    /// Source column captured at the current token start.
459    #[must_use]
460    pub const fn token_start_column(&self) -> usize {
461        self.lexer.token_start_column()
462    }
463
464    /// Pending type of the token being matched.
465    #[must_use]
466    pub const fn token_type(&self) -> i32 {
467        self.lexer.token_type()
468    }
469
470    /// Pending channel of the token being matched.
471    #[must_use]
472    pub const fn channel(&self) -> i32 {
473        self.lexer.channel()
474    }
475
476    /// Number of tokens waiting to be returned before another ATN match.
477    #[must_use]
478    pub fn pending_token_count(&self) -> usize {
479        self.lexer.pending_tokens.len()
480    }
481
482    /// Text from the current token start through the committed input cursor.
483    #[must_use]
484    pub fn token_text(&self) -> String {
485        self.lexer.token_text()
486    }
487
488    /// Text selected by the original accepted ATN path.
489    ///
490    /// Returns `None` outside the post-accept callback.
491    #[must_use]
492    pub fn accepted_text(&self) -> Option<String> {
493        self.accept_position
494            .map(|position| self.lexer.token_text_until(position))
495    }
496
497    /// Character at a one-based lookahead/lookbehind offset from the
498    /// committed input cursor.
499    pub fn la(&mut self, offset: isize) -> i32 {
500        self.lexer.la(offset)
501    }
502
503    /// Consumes one input character and updates source position tracking.
504    pub fn consume(&mut self) {
505        self.lexer.consume_char();
506    }
507
508    /// Overrides the pending emitted token type.
509    pub const fn set_type(&mut self, token_type: i32) {
510        self.lexer.set_type(token_type);
511    }
512
513    /// Overrides the pending emitted token channel.
514    pub const fn set_channel(&mut self, channel: i32) {
515        self.lexer.set_channel(channel);
516    }
517
518    /// Marks the current match as skipped.
519    pub const fn skip(&mut self) {
520        self.lexer.skip();
521    }
522
523    /// Extends the current token with another lexer-rule match.
524    pub const fn more(&mut self) {
525        self.lexer.more();
526    }
527
528    /// Repositions the committed accept cursor.
529    pub fn reset_accept_position(&mut self, index: usize) {
530        self.lexer.reset_accept_position(index);
531    }
532
533    /// Moves the current token start forward within the committed match.
534    pub fn set_token_start(&mut self, index: usize) -> bool {
535        self.lexer.set_token_start(index)
536    }
537
538    /// Queues an additional token on the current channel.
539    pub fn enqueue_token(&mut self, token_type: i32, stop: usize) {
540        self.enqueue_token_with_channel(token_type, self.channel(), stop);
541    }
542
543    /// Queues an additional token on an explicit channel.
544    pub fn enqueue_token_with_channel(&mut self, token_type: i32, channel: i32, stop: usize) {
545        self.lexer.enqueue_token(token_type, channel, stop, None);
546    }
547
548    /// Sets the current lexer mode.
549    pub fn set_mode(&mut self, mode: i32) {
550        self.lexer.set_mode(mode);
551    }
552
553    /// Pushes the current mode and switches to `mode`.
554    pub fn push_mode(&mut self, mode: i32) {
555        self.lexer.push_mode(mode);
556    }
557
558    /// Pops the mode stack, restoring the previous mode.
559    pub fn pop_mode(&mut self) -> Option<i32> {
560        self.lexer.pop_mode()
561    }
562}
563
564pub trait Lexer: Recognizer {
565    fn mode(&self) -> i32;
566    fn set_mode(&mut self, mode: i32);
567    fn push_mode(&mut self, mode: i32);
568    fn pop_mode(&mut self) -> Option<i32>;
569}
570
571#[derive(Clone, Debug)]
572pub struct BaseLexer<I> {
573    input: I,
574    data: RecognizerData,
575    has_source_text: bool,
576    mode: i32,
577    mode_stack: Vec<i32>,
578    token_type: i32,
579    channel: i32,
580    token_start: usize,
581    token_start_line: usize,
582    token_start_column: usize,
583    line: usize,
584    column: usize,
585    hit_eof: bool,
586    force_interpreted: bool,
587    errors: RefCell<Vec<TokenSourceError>>,
588    semantic_error_coordinates: RefCell<BTreeSet<(u8, usize, usize, usize)>>,
589    pending_tokens: VecDeque<TokenSpec>,
590    dfa_cache: Rc<RefCell<LexerDfaCache>>,
591}
592
593/// Learned lexer DFA: the input-independent state/transition tables built up
594/// by ATN simulation.
595///
596/// Semantic-predicate-dependent states are stored flagged and every consumer
597/// re-simulates them instead of trusting their cached data, so the cache can
598/// be shared across lexer instances (and inputs) for the same ATN — see
599/// [`BaseLexer::with_shared_dfa`].
600#[derive(Debug, Default)]
601struct LexerDfaCache {
602    prediction: LexerPredictionStore,
603    state_numbers: FxHashMap<LexerDfaKey, usize>,
604    accept_predictions: FxHashMap<usize, i32>,
605    /// `showDFA` edge trace. Lives with the tables it describes, so a lexer
606    /// on a shared cache reports the accumulated DFA — matching the reference
607    /// runtimes, whose static shared DFA is what `showDFA` prints.
608    edges: BTreeSet<LexerDfaEdge>,
609    /// Dense by DFA state number (states are numbered contiguously from 0).
610    cached_states: Vec<Option<Rc<LexerDfaCachedState>>>,
611    /// Per-source-state edge rows for symbols in `0..DENSE_EDGE_SYMBOLS`,
612    /// allocated lazily on the first cached transition out of a state. The
613    /// per-character lookup is then one bounds check and an array index —
614    /// the same scheme as Go's `edges[t-MinDFAEdge]`.
615    dense_edges: Vec<Option<Box<DenseEdgeRow>>>,
616    /// Transitions on symbols outside the dense range (supplementary planes).
617    sparse_edges: FxHashMap<(usize, i32), LexerDfaCachedTransition>,
618    mode_starts: FxHashMap<i32, usize>,
619}
620
621/// Canonical caller contexts paired with the learned lexer DFA that stores
622/// their IDs.
623#[derive(Debug, Default)]
624pub(crate) struct LexerPredictionStore {
625    pub(crate) contexts: LexerContextArena,
626    pub(crate) workspace: PredictionWorkspace,
627}
628
629/// Store-local identity for one ordered lexer caller-context node.
630#[repr(transparent)]
631#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
632pub(crate) struct LexerContextId(u32);
633
634pub(crate) const EMPTY_LEXER_CONTEXT: LexerContextId = LexerContextId(0);
635
636/// One node in an ordered graph of lexer caller stacks.
637///
638/// `Union` preserves ATN traversal priority. The paired unordered prediction
639/// context detects when a later union adds no stack paths, which keeps cyclic
640/// lexer closures finite without flattening their priority order.
641#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
642pub(crate) enum LexerContextNode {
643    Empty,
644    Singleton {
645        parent: LexerContextId,
646        return_state: usize,
647    },
648    Union {
649        left: LexerContextId,
650        right: LexerContextId,
651    },
652}
653
654#[derive(Clone, Copy, Debug)]
655struct LexerContextRecord {
656    node: LexerContextNode,
657    path_set: ContextId,
658}
659
660/// Canonical ordered caller-context DAG for one learned or compiled lexer DFA.
661#[derive(Debug)]
662pub(crate) struct LexerContextArena {
663    records: Vec<LexerContextRecord>,
664    ids: FxHashMap<LexerContextNode, LexerContextId>,
665    path_sets: ContextArena,
666}
667
668impl LexerContextArena {
669    pub(crate) fn new() -> Self {
670        let mut ids = FxHashMap::default();
671        ids.insert(LexerContextNode::Empty, EMPTY_LEXER_CONTEXT);
672        Self {
673            records: vec![LexerContextRecord {
674                node: LexerContextNode::Empty,
675                path_set: EMPTY_CONTEXT,
676            }],
677            ids,
678            path_sets: ContextArena::new(),
679        }
680    }
681
682    pub(crate) fn singleton(
683        &mut self,
684        parent: LexerContextId,
685        return_state: usize,
686    ) -> LexerContextId {
687        self.assert_valid(parent);
688        let node = LexerContextNode::Singleton {
689            parent,
690            return_state,
691        };
692        if let Some(&context) = self.ids.get(&node) {
693            return context;
694        }
695        let path_set = self
696            .path_sets
697            .singleton(self.record(parent).path_set, return_state);
698        self.intern(node, path_set)
699    }
700
701    pub(crate) fn merge(
702        &mut self,
703        left: LexerContextId,
704        right: LexerContextId,
705        workspace: &mut PredictionWorkspace,
706    ) -> LexerContextId {
707        self.assert_valid(left);
708        self.assert_valid(right);
709        if left == right {
710            return left;
711        }
712        let left_set = self.record(left).path_set;
713        let right_set = self.record(right).path_set;
714        let path_set = self.path_sets.merge(left_set, right_set, false, workspace);
715        if path_set == left_set {
716            return left;
717        }
718        let node = LexerContextNode::Union { left, right };
719        if let Some(&context) = self.ids.get(&node) {
720            return context;
721        }
722        self.intern(node, path_set)
723    }
724
725    pub(crate) fn node(&self, context: LexerContextId) -> LexerContextNode {
726        self.record(context).node
727    }
728
729    #[cfg(test)]
730    pub(crate) const fn len(&self) -> usize {
731        self.records.len()
732    }
733
734    fn intern(&mut self, node: LexerContextNode, path_set: ContextId) -> LexerContextId {
735        let context = LexerContextId(
736            u32::try_from(self.records.len()).expect("lexer context arena must fit in u32"),
737        );
738        self.records.push(LexerContextRecord { node, path_set });
739        self.ids.insert(node, context);
740        context
741    }
742
743    fn record(&self, context: LexerContextId) -> &LexerContextRecord {
744        self.assert_valid(context);
745        &self.records[usize::try_from(context.0).expect("u32 lexer context ID fits in usize")]
746    }
747
748    fn assert_valid(&self, context: LexerContextId) {
749        assert!(
750            usize::try_from(context.0).is_ok_and(|index| index < self.records.len()),
751            "lexer context ID does not belong to this store"
752        );
753    }
754}
755
756impl Default for LexerContextArena {
757    fn default() -> Self {
758        Self::new()
759    }
760}
761
762/// Dense-row width: ASCII, matching the reference runtimes' DFA edge arrays.
763const DENSE_EDGE_SYMBOLS: usize = 128;
764
765type DenseEdgeRow = [LexerDfaCachedTransition; DENSE_EDGE_SYMBOLS];
766
767/// Sentinel for an empty dense-row slot; no real transition targets it
768/// because DFA state numbers are assigned contiguously from 0.
769const EMPTY_DENSE_EDGE: LexerDfaCachedTransition = LexerDfaCachedTransition {
770    target_state: usize::MAX,
771    position_delta: 0,
772};
773
774thread_local! {
775    /// Learned lexer DFAs shared across lexer instances, keyed by a generated
776    /// lexer's static ATN identity (mirrors the parser's shared decision DFAs).
777    static SHARED_LEXER_DFA_CACHES: RefCell<HashMap<usize, Rc<RefCell<LexerDfaCache>>>> =
778        RefCell::new(HashMap::new());
779}
780
781/// Position-independent lexer ATN config sequence used for observed DFA traces.
782///
783/// Configuration order is part of the identity because it carries serialized
784/// ATN priority through non-greedy decisions.
785#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
786pub(crate) struct LexerDfaKey {
787    configs: Vec<LexerDfaConfigKey>,
788}
789
790impl LexerDfaKey {
791    pub(crate) const fn new(configs: Vec<LexerDfaConfigKey>) -> Self {
792        Self { configs }
793    }
794}
795
796/// One lexer ATN config identity with the absolute input position removed.
797#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
798pub(crate) struct LexerDfaConfigKey {
799    pub(crate) state: usize,
800    pub(crate) alt_rule_index: Option<usize>,
801    pub(crate) consumed_eof: bool,
802    pub(crate) passed_non_greedy: bool,
803    pub(crate) context: LexerContextId,
804    pub(crate) actions: Vec<LexerDfaActionKey>,
805}
806
807#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
808pub(crate) struct LexerDfaActionKey {
809    pub(crate) action_index: usize,
810    pub(crate) position_delta: usize,
811    pub(crate) rule_index: usize,
812}
813
814impl LexerDfaConfigKey {
815    pub(crate) const fn new(
816        state: usize,
817        alt_rule_index: Option<usize>,
818        consumed_eof: bool,
819        passed_non_greedy: bool,
820        context: LexerContextId,
821        actions: Vec<LexerDfaActionKey>,
822    ) -> Self {
823        Self {
824            state,
825            alt_rule_index,
826            consumed_eof,
827            passed_non_greedy,
828            context,
829            actions,
830        }
831    }
832}
833
834#[derive(Clone, Copy, Debug)]
835pub(crate) struct LexerDfaCachedTransition {
836    pub(crate) target_state: usize,
837    pub(crate) position_delta: usize,
838}
839
840#[derive(Clone, Debug)]
841pub(crate) struct LexerDfaCachedAccept {
842    pub(crate) position_delta: usize,
843    pub(crate) rule_index: usize,
844    pub(crate) consumed_eof: bool,
845    pub(crate) actions: Vec<LexerDfaActionKey>,
846}
847
848#[derive(Clone, Debug)]
849pub(crate) struct LexerDfaCachedState {
850    pub(crate) has_semantic_context: bool,
851    pub(crate) configs: Vec<LexerDfaConfigKey>,
852    pub(crate) accept: Option<LexerDfaCachedAccept>,
853}
854
855/// One printable lexer DFA edge keyed so repeated matches keep deterministic
856/// output order.
857#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
858struct LexerDfaEdge {
859    from: usize,
860    symbol: i32,
861    to: usize,
862}
863
864impl<I> BaseLexer<I>
865where
866    I: CharStream,
867{
868    pub fn new(input: I, data: RecognizerData) -> Self {
869        let has_source_text = input.source_text().is_some();
870        Self {
871            input,
872            data,
873            has_source_text,
874            mode: DEFAULT_MODE,
875            mode_stack: Vec::new(),
876            token_type: INVALID_TOKEN_TYPE,
877            channel: DEFAULT_CHANNEL,
878            token_start: 0,
879            token_start_line: 1,
880            token_start_column: 0,
881            line: 1,
882            column: 0,
883            hit_eof: false,
884            force_interpreted: false,
885            errors: RefCell::new(Vec::new()),
886            semantic_error_coordinates: RefCell::new(BTreeSet::new()),
887            pending_tokens: VecDeque::new(),
888            dfa_cache: Rc::new(RefCell::new(LexerDfaCache::default())),
889        }
890    }
891
892    /// Resets runtime-owned lexer state so this instance can consume its input
893    /// again from the beginning.
894    ///
895    /// Learned DFA tables and configuration such as forced interpretation are
896    /// retained. Token-production state, diagnostics, pending tokens, modes,
897    /// and source position are cleared.
898    pub fn reset(&mut self) {
899        self.input.seek(0);
900        self.mode = DEFAULT_MODE;
901        self.mode_stack.clear();
902        self.token_type = INVALID_TOKEN_TYPE;
903        self.channel = DEFAULT_CHANNEL;
904        self.token_start = 0;
905        self.token_start_line = 1;
906        self.token_start_column = 0;
907        self.line = 1;
908        self.column = 0;
909        self.hit_eof = false;
910        self.errors.get_mut().clear();
911        self.semantic_error_coordinates.get_mut().clear();
912        self.pending_tokens.clear();
913    }
914
915    /// Replaces the character stream and fully resets lexer state for reuse.
916    ///
917    /// Learned DFA tables and configuration such as forced interpretation are
918    /// retained. The new stream is always rewound to its beginning.
919    pub fn set_input_stream(&mut self, input: I) {
920        self.input = input;
921        self.has_source_text = self.input.source_text().is_some();
922        self.reset();
923    }
924
925    /// Switches this lexer to the thread-shared learned DFA for `atn`.
926    ///
927    /// Generated lexers create a fresh instance per parse; without sharing,
928    /// every instance relearns the same DFA through ATN simulation. The shared
929    /// cache is keyed by the generated lexer's `&'static LexerAtn` identity and
930    /// holds only input-independent data, so it stays valid across inputs.
931    /// The `showDFA` edge trace lives in the cache too, so it reports the
932    /// accumulated DFA — the same view the reference runtimes print from
933    /// their static shared DFA.
934    #[must_use]
935    pub fn with_shared_dfa(mut self, atn: &'static LexerAtn) -> Self {
936        let ptr: *const LexerAtn = atn;
937        let key = ptr as usize;
938        self.dfa_cache = SHARED_LEXER_DFA_CACHES
939            .with(|caches| Rc::clone(caches.borrow_mut().entry(key).or_insert_with(Rc::default)));
940        self
941    }
942
943    /// Clears the learned lexer DFA shared by recognizers for this grammar.
944    ///
945    /// Ahead-of-time compiled DFA tables are immutable generated data and are
946    /// unaffected. Any path that falls back to ATN interpretation relearns its
947    /// dynamic DFA from an empty cache after this call.
948    pub fn clear_dfa(&self) {
949        let mut cache = self.dfa_cache.borrow_mut();
950        // In-flight predicate evaluation may clear the DFA while its configs
951        // still hold store-local context IDs.
952        let prediction = std::mem::take(&mut cache.prediction);
953        *cache = LexerDfaCache {
954            prediction,
955            ..LexerDfaCache::default()
956        };
957    }
958
959    pub const fn input(&self) -> &I {
960        &self.input
961    }
962
963    pub const fn input_mut(&mut self) -> &mut I {
964        &mut self.input
965    }
966
967    /// Captures the input index and source position for the token currently
968    /// being matched.
969    pub fn begin_token(&mut self) {
970        self.semantic_error_coordinates.get_mut().clear();
971        self.token_type = INVALID_TOKEN_TYPE;
972        self.channel = DEFAULT_CHANNEL;
973        self.token_start = self.input.index();
974        self.token_start_line = self.line;
975        self.token_start_column = self.column;
976    }
977
978    /// Returns the absolute character index where the current token began.
979    pub const fn token_start(&self) -> usize {
980        self.token_start
981    }
982
983    /// Returns the source line captured at the start of the current token.
984    pub const fn token_start_line(&self) -> usize {
985        self.token_start_line
986    }
987
988    /// Returns the source column captured at the start of the current token.
989    pub const fn token_start_column(&self) -> usize {
990        self.token_start_column
991    }
992
993    /// Returns the pending type of the token being matched.
994    pub const fn token_type(&self) -> i32 {
995        self.token_type
996    }
997
998    /// Overrides the pending type of the token being matched.
999    pub const fn set_type(&mut self, token_type: i32) {
1000        self.token_type = token_type;
1001    }
1002
1003    /// Returns the pending channel of the token being matched.
1004    pub const fn channel(&self) -> i32 {
1005        self.channel
1006    }
1007
1008    /// Overrides the pending channel of the token being matched.
1009    pub const fn set_channel(&mut self, channel: i32) {
1010        self.channel = channel;
1011    }
1012
1013    /// Marks the current match as skipped.
1014    pub const fn skip(&mut self) {
1015        self.set_type(SKIP);
1016    }
1017
1018    /// Extends the current token with another lexer-rule match.
1019    pub const fn more(&mut self) {
1020        self.set_type(MORE);
1021    }
1022
1023    /// Reads a character at a one-based lookahead/lookbehind offset from the
1024    /// committed input cursor without moving it.
1025    pub fn la(&mut self, offset: isize) -> i32 {
1026        self.input.la(offset)
1027    }
1028
1029    fn lookahead_at(&self, position: usize, offset: isize) -> i32 {
1030        if offset == 0 {
1031            return 0;
1032        }
1033        let absolute = if offset > 0 {
1034            position.checked_add((offset - 1).cast_unsigned())
1035        } else {
1036            offset
1037                .checked_neg()
1038                .and_then(|distance| usize::try_from(distance).ok())
1039                .and_then(|distance| position.checked_sub(distance))
1040        };
1041        let Some(index) = absolute.filter(|index| *index < self.input.size()) else {
1042            return EOF;
1043        };
1044        if let Some(symbol) = self.input.symbol_at(index) {
1045            return symbol;
1046        }
1047        self.input
1048            .text(TextInterval::new(index, index))
1049            .chars()
1050            .next()
1051            .map_or(EOF, |ch| u32::from(ch).cast_signed())
1052    }
1053
1054    /// Consumes one character from the input stream and updates lexer line and
1055    /// column counters.
1056    ///
1057    /// The input stream is indexed by Unicode scalar values. Newline handling
1058    /// follows ANTLR's default convention of incrementing the line and resetting
1059    /// the column after `\n`.
1060    pub fn consume_char(&mut self) {
1061        let la = self.input.la(1);
1062        if la == EOF {
1063            return;
1064        }
1065        self.input.consume();
1066        if char::from_u32(la.cast_unsigned()) == Some('\n') {
1067            self.line += 1;
1068            self.column = 0;
1069        } else {
1070            self.column += 1;
1071        }
1072    }
1073
1074    /// Commits a predicted input span while keeping the current line and column
1075    /// as the coordinates at `start`.
1076    pub(crate) fn commit_position(&mut self, start: usize, target: usize) {
1077        self.reposition_from(start, self.line, self.column, target);
1078    }
1079
1080    fn reposition_from(&mut self, start: usize, line: usize, column: usize, target: usize) {
1081        let start = start.min(self.input.size());
1082        let target = target.max(start).min(self.input.size());
1083        if let Some(summary) = self.input.position_summary(start, target) {
1084            self.input.seek(target);
1085            (self.line, self.column) = summary.apply(line, column);
1086            #[cfg(feature = "perf-counters")]
1087            crate::perf::record_lexer_bulk_commit(target - start);
1088            return;
1089        }
1090
1091        self.input.seek(start);
1092        self.line = line;
1093        self.column = column;
1094        #[cfg(feature = "perf-counters")]
1095        let before = self.input.index();
1096        while self.input.index() < target && self.input.la(1) != EOF {
1097            self.consume_char();
1098        }
1099        #[cfg(feature = "perf-counters")]
1100        crate::perf::record_lexer_scalar_replay(self.input.index().saturating_sub(before));
1101    }
1102
1103    /// Rewinds or advances the input cursor to a token accept boundary.
1104    ///
1105    /// Some generated lexers intentionally accept a longer path to disambiguate
1106    /// a token, then emit only the prefix and leave the suffix for the next
1107    /// token. Recomputing line/column from `token_start` keeps the visible lexer
1108    /// position consistent after moving the cursor backwards.
1109    pub fn reset_accept_position(&mut self, index: usize) {
1110        let target = index.max(self.token_start);
1111        self.reposition_from(
1112            self.token_start,
1113            self.token_start_line,
1114            self.token_start_column,
1115            target,
1116        );
1117    }
1118
1119    /// Moves the current token start forward within the consumed input span.
1120    ///
1121    /// Source line and column are advanced with the start, so a subsequently
1122    /// emitted suffix token carries the same coordinates it would have had if
1123    /// lexed independently.
1124    pub fn set_token_start(&mut self, index: usize) -> bool {
1125        if index < self.token_start || index > self.input.index() {
1126            return false;
1127        }
1128        let (line, column) = self.position_at(index);
1129        self.token_start = index;
1130        self.token_start_line = line;
1131        self.token_start_column = column;
1132        true
1133    }
1134
1135    /// Builds a token spanning from the current token start to the character
1136    /// before the input cursor.
1137    ///
1138    /// When generated or interpreted lexer code does not supply explicit text,
1139    /// the base lexer captures the matched source interval so downstream token
1140    /// streams and parse trees can render token text without retaining a source
1141    /// pair object.
1142    pub fn emit(
1143        &self,
1144        sink: &mut TokenSink<'_>,
1145        token_type: i32,
1146        channel: i32,
1147        text: Option<String>,
1148    ) -> Result<TokenId, TokenStoreError> {
1149        let stop = self.input.index().checked_sub(1).unwrap_or(usize::MAX);
1150        self.emit_with_stop(sink, token_type, channel, stop, text)
1151    }
1152
1153    /// Builds a token with an explicit stop index.
1154    ///
1155    /// EOF-matching lexer rules do not consume a Unicode scalar value, so their
1156    /// stop index can be one before the current input index. The caller passes
1157    /// `usize::MAX` to represent ANTLR's `-1` stop index at empty input.
1158    pub fn emit_with_stop(
1159        &self,
1160        sink: &mut TokenSink<'_>,
1161        token_type: i32,
1162        channel: i32,
1163        stop: usize,
1164        text: Option<String>,
1165    ) -> Result<TokenId, TokenStoreError> {
1166        sink.push(self.token_spec_with_stop(token_type, channel, stop, text))
1167    }
1168
1169    fn token_spec_with_stop(
1170        &self,
1171        token_type: i32,
1172        channel: i32,
1173        stop: usize,
1174        text: Option<String>,
1175    ) -> TokenSpec {
1176        let text = text.or_else(|| {
1177            if stop == usize::MAX {
1178                Some("<EOF>".to_owned())
1179            } else {
1180                None
1181            }
1182        });
1183        let source_interval = if self.has_source_text
1184            && text.is_none()
1185            && stop != usize::MAX
1186            && self.token_start <= stop
1187        {
1188            self.input
1189                .byte_interval(TextInterval::new(self.token_start, stop))
1190        } else {
1191            None
1192        };
1193        let text = text.or_else(|| {
1194            source_interval
1195                .is_none()
1196                .then(|| self.input.text(TextInterval::new(self.token_start, stop)))
1197        });
1198        let (start_byte, stop_byte) = source_interval
1199            .or_else(|| self.token_byte_span(stop))
1200            .unwrap_or((self.token_start, self.token_start));
1201        TokenSpec {
1202            token_type,
1203            channel,
1204            start: self.token_start,
1205            stop,
1206            start_byte,
1207            stop_byte,
1208            line: self.token_start_line,
1209            column: self.token_start_column,
1210            text,
1211            source_backed: source_interval.is_some(),
1212        }
1213    }
1214
1215    /// Queues an additional token to be returned before the current match's
1216    /// automatic token.
1217    ///
1218    /// The token spans the current token start through `stop` (inclusive).
1219    /// `text = None` keeps the token source-backed when the input supports it.
1220    pub fn enqueue_token(
1221        &mut self,
1222        token_type: i32,
1223        channel: i32,
1224        stop: usize,
1225        text: Option<String>,
1226    ) {
1227        let token = self.token_spec_with_stop(token_type, channel, stop, text);
1228        self.pending_tokens.push_back(token);
1229    }
1230
1231    pub(crate) fn emit_pending_token(
1232        &mut self,
1233        sink: &mut TokenSink<'_>,
1234    ) -> Result<Option<TokenId>, TokenStoreError> {
1235        self.pending_tokens
1236            .pop_front()
1237            .map(|token| sink.push(token))
1238            .transpose()
1239    }
1240
1241    pub(crate) fn emit_or_enqueue_with_stop(
1242        &mut self,
1243        sink: &mut TokenSink<'_>,
1244        stop: usize,
1245        text: Option<String>,
1246    ) -> Result<TokenId, TokenStoreError> {
1247        let token = self.token_spec_with_stop(self.token_type, self.channel, stop, text);
1248        self.emit_or_enqueue(sink, token)
1249    }
1250
1251    fn emit_or_enqueue(
1252        &mut self,
1253        sink: &mut TokenSink<'_>,
1254        token: TokenSpec,
1255    ) -> Result<TokenId, TokenStoreError> {
1256        if self.pending_tokens.is_empty() {
1257            return sink.push(token);
1258        }
1259        self.pending_tokens.push_back(token);
1260        self.emit_pending_token(sink)?
1261            .ok_or_else(|| unreachable!("the pending-token queue was just populated"))
1262    }
1263
1264    /// Returns the current token text from the token start through the input
1265    /// cursor.
1266    pub fn token_text(&self) -> String {
1267        self.token_text_until(self.input.index())
1268    }
1269
1270    /// Returns the current token text from the token start through
1271    /// `stop_exclusive`.
1272    ///
1273    /// Lexer custom actions can occur before the accepted token is complete.
1274    /// The action event records the position where the transition fired, and
1275    /// generated action code uses this helper to render ANTLR's `Text()`
1276    /// template at that exact point.
1277    pub fn token_text_until(&self, stop_exclusive: usize) -> String {
1278        if stop_exclusive <= self.token_start {
1279            return String::new();
1280        }
1281        self.input
1282            .text(TextInterval::new(self.token_start, stop_exclusive - 1))
1283    }
1284
1285    /// Computes the zero-based source column at an absolute input position
1286    /// reached during prediction of the current token.
1287    pub fn column_at(&self, position: usize) -> usize {
1288        self.position_at(position).1
1289    }
1290
1291    fn position_at(&self, position: usize) -> (usize, usize) {
1292        let mut line = self.token_start_line;
1293        let mut column = self.token_start_column;
1294        if position <= self.token_start {
1295            return (line, column);
1296        }
1297        if let Some(summary) = self.input.position_summary(self.token_start, position) {
1298            return summary.apply(line, column);
1299        }
1300        for ch in self
1301            .input
1302            .text(TextInterval::new(self.token_start, position - 1))
1303            .chars()
1304        {
1305            if ch == '\n' {
1306                line += 1;
1307                column = 0;
1308            } else {
1309                column += 1;
1310            }
1311        }
1312        (line, column)
1313    }
1314
1315    /// Builds the synthetic EOF token at the current input cursor.
1316    pub fn eof_token(&self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
1317        sink.push(self.eof_token_spec())
1318    }
1319
1320    pub(crate) fn emit_eof_or_pending(
1321        &mut self,
1322        sink: &mut TokenSink<'_>,
1323    ) -> Result<TokenId, TokenStoreError> {
1324        let token = self.eof_token_spec();
1325        self.emit_or_enqueue(sink, token)
1326    }
1327
1328    fn eof_token_spec(&self) -> TokenSpec {
1329        let byte_offset = self.eof_byte_offset().unwrap_or_else(|| self.input.index());
1330        TokenSpec::eof(self.input.index(), byte_offset, self.line, self.column)
1331    }
1332
1333    fn eof_byte_offset(&self) -> Option<usize> {
1334        self.byte_offset_at(self.input.index())
1335    }
1336
1337    fn token_byte_span(&self, stop: usize) -> Option<(usize, usize)> {
1338        if stop != usize::MAX && self.token_start <= stop {
1339            let (start_byte, stop_byte) = self
1340                .input
1341                .byte_interval(TextInterval::new(self.token_start, stop))?;
1342            return Some((start_byte, stop_byte));
1343        }
1344        let byte_offset = self.byte_offset_at(self.token_start)?;
1345        Some((byte_offset, byte_offset))
1346    }
1347
1348    fn byte_offset_at(&self, index: usize) -> Option<usize> {
1349        let byte_offset = if index == 0 {
1350            0
1351        } else {
1352            let previous = TextInterval::new(index - 1, index - 1);
1353            self.input.byte_interval(previous)?.1
1354        };
1355        Some(byte_offset)
1356    }
1357}
1358
1359impl<I> Recognizer for BaseLexer<I>
1360where
1361    I: CharStream,
1362{
1363    fn data(&self) -> &RecognizerData {
1364        &self.data
1365    }
1366
1367    fn data_mut(&mut self) -> &mut RecognizerData {
1368        &mut self.data
1369    }
1370}
1371
1372impl<I> Lexer for BaseLexer<I>
1373where
1374    I: CharStream,
1375{
1376    fn mode(&self) -> i32 {
1377        self.mode
1378    }
1379
1380    fn set_mode(&mut self, mode: i32) {
1381        self.mode = mode;
1382    }
1383
1384    fn push_mode(&mut self, mode: i32) {
1385        self.mode_stack.push(self.mode);
1386        self.mode = mode;
1387    }
1388
1389    fn pop_mode(&mut self) -> Option<i32> {
1390        let mode = self.mode_stack.pop()?;
1391        self.mode = mode;
1392        Some(mode)
1393    }
1394}
1395
1396impl<I> BaseLexer<I>
1397where
1398    I: CharStream,
1399{
1400    pub const fn line(&self) -> usize {
1401        self.line
1402    }
1403
1404    pub const fn column(&self) -> usize {
1405        self.column
1406    }
1407
1408    pub fn source_name(&self) -> &str {
1409        self.input.source_name()
1410    }
1411
1412    pub fn source_text(&self) -> Option<Rc<str>> {
1413        self.input.source_text()
1414    }
1415
1416    pub const fn hit_eof(&self) -> bool {
1417        self.hit_eof
1418    }
1419
1420    pub const fn set_hit_eof(&mut self, hit_eof: bool) {
1421        self.hit_eof = hit_eof;
1422    }
1423
1424    /// Routes every token through ATN interpretation even when the generated
1425    /// lexer carries an ahead-of-time compiled DFA.
1426    ///
1427    /// Interpretation is what learns the replayable DFA that
1428    /// [`Self::lexer_dfa_string`] reports, so harnesses asserting on the
1429    /// observed-DFA trace (ANTLR's `showDFA` descriptors) enable this before
1430    /// lexing.
1431    pub const fn set_force_interpreted(&mut self, force_interpreted: bool) {
1432        self.force_interpreted = force_interpreted;
1433    }
1434
1435    /// Whether compiled-DFA entry points must fall back to interpretation.
1436    pub const fn force_interpreted(&self) -> bool {
1437        self.force_interpreted
1438    }
1439
1440    /// Buffers a lexer diagnostic until the token stream consumer is ready to
1441    /// emit errors in parser-compatible order.
1442    pub fn record_error(&self, line: usize, column: usize, message: impl Into<String>) {
1443        self.errors
1444            .borrow_mut()
1445            .push(TokenSourceError::new(line, column, message));
1446    }
1447
1448    /// Records one fail-loud semantic-hook miss per coordinate and token start.
1449    pub fn record_semantic_error(&self, action: bool, rule_index: usize, coordinate_index: usize) {
1450        let kind = u8::from(action);
1451        if !self.semantic_error_coordinates.borrow_mut().insert((
1452            kind,
1453            rule_index,
1454            coordinate_index,
1455            self.token_start,
1456        )) {
1457            return;
1458        }
1459        let label = if action { "action" } else { "predicate" };
1460        self.record_error(
1461            self.token_start_line,
1462            self.token_start_column,
1463            format!("unhandled lexer semantic {label}: rule={rule_index} index={coordinate_index}"),
1464        );
1465    }
1466
1467    /// Returns and clears lexer diagnostics produced while fetching tokens.
1468    pub fn drain_errors(&mut self) -> Vec<TokenSourceError> {
1469        std::mem::take(self.errors.get_mut())
1470    }
1471
1472    /// Borrows the canonical caller-context store paired with this lexer's
1473    /// learned DFA.
1474    pub(crate) fn lexer_prediction_store(&self) -> RefMut<'_, LexerPredictionStore> {
1475        RefMut::map(self.dfa_cache.borrow_mut(), |cache| &mut cache.prediction)
1476    }
1477
1478    /// Starts a fresh token prediction while retaining bounded scratch
1479    /// allocations for subsequent matches.
1480    pub(crate) fn reset_lexer_prediction_workspace(&self) {
1481        self.dfa_cache.borrow_mut().prediction.workspace.reset();
1482    }
1483
1484    #[cfg(test)]
1485    pub(crate) fn lexer_dfa_cache_shape(&self) -> (usize, usize, usize, usize) {
1486        let cache = self.dfa_cache.borrow();
1487        let cached_states = cache.cached_states.iter().flatten().count();
1488        let cached_transitions = cache
1489            .dense_edges
1490            .iter()
1491            .flatten()
1492            .map(|row| {
1493                row.iter()
1494                    .filter(|transition| transition.target_state != usize::MAX)
1495                    .count()
1496            })
1497            .sum::<usize>()
1498            + cache.sparse_edges.len();
1499        let max_configs = cache
1500            .cached_states
1501            .iter()
1502            .flatten()
1503            .map(|state| state.configs.len())
1504            .max()
1505            .unwrap_or(0);
1506        let contexts = cache.prediction.contexts.len();
1507        (cached_states, cached_transitions, max_configs, contexts)
1508    }
1509
1510    /// Returns the stable state number for a normalized lexer DFA config set,
1511    /// creating one if this input path has not reached it before.
1512    pub(crate) fn lexer_dfa_state(
1513        &self,
1514        key: LexerDfaKey,
1515        accept_prediction: Option<i32>,
1516    ) -> usize {
1517        let mut cache = self.dfa_cache.borrow_mut();
1518        let next = cache.state_numbers.len();
1519        let state = *cache.state_numbers.entry(key).or_insert(next);
1520        if let Some(prediction) = accept_prediction {
1521            cache.accept_predictions.insert(state, prediction);
1522        }
1523        state
1524    }
1525
1526    /// Records a visible lexer DFA edge unless it was already observed.
1527    pub fn record_lexer_dfa_edge(&self, from: usize, symbol: i32, to: usize) {
1528        self.dfa_cache
1529            .borrow_mut()
1530            .edges
1531            .insert(LexerDfaEdge { from, symbol, to });
1532    }
1533
1534    pub(crate) fn cached_lexer_dfa_transition(
1535        &self,
1536        state: usize,
1537        symbol: i32,
1538    ) -> Option<LexerDfaCachedTransition> {
1539        let cache = self.dfa_cache.borrow();
1540        if let Ok(sym) = usize::try_from(symbol)
1541            && sym < DENSE_EDGE_SYMBOLS
1542        {
1543            let transition = cache.dense_edges.get(state)?.as_ref()?[sym];
1544            return (transition.target_state != usize::MAX).then_some(transition);
1545        }
1546        cache.sparse_edges.get(&(state, symbol)).copied()
1547    }
1548
1549    pub(crate) fn cache_lexer_dfa_transition(
1550        &self,
1551        state: usize,
1552        symbol: i32,
1553        transition: LexerDfaCachedTransition,
1554    ) {
1555        let mut cache = self.dfa_cache.borrow_mut();
1556        if let Ok(sym) = usize::try_from(symbol)
1557            && sym < DENSE_EDGE_SYMBOLS
1558        {
1559            if cache.dense_edges.len() <= state {
1560                cache.dense_edges.resize_with(state + 1, || None);
1561            }
1562            let row = cache.dense_edges[state]
1563                .get_or_insert_with(|| Box::new([EMPTY_DENSE_EDGE; DENSE_EDGE_SYMBOLS]));
1564            // First write wins, matching the previous map `entry().or_insert`.
1565            if row[sym].target_state == usize::MAX {
1566                row[sym] = transition;
1567            }
1568            return;
1569        }
1570        cache
1571            .sparse_edges
1572            .entry((state, symbol))
1573            .or_insert(transition);
1574    }
1575
1576    pub(crate) fn cached_lexer_dfa_state(&self, state: usize) -> Option<Rc<LexerDfaCachedState>> {
1577        self.dfa_cache
1578            .borrow()
1579            .cached_states
1580            .get(state)
1581            .cloned()
1582            .flatten()
1583    }
1584
1585    pub(crate) fn cache_lexer_dfa_state(&self, state: usize, cached_state: LexerDfaCachedState) {
1586        let mut cache = self.dfa_cache.borrow_mut();
1587        if cache.cached_states.len() <= state {
1588            cache.cached_states.resize_with(state + 1, || None);
1589        }
1590        cache.cached_states[state].get_or_insert_with(|| Rc::new(cached_state));
1591    }
1592
1593    pub(crate) fn cached_lexer_mode_start(&self, mode: i32) -> Option<usize> {
1594        self.dfa_cache.borrow().mode_starts.get(&mode).copied()
1595    }
1596
1597    pub(crate) fn cache_lexer_mode_start(&self, mode: i32, state: usize) {
1598        self.dfa_cache
1599            .borrow_mut()
1600            .mode_starts
1601            .entry(mode)
1602            .or_insert(state);
1603    }
1604
1605    /// Serializes the observed default-mode lexer DFA in ANTLR's text shape.
1606    pub fn lexer_dfa_string(&self) -> String {
1607        let mut out = String::new();
1608        let cache = self.dfa_cache.borrow();
1609        for edge in &cache.edges {
1610            let Some(label) = lexer_dfa_edge_label(edge.symbol) else {
1611                continue;
1612            };
1613            out.push_str(&self.lexer_dfa_state_string(edge.from));
1614            out.push('-');
1615            out.push_str(&label);
1616            out.push_str("->");
1617            out.push_str(&self.lexer_dfa_state_string(edge.to));
1618            out.push('\n');
1619        }
1620        out
1621    }
1622
1623    fn lexer_dfa_state_string(&self, state: usize) -> String {
1624        self.dfa_cache
1625            .borrow()
1626            .accept_predictions
1627            .get(&state)
1628            .map_or_else(
1629                || format!("s{state}"),
1630                |prediction| format!(":s{state}=>{prediction}"),
1631            )
1632    }
1633}
1634
1635fn lexer_dfa_edge_label(symbol: i32) -> Option<String> {
1636    char::from_u32(symbol.cast_unsigned()).map(|ch| format!("'{ch}'"))
1637}
1638
1639#[cfg(test)]
1640#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
1641mod tests {
1642    use super::*;
1643    use crate::char_stream::InputStream;
1644    use crate::int_stream::IntStream;
1645    use crate::recognizer::RecognizerData;
1646    use crate::token::{DEFAULT_CHANNEL, Token, TokenStore};
1647    use crate::vocabulary::Vocabulary;
1648
1649    #[derive(Clone, Debug)]
1650    struct UnsharedInput(InputStream);
1651
1652    impl IntStream for UnsharedInput {
1653        fn consume(&mut self) {
1654            self.0.consume();
1655        }
1656
1657        fn la(&mut self, offset: isize) -> i32 {
1658            self.0.la(offset)
1659        }
1660
1661        fn index(&self) -> usize {
1662            self.0.index()
1663        }
1664
1665        fn seek(&mut self, index: usize) {
1666            self.0.seek(index);
1667        }
1668
1669        fn size(&self) -> usize {
1670            self.0.size()
1671        }
1672
1673        fn source_name(&self) -> &str {
1674            self.0.source_name()
1675        }
1676    }
1677
1678    impl CharStream for UnsharedInput {
1679        fn text(&self, interval: TextInterval) -> String {
1680            self.0.text(interval)
1681        }
1682
1683        fn byte_interval(&self, interval: TextInterval) -> Option<(usize, usize)> {
1684            self.0.byte_interval(interval)
1685        }
1686    }
1687
1688    #[test]
1689    fn eof_token_uses_utf8_byte_offset_after_non_ascii_input() {
1690        let data = RecognizerData::new(
1691            "T",
1692            Vocabulary::new(
1693                std::iter::empty::<Option<&str>>(),
1694                std::iter::empty::<Option<&str>>(),
1695                std::iter::empty::<Option<&str>>(),
1696            ),
1697        );
1698        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1699        lexer.consume_char();
1700
1701        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1702        let mut sink = TokenSink::new(&mut store);
1703        let id = lexer.eof_token(&mut sink).expect("test token should fit");
1704        let token = sink.view(id).expect("emitted token should exist");
1705
1706        // byte_span is the field this test exists to pin and is absent from TokenView's Debug, so
1707        // snapshot the explicit (start, stop, text, byte_span) record rather than the token.
1708        insta::assert_compact_debug_snapshot!(
1709            (token.start(), token.stop(), token.text(), token.byte_span()),
1710            @r#"(1, 0, Some("<EOF>"), 2..2)"#
1711        );
1712    }
1713
1714    #[test]
1715    fn eof_rule_token_uses_utf8_byte_offset_after_non_ascii_input() {
1716        let data = RecognizerData::new(
1717            "T",
1718            Vocabulary::new(
1719                std::iter::empty::<Option<&str>>(),
1720                std::iter::empty::<Option<&str>>(),
1721                std::iter::empty::<Option<&str>>(),
1722            ),
1723        );
1724        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1725        lexer.consume_char();
1726        lexer.begin_token();
1727
1728        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1729        let mut sink = TokenSink::new(&mut store);
1730        let id = lexer
1731            .emit_with_stop(&mut sink, 1, DEFAULT_CHANNEL, 0, Some("<EOF>".to_owned()))
1732            .expect("test token should fit");
1733        let token = sink.view(id).expect("emitted token should exist");
1734
1735        // byte_span is the field this test exists to pin and is absent from TokenView's Debug, so
1736        // snapshot the explicit (start, stop, text, byte_span) record rather than the token.
1737        insta::assert_compact_debug_snapshot!(
1738            (token.start(), token.stop(), token.text(), token.byte_span()),
1739            @r#"(1, 0, Some("<EOF>"), 2..2)"#
1740        );
1741    }
1742
1743    #[test]
1744    fn emit_implicit_text_uses_utf8_byte_span_for_non_ascii_input() {
1745        let data = RecognizerData::new(
1746            "T",
1747            Vocabulary::new(
1748                std::iter::empty::<Option<&str>>(),
1749                std::iter::empty::<Option<&str>>(),
1750                std::iter::empty::<Option<&str>>(),
1751            ),
1752        );
1753        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1754        lexer.begin_token();
1755        lexer.consume_char();
1756
1757        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1758        let mut sink = TokenSink::new(&mut store);
1759        let id = lexer
1760            .emit(&mut sink, 1, DEFAULT_CHANNEL, None)
1761            .expect("test token should fit");
1762        let token = sink.view(id).expect("emitted token should exist");
1763
1764        // byte_span is the field this test exists to pin and is absent from TokenView's Debug, so
1765        // snapshot the explicit (start, stop, text, byte_span) record rather than the token.
1766        insta::assert_compact_debug_snapshot!(
1767            (token.start(), token.stop(), token.text(), token.byte_span()),
1768            @r#"(0, 0, Some("β"), 0..2)"#
1769        );
1770    }
1771
1772    #[test]
1773    fn emit_falls_back_to_explicit_text_without_shareable_source() {
1774        let data = RecognizerData::new(
1775            "T",
1776            Vocabulary::new(
1777                std::iter::empty::<Option<&str>>(),
1778                std::iter::empty::<Option<&str>>(),
1779                std::iter::empty::<Option<&str>>(),
1780            ),
1781        );
1782        let mut lexer = BaseLexer::new(UnsharedInput(InputStream::new("β")), data);
1783        lexer.begin_token();
1784        lexer.consume_char();
1785
1786        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1787        let mut sink = TokenSink::new(&mut store);
1788        let id = lexer
1789            .emit(&mut sink, 1, DEFAULT_CHANNEL, None)
1790            .expect("unshared input should emit explicit token text");
1791        let token = sink.view(id).expect("emitted token should exist");
1792
1793        assert_eq!(token.text(), Some("β"));
1794        assert_eq!(token.byte_span(), 0..2);
1795    }
1796
1797    #[test]
1798    fn position_commits_and_rewinds_preserve_line_and_column() {
1799        let data = RecognizerData::new(
1800            "T",
1801            Vocabulary::new(
1802                std::iter::empty::<Option<&str>>(),
1803                std::iter::empty::<Option<&str>>(),
1804                std::iter::empty::<Option<&str>>(),
1805            ),
1806        );
1807        let mut lexer = BaseLexer::new(InputStream::new("ab\nγd"), data);
1808        lexer.begin_token();
1809
1810        lexer.commit_position(0, 5);
1811        assert_eq!(lexer.input().index(), 5);
1812        assert_eq!((lexer.line(), lexer.column()), (2, 2));
1813        assert_eq!(lexer.column_at(2), 2);
1814        assert_eq!(lexer.column_at(4), 1);
1815
1816        lexer.reset_accept_position(3);
1817        assert_eq!(lexer.input().index(), 3);
1818        assert_eq!((lexer.line(), lexer.column()), (2, 0));
1819    }
1820
1821    #[test]
1822    fn custom_stream_position_commit_replays_without_fast_path_methods() {
1823        let data = RecognizerData::new(
1824            "T",
1825            Vocabulary::new(
1826                std::iter::empty::<Option<&str>>(),
1827                std::iter::empty::<Option<&str>>(),
1828                std::iter::empty::<Option<&str>>(),
1829            ),
1830        );
1831        let mut lexer = BaseLexer::new(UnsharedInput(InputStream::new("a\nb")), data);
1832        lexer.begin_token();
1833
1834        lexer.commit_position(0, 3);
1835        assert_eq!(lexer.input().index(), 3);
1836        assert_eq!((lexer.line(), lexer.column()), (2, 1));
1837    }
1838
1839    #[test]
1840    fn semantic_hook_errors_are_deduplicated_per_token_coordinate() {
1841        let data = RecognizerData::new(
1842            "T",
1843            Vocabulary::new(
1844                std::iter::empty::<Option<&str>>(),
1845                std::iter::empty::<Option<&str>>(),
1846                std::iter::empty::<Option<&str>>(),
1847            ),
1848        );
1849        let mut lexer = BaseLexer::new(InputStream::new("a"), data);
1850        lexer.begin_token();
1851        lexer.record_semantic_error(false, 3, 7);
1852        lexer.record_semantic_error(false, 3, 7);
1853
1854        let errors = lexer.drain_errors();
1855        insta::assert_compact_debug_snapshot!(errors, @r#"[TokenSourceError { line: 1, column: 0, message: "unhandled lexer semantic predicate: rule=3 index=7" }]"#);
1856
1857        lexer.begin_token();
1858        lexer.record_semantic_error(false, 3, 7);
1859        assert_eq!(
1860            lexer.drain_errors().len(),
1861            1,
1862            "deduplication resets at every token boundary, even after rewinding"
1863        );
1864    }
1865
1866    #[test]
1867    fn set_input_stream_replaces_input_and_resets_transient_state() {
1868        let data = RecognizerData::new(
1869            "T",
1870            Vocabulary::new(
1871                std::iter::empty::<Option<&str>>(),
1872                std::iter::empty::<Option<&str>>(),
1873                std::iter::empty::<Option<&str>>(),
1874            ),
1875        );
1876        let mut lexer = BaseLexer::new(InputStream::new("old"), data);
1877        lexer.consume_char();
1878        lexer.set_mode(7);
1879        lexer.push_mode(9);
1880        lexer.set_type(3);
1881        lexer.record_error(1, 0, "stale");
1882
1883        lexer.set_input_stream(InputStream::with_source_name("new", "replacement"));
1884
1885        assert_eq!(lexer.input().index(), 0);
1886        assert_eq!(lexer.input().size(), 3);
1887        assert_eq!(lexer.source_name(), "replacement");
1888        assert_eq!(lexer.source_text().as_deref(), Some("new"));
1889        assert_eq!(lexer.mode(), DEFAULT_MODE);
1890        assert_eq!(lexer.token_type(), INVALID_TOKEN_TYPE);
1891        assert_eq!((lexer.line(), lexer.column()), (1, 0));
1892        assert!(!lexer.hit_eof());
1893        assert!(lexer.drain_errors().is_empty());
1894        assert!(lexer.pop_mode().is_none());
1895    }
1896
1897    #[test]
1898    fn clear_dfa_invalidates_all_lexers_sharing_the_cache() {
1899        let atn = Box::leak(Box::new(LexerAtn::new(1)));
1900        let data = || {
1901            RecognizerData::new(
1902                "T",
1903                Vocabulary::new(
1904                    std::iter::empty::<Option<&str>>(),
1905                    std::iter::empty::<Option<&str>>(),
1906                    std::iter::empty::<Option<&str>>(),
1907                ),
1908            )
1909        };
1910        let first = BaseLexer::new(InputStream::new("a"), data()).with_shared_dfa(atn);
1911        let second = BaseLexer::new(InputStream::new("a"), data()).with_shared_dfa(atn);
1912        let state = first.lexer_dfa_state(LexerDfaKey::new(Vec::new()), Some(1));
1913        first.record_lexer_dfa_edge(state, i32::from(b'a'), state);
1914
1915        assert!(!second.lexer_dfa_string().is_empty());
1916        first.clear_dfa();
1917        assert!(first.lexer_dfa_string().is_empty());
1918        assert!(second.lexer_dfa_string().is_empty());
1919    }
1920}