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/// Normalized lexer ATN config-set identity used for observed DFA traces.
782#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
783pub(crate) struct LexerDfaKey {
784    configs: Vec<LexerDfaConfigKey>,
785}
786
787impl LexerDfaKey {
788    pub(crate) fn new(mut configs: Vec<LexerDfaConfigKey>) -> Self {
789        configs.sort_unstable();
790        Self { configs }
791    }
792}
793
794/// One lexer ATN config identity with the absolute input position removed.
795#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
796pub(crate) struct LexerDfaConfigKey {
797    pub(crate) state: usize,
798    pub(crate) alt_rule_index: Option<usize>,
799    pub(crate) consumed_eof: bool,
800    pub(crate) passed_non_greedy: bool,
801    pub(crate) context: LexerContextId,
802    pub(crate) actions: Vec<LexerDfaActionKey>,
803}
804
805#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
806pub(crate) struct LexerDfaActionKey {
807    pub(crate) action_index: usize,
808    pub(crate) position_delta: usize,
809    pub(crate) rule_index: usize,
810}
811
812impl LexerDfaConfigKey {
813    pub(crate) const fn new(
814        state: usize,
815        alt_rule_index: Option<usize>,
816        consumed_eof: bool,
817        passed_non_greedy: bool,
818        context: LexerContextId,
819        actions: Vec<LexerDfaActionKey>,
820    ) -> Self {
821        Self {
822            state,
823            alt_rule_index,
824            consumed_eof,
825            passed_non_greedy,
826            context,
827            actions,
828        }
829    }
830}
831
832#[derive(Clone, Copy, Debug)]
833pub(crate) struct LexerDfaCachedTransition {
834    pub(crate) target_state: usize,
835    pub(crate) position_delta: usize,
836}
837
838#[derive(Clone, Debug)]
839pub(crate) struct LexerDfaCachedAccept {
840    pub(crate) position_delta: usize,
841    pub(crate) rule_index: usize,
842    pub(crate) consumed_eof: bool,
843    pub(crate) actions: Vec<LexerDfaActionKey>,
844}
845
846#[derive(Clone, Debug)]
847pub(crate) struct LexerDfaCachedState {
848    pub(crate) has_semantic_context: bool,
849    pub(crate) configs: Vec<LexerDfaConfigKey>,
850    pub(crate) accept: Option<LexerDfaCachedAccept>,
851}
852
853/// One printable lexer DFA edge keyed so repeated matches keep deterministic
854/// output order.
855#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
856struct LexerDfaEdge {
857    from: usize,
858    symbol: i32,
859    to: usize,
860}
861
862impl<I> BaseLexer<I>
863where
864    I: CharStream,
865{
866    pub fn new(input: I, data: RecognizerData) -> Self {
867        let has_source_text = input.source_text().is_some();
868        Self {
869            input,
870            data,
871            has_source_text,
872            mode: DEFAULT_MODE,
873            mode_stack: Vec::new(),
874            token_type: INVALID_TOKEN_TYPE,
875            channel: DEFAULT_CHANNEL,
876            token_start: 0,
877            token_start_line: 1,
878            token_start_column: 0,
879            line: 1,
880            column: 0,
881            hit_eof: false,
882            force_interpreted: false,
883            errors: RefCell::new(Vec::new()),
884            semantic_error_coordinates: RefCell::new(BTreeSet::new()),
885            pending_tokens: VecDeque::new(),
886            dfa_cache: Rc::new(RefCell::new(LexerDfaCache::default())),
887        }
888    }
889
890    /// Resets runtime-owned lexer state so this instance can consume its input
891    /// again from the beginning.
892    ///
893    /// Learned DFA tables and configuration such as forced interpretation are
894    /// retained. Token-production state, diagnostics, pending tokens, modes,
895    /// and source position are cleared.
896    pub fn reset(&mut self) {
897        self.input.seek(0);
898        self.mode = DEFAULT_MODE;
899        self.mode_stack.clear();
900        self.token_type = INVALID_TOKEN_TYPE;
901        self.channel = DEFAULT_CHANNEL;
902        self.token_start = 0;
903        self.token_start_line = 1;
904        self.token_start_column = 0;
905        self.line = 1;
906        self.column = 0;
907        self.hit_eof = false;
908        self.errors.get_mut().clear();
909        self.semantic_error_coordinates.get_mut().clear();
910        self.pending_tokens.clear();
911    }
912
913    /// Replaces the character stream and fully resets lexer state for reuse.
914    ///
915    /// Learned DFA tables and configuration such as forced interpretation are
916    /// retained. The new stream is always rewound to its beginning.
917    pub fn set_input_stream(&mut self, input: I) {
918        self.input = input;
919        self.has_source_text = self.input.source_text().is_some();
920        self.reset();
921    }
922
923    /// Switches this lexer to the thread-shared learned DFA for `atn`.
924    ///
925    /// Generated lexers create a fresh instance per parse; without sharing,
926    /// every instance relearns the same DFA through ATN simulation. The shared
927    /// cache is keyed by the generated lexer's `&'static LexerAtn` identity and
928    /// holds only input-independent data, so it stays valid across inputs.
929    /// The `showDFA` edge trace lives in the cache too, so it reports the
930    /// accumulated DFA — the same view the reference runtimes print from
931    /// their static shared DFA.
932    #[must_use]
933    pub fn with_shared_dfa(mut self, atn: &'static LexerAtn) -> Self {
934        let ptr: *const LexerAtn = atn;
935        let key = ptr as usize;
936        self.dfa_cache = SHARED_LEXER_DFA_CACHES
937            .with(|caches| Rc::clone(caches.borrow_mut().entry(key).or_insert_with(Rc::default)));
938        self
939    }
940
941    /// Clears the learned lexer DFA shared by recognizers for this grammar.
942    ///
943    /// Ahead-of-time compiled DFA tables are immutable generated data and are
944    /// unaffected. Any path that falls back to ATN interpretation relearns its
945    /// dynamic DFA from an empty cache after this call.
946    pub fn clear_dfa(&self) {
947        let mut cache = self.dfa_cache.borrow_mut();
948        // In-flight predicate evaluation may clear the DFA while its configs
949        // still hold store-local context IDs.
950        let prediction = std::mem::take(&mut cache.prediction);
951        *cache = LexerDfaCache {
952            prediction,
953            ..LexerDfaCache::default()
954        };
955    }
956
957    pub const fn input(&self) -> &I {
958        &self.input
959    }
960
961    pub const fn input_mut(&mut self) -> &mut I {
962        &mut self.input
963    }
964
965    /// Captures the input index and source position for the token currently
966    /// being matched.
967    pub fn begin_token(&mut self) {
968        self.semantic_error_coordinates.get_mut().clear();
969        self.token_type = INVALID_TOKEN_TYPE;
970        self.channel = DEFAULT_CHANNEL;
971        self.token_start = self.input.index();
972        self.token_start_line = self.line;
973        self.token_start_column = self.column;
974    }
975
976    /// Returns the absolute character index where the current token began.
977    pub const fn token_start(&self) -> usize {
978        self.token_start
979    }
980
981    /// Returns the source line captured at the start of the current token.
982    pub const fn token_start_line(&self) -> usize {
983        self.token_start_line
984    }
985
986    /// Returns the source column captured at the start of the current token.
987    pub const fn token_start_column(&self) -> usize {
988        self.token_start_column
989    }
990
991    /// Returns the pending type of the token being matched.
992    pub const fn token_type(&self) -> i32 {
993        self.token_type
994    }
995
996    /// Overrides the pending type of the token being matched.
997    pub const fn set_type(&mut self, token_type: i32) {
998        self.token_type = token_type;
999    }
1000
1001    /// Returns the pending channel of the token being matched.
1002    pub const fn channel(&self) -> i32 {
1003        self.channel
1004    }
1005
1006    /// Overrides the pending channel of the token being matched.
1007    pub const fn set_channel(&mut self, channel: i32) {
1008        self.channel = channel;
1009    }
1010
1011    /// Marks the current match as skipped.
1012    pub const fn skip(&mut self) {
1013        self.set_type(SKIP);
1014    }
1015
1016    /// Extends the current token with another lexer-rule match.
1017    pub const fn more(&mut self) {
1018        self.set_type(MORE);
1019    }
1020
1021    /// Reads a character at a one-based lookahead/lookbehind offset from the
1022    /// committed input cursor without moving it.
1023    pub fn la(&mut self, offset: isize) -> i32 {
1024        self.input.la(offset)
1025    }
1026
1027    fn lookahead_at(&self, position: usize, offset: isize) -> i32 {
1028        if offset == 0 {
1029            return 0;
1030        }
1031        let absolute = if offset > 0 {
1032            position.checked_add((offset - 1).cast_unsigned())
1033        } else {
1034            offset
1035                .checked_neg()
1036                .and_then(|distance| usize::try_from(distance).ok())
1037                .and_then(|distance| position.checked_sub(distance))
1038        };
1039        let Some(index) = absolute.filter(|index| *index < self.input.size()) else {
1040            return EOF;
1041        };
1042        if let Some(symbol) = self.input.symbol_at(index) {
1043            return symbol;
1044        }
1045        self.input
1046            .text(TextInterval::new(index, index))
1047            .chars()
1048            .next()
1049            .map_or(EOF, |ch| u32::from(ch).cast_signed())
1050    }
1051
1052    /// Consumes one character from the input stream and updates lexer line and
1053    /// column counters.
1054    ///
1055    /// The input stream is indexed by Unicode scalar values. Newline handling
1056    /// follows ANTLR's default convention of incrementing the line and resetting
1057    /// the column after `\n`.
1058    pub fn consume_char(&mut self) {
1059        let la = self.input.la(1);
1060        if la == EOF {
1061            return;
1062        }
1063        self.input.consume();
1064        if char::from_u32(la.cast_unsigned()) == Some('\n') {
1065            self.line += 1;
1066            self.column = 0;
1067        } else {
1068            self.column += 1;
1069        }
1070    }
1071
1072    /// Commits a predicted input span while keeping the current line and column
1073    /// as the coordinates at `start`.
1074    pub(crate) fn commit_position(&mut self, start: usize, target: usize) {
1075        self.reposition_from(start, self.line, self.column, target);
1076    }
1077
1078    fn reposition_from(&mut self, start: usize, line: usize, column: usize, target: usize) {
1079        let start = start.min(self.input.size());
1080        let target = target.max(start).min(self.input.size());
1081        if let Some(summary) = self.input.position_summary(start, target) {
1082            self.input.seek(target);
1083            (self.line, self.column) = summary.apply(line, column);
1084            #[cfg(feature = "perf-counters")]
1085            crate::perf::record_lexer_bulk_commit(target - start);
1086            return;
1087        }
1088
1089        self.input.seek(start);
1090        self.line = line;
1091        self.column = column;
1092        #[cfg(feature = "perf-counters")]
1093        let before = self.input.index();
1094        while self.input.index() < target && self.input.la(1) != EOF {
1095            self.consume_char();
1096        }
1097        #[cfg(feature = "perf-counters")]
1098        crate::perf::record_lexer_scalar_replay(self.input.index().saturating_sub(before));
1099    }
1100
1101    /// Rewinds or advances the input cursor to a token accept boundary.
1102    ///
1103    /// Some generated lexers intentionally accept a longer path to disambiguate
1104    /// a token, then emit only the prefix and leave the suffix for the next
1105    /// token. Recomputing line/column from `token_start` keeps the visible lexer
1106    /// position consistent after moving the cursor backwards.
1107    pub fn reset_accept_position(&mut self, index: usize) {
1108        let target = index.max(self.token_start);
1109        self.reposition_from(
1110            self.token_start,
1111            self.token_start_line,
1112            self.token_start_column,
1113            target,
1114        );
1115    }
1116
1117    /// Moves the current token start forward within the consumed input span.
1118    ///
1119    /// Source line and column are advanced with the start, so a subsequently
1120    /// emitted suffix token carries the same coordinates it would have had if
1121    /// lexed independently.
1122    pub fn set_token_start(&mut self, index: usize) -> bool {
1123        if index < self.token_start || index > self.input.index() {
1124            return false;
1125        }
1126        let (line, column) = self.position_at(index);
1127        self.token_start = index;
1128        self.token_start_line = line;
1129        self.token_start_column = column;
1130        true
1131    }
1132
1133    /// Builds a token spanning from the current token start to the character
1134    /// before the input cursor.
1135    ///
1136    /// When generated or interpreted lexer code does not supply explicit text,
1137    /// the base lexer captures the matched source interval so downstream token
1138    /// streams and parse trees can render token text without retaining a source
1139    /// pair object.
1140    pub fn emit(
1141        &self,
1142        sink: &mut TokenSink<'_>,
1143        token_type: i32,
1144        channel: i32,
1145        text: Option<String>,
1146    ) -> Result<TokenId, TokenStoreError> {
1147        let stop = self.input.index().checked_sub(1).unwrap_or(usize::MAX);
1148        self.emit_with_stop(sink, token_type, channel, stop, text)
1149    }
1150
1151    /// Builds a token with an explicit stop index.
1152    ///
1153    /// EOF-matching lexer rules do not consume a Unicode scalar value, so their
1154    /// stop index can be one before the current input index. The caller passes
1155    /// `usize::MAX` to represent ANTLR's `-1` stop index at empty input.
1156    pub fn emit_with_stop(
1157        &self,
1158        sink: &mut TokenSink<'_>,
1159        token_type: i32,
1160        channel: i32,
1161        stop: usize,
1162        text: Option<String>,
1163    ) -> Result<TokenId, TokenStoreError> {
1164        sink.push(self.token_spec_with_stop(token_type, channel, stop, text))
1165    }
1166
1167    fn token_spec_with_stop(
1168        &self,
1169        token_type: i32,
1170        channel: i32,
1171        stop: usize,
1172        text: Option<String>,
1173    ) -> TokenSpec {
1174        let text = text.or_else(|| {
1175            if stop == usize::MAX {
1176                Some("<EOF>".to_owned())
1177            } else {
1178                None
1179            }
1180        });
1181        let source_interval = if self.has_source_text
1182            && text.is_none()
1183            && stop != usize::MAX
1184            && self.token_start <= stop
1185        {
1186            self.input
1187                .byte_interval(TextInterval::new(self.token_start, stop))
1188        } else {
1189            None
1190        };
1191        let text = text.or_else(|| {
1192            source_interval
1193                .is_none()
1194                .then(|| self.input.text(TextInterval::new(self.token_start, stop)))
1195        });
1196        let (start_byte, stop_byte) = source_interval
1197            .or_else(|| self.token_byte_span(stop))
1198            .unwrap_or((self.token_start, self.token_start));
1199        TokenSpec {
1200            token_type,
1201            channel,
1202            start: self.token_start,
1203            stop,
1204            start_byte,
1205            stop_byte,
1206            line: self.token_start_line,
1207            column: self.token_start_column,
1208            text,
1209            source_backed: source_interval.is_some(),
1210        }
1211    }
1212
1213    /// Queues an additional token to be returned before the current match's
1214    /// automatic token.
1215    ///
1216    /// The token spans the current token start through `stop` (inclusive).
1217    /// `text = None` keeps the token source-backed when the input supports it.
1218    pub fn enqueue_token(
1219        &mut self,
1220        token_type: i32,
1221        channel: i32,
1222        stop: usize,
1223        text: Option<String>,
1224    ) {
1225        let token = self.token_spec_with_stop(token_type, channel, stop, text);
1226        self.pending_tokens.push_back(token);
1227    }
1228
1229    pub(crate) fn emit_pending_token(
1230        &mut self,
1231        sink: &mut TokenSink<'_>,
1232    ) -> Result<Option<TokenId>, TokenStoreError> {
1233        self.pending_tokens
1234            .pop_front()
1235            .map(|token| sink.push(token))
1236            .transpose()
1237    }
1238
1239    pub(crate) fn emit_or_enqueue_with_stop(
1240        &mut self,
1241        sink: &mut TokenSink<'_>,
1242        stop: usize,
1243        text: Option<String>,
1244    ) -> Result<TokenId, TokenStoreError> {
1245        let token = self.token_spec_with_stop(self.token_type, self.channel, stop, text);
1246        self.emit_or_enqueue(sink, token)
1247    }
1248
1249    fn emit_or_enqueue(
1250        &mut self,
1251        sink: &mut TokenSink<'_>,
1252        token: TokenSpec,
1253    ) -> Result<TokenId, TokenStoreError> {
1254        if self.pending_tokens.is_empty() {
1255            return sink.push(token);
1256        }
1257        self.pending_tokens.push_back(token);
1258        self.emit_pending_token(sink)?
1259            .ok_or_else(|| unreachable!("the pending-token queue was just populated"))
1260    }
1261
1262    /// Returns the current token text from the token start through the input
1263    /// cursor.
1264    pub fn token_text(&self) -> String {
1265        self.token_text_until(self.input.index())
1266    }
1267
1268    /// Returns the current token text from the token start through
1269    /// `stop_exclusive`.
1270    ///
1271    /// Lexer custom actions can occur before the accepted token is complete.
1272    /// The action event records the position where the transition fired, and
1273    /// generated action code uses this helper to render ANTLR's `Text()`
1274    /// template at that exact point.
1275    pub fn token_text_until(&self, stop_exclusive: usize) -> String {
1276        if stop_exclusive <= self.token_start {
1277            return String::new();
1278        }
1279        self.input
1280            .text(TextInterval::new(self.token_start, stop_exclusive - 1))
1281    }
1282
1283    /// Computes the zero-based source column at an absolute input position
1284    /// reached during prediction of the current token.
1285    pub fn column_at(&self, position: usize) -> usize {
1286        self.position_at(position).1
1287    }
1288
1289    fn position_at(&self, position: usize) -> (usize, usize) {
1290        let mut line = self.token_start_line;
1291        let mut column = self.token_start_column;
1292        if position <= self.token_start {
1293            return (line, column);
1294        }
1295        if let Some(summary) = self.input.position_summary(self.token_start, position) {
1296            return summary.apply(line, column);
1297        }
1298        for ch in self
1299            .input
1300            .text(TextInterval::new(self.token_start, position - 1))
1301            .chars()
1302        {
1303            if ch == '\n' {
1304                line += 1;
1305                column = 0;
1306            } else {
1307                column += 1;
1308            }
1309        }
1310        (line, column)
1311    }
1312
1313    /// Builds the synthetic EOF token at the current input cursor.
1314    pub fn eof_token(&self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
1315        sink.push(self.eof_token_spec())
1316    }
1317
1318    pub(crate) fn emit_eof_or_pending(
1319        &mut self,
1320        sink: &mut TokenSink<'_>,
1321    ) -> Result<TokenId, TokenStoreError> {
1322        let token = self.eof_token_spec();
1323        self.emit_or_enqueue(sink, token)
1324    }
1325
1326    fn eof_token_spec(&self) -> TokenSpec {
1327        let byte_offset = self.eof_byte_offset().unwrap_or_else(|| self.input.index());
1328        TokenSpec::eof(self.input.index(), byte_offset, self.line, self.column)
1329    }
1330
1331    fn eof_byte_offset(&self) -> Option<usize> {
1332        self.byte_offset_at(self.input.index())
1333    }
1334
1335    fn token_byte_span(&self, stop: usize) -> Option<(usize, usize)> {
1336        if stop != usize::MAX && self.token_start <= stop {
1337            let (start_byte, stop_byte) = self
1338                .input
1339                .byte_interval(TextInterval::new(self.token_start, stop))?;
1340            return Some((start_byte, stop_byte));
1341        }
1342        let byte_offset = self.byte_offset_at(self.token_start)?;
1343        Some((byte_offset, byte_offset))
1344    }
1345
1346    fn byte_offset_at(&self, index: usize) -> Option<usize> {
1347        let byte_offset = if index == 0 {
1348            0
1349        } else {
1350            let previous = TextInterval::new(index - 1, index - 1);
1351            self.input.byte_interval(previous)?.1
1352        };
1353        Some(byte_offset)
1354    }
1355}
1356
1357impl<I> Recognizer for BaseLexer<I>
1358where
1359    I: CharStream,
1360{
1361    fn data(&self) -> &RecognizerData {
1362        &self.data
1363    }
1364
1365    fn data_mut(&mut self) -> &mut RecognizerData {
1366        &mut self.data
1367    }
1368}
1369
1370impl<I> Lexer for BaseLexer<I>
1371where
1372    I: CharStream,
1373{
1374    fn mode(&self) -> i32 {
1375        self.mode
1376    }
1377
1378    fn set_mode(&mut self, mode: i32) {
1379        self.mode = mode;
1380    }
1381
1382    fn push_mode(&mut self, mode: i32) {
1383        self.mode_stack.push(self.mode);
1384        self.mode = mode;
1385    }
1386
1387    fn pop_mode(&mut self) -> Option<i32> {
1388        let mode = self.mode_stack.pop()?;
1389        self.mode = mode;
1390        Some(mode)
1391    }
1392}
1393
1394impl<I> BaseLexer<I>
1395where
1396    I: CharStream,
1397{
1398    pub const fn line(&self) -> usize {
1399        self.line
1400    }
1401
1402    pub const fn column(&self) -> usize {
1403        self.column
1404    }
1405
1406    pub fn source_name(&self) -> &str {
1407        self.input.source_name()
1408    }
1409
1410    pub fn source_text(&self) -> Option<Rc<str>> {
1411        self.input.source_text()
1412    }
1413
1414    pub const fn hit_eof(&self) -> bool {
1415        self.hit_eof
1416    }
1417
1418    pub const fn set_hit_eof(&mut self, hit_eof: bool) {
1419        self.hit_eof = hit_eof;
1420    }
1421
1422    /// Routes every token through ATN interpretation even when the generated
1423    /// lexer carries an ahead-of-time compiled DFA.
1424    ///
1425    /// Interpretation is what learns the replayable DFA that
1426    /// [`Self::lexer_dfa_string`] reports, so harnesses asserting on the
1427    /// observed-DFA trace (ANTLR's `showDFA` descriptors) enable this before
1428    /// lexing.
1429    pub const fn set_force_interpreted(&mut self, force_interpreted: bool) {
1430        self.force_interpreted = force_interpreted;
1431    }
1432
1433    /// Whether compiled-DFA entry points must fall back to interpretation.
1434    pub const fn force_interpreted(&self) -> bool {
1435        self.force_interpreted
1436    }
1437
1438    /// Buffers a lexer diagnostic until the token stream consumer is ready to
1439    /// emit errors in parser-compatible order.
1440    pub fn record_error(&self, line: usize, column: usize, message: impl Into<String>) {
1441        self.errors
1442            .borrow_mut()
1443            .push(TokenSourceError::new(line, column, message));
1444    }
1445
1446    /// Records one fail-loud semantic-hook miss per coordinate and token start.
1447    pub fn record_semantic_error(&self, action: bool, rule_index: usize, coordinate_index: usize) {
1448        let kind = u8::from(action);
1449        if !self.semantic_error_coordinates.borrow_mut().insert((
1450            kind,
1451            rule_index,
1452            coordinate_index,
1453            self.token_start,
1454        )) {
1455            return;
1456        }
1457        let label = if action { "action" } else { "predicate" };
1458        self.record_error(
1459            self.token_start_line,
1460            self.token_start_column,
1461            format!("unhandled lexer semantic {label}: rule={rule_index} index={coordinate_index}"),
1462        );
1463    }
1464
1465    /// Returns and clears lexer diagnostics produced while fetching tokens.
1466    pub fn drain_errors(&mut self) -> Vec<TokenSourceError> {
1467        std::mem::take(self.errors.get_mut())
1468    }
1469
1470    /// Borrows the canonical caller-context store paired with this lexer's
1471    /// learned DFA.
1472    pub(crate) fn lexer_prediction_store(&self) -> RefMut<'_, LexerPredictionStore> {
1473        RefMut::map(self.dfa_cache.borrow_mut(), |cache| &mut cache.prediction)
1474    }
1475
1476    /// Starts a fresh token prediction while retaining bounded scratch
1477    /// allocations for subsequent matches.
1478    pub(crate) fn reset_lexer_prediction_workspace(&self) {
1479        self.dfa_cache.borrow_mut().prediction.workspace.reset();
1480    }
1481
1482    #[cfg(test)]
1483    pub(crate) fn lexer_dfa_cache_shape(&self) -> (usize, usize, usize, usize) {
1484        let cache = self.dfa_cache.borrow();
1485        let cached_states = cache.cached_states.iter().flatten().count();
1486        let cached_transitions = cache
1487            .dense_edges
1488            .iter()
1489            .flatten()
1490            .map(|row| {
1491                row.iter()
1492                    .filter(|transition| transition.target_state != usize::MAX)
1493                    .count()
1494            })
1495            .sum::<usize>()
1496            + cache.sparse_edges.len();
1497        let max_configs = cache
1498            .cached_states
1499            .iter()
1500            .flatten()
1501            .map(|state| state.configs.len())
1502            .max()
1503            .unwrap_or(0);
1504        let contexts = cache.prediction.contexts.len();
1505        (cached_states, cached_transitions, max_configs, contexts)
1506    }
1507
1508    /// Returns the stable state number for a normalized lexer DFA config set,
1509    /// creating one if this input path has not reached it before.
1510    pub(crate) fn lexer_dfa_state(
1511        &self,
1512        key: LexerDfaKey,
1513        accept_prediction: Option<i32>,
1514    ) -> usize {
1515        let mut cache = self.dfa_cache.borrow_mut();
1516        let next = cache.state_numbers.len();
1517        let state = *cache.state_numbers.entry(key).or_insert(next);
1518        if let Some(prediction) = accept_prediction {
1519            cache.accept_predictions.insert(state, prediction);
1520        }
1521        state
1522    }
1523
1524    /// Records a visible lexer DFA edge unless it was already observed.
1525    pub fn record_lexer_dfa_edge(&self, from: usize, symbol: i32, to: usize) {
1526        self.dfa_cache
1527            .borrow_mut()
1528            .edges
1529            .insert(LexerDfaEdge { from, symbol, to });
1530    }
1531
1532    pub(crate) fn cached_lexer_dfa_transition(
1533        &self,
1534        state: usize,
1535        symbol: i32,
1536    ) -> Option<LexerDfaCachedTransition> {
1537        let cache = self.dfa_cache.borrow();
1538        if let Ok(sym) = usize::try_from(symbol)
1539            && sym < DENSE_EDGE_SYMBOLS
1540        {
1541            let transition = cache.dense_edges.get(state)?.as_ref()?[sym];
1542            return (transition.target_state != usize::MAX).then_some(transition);
1543        }
1544        cache.sparse_edges.get(&(state, symbol)).copied()
1545    }
1546
1547    pub(crate) fn cache_lexer_dfa_transition(
1548        &self,
1549        state: usize,
1550        symbol: i32,
1551        transition: LexerDfaCachedTransition,
1552    ) {
1553        let mut cache = self.dfa_cache.borrow_mut();
1554        if let Ok(sym) = usize::try_from(symbol)
1555            && sym < DENSE_EDGE_SYMBOLS
1556        {
1557            if cache.dense_edges.len() <= state {
1558                cache.dense_edges.resize_with(state + 1, || None);
1559            }
1560            let row = cache.dense_edges[state]
1561                .get_or_insert_with(|| Box::new([EMPTY_DENSE_EDGE; DENSE_EDGE_SYMBOLS]));
1562            // First write wins, matching the previous map `entry().or_insert`.
1563            if row[sym].target_state == usize::MAX {
1564                row[sym] = transition;
1565            }
1566            return;
1567        }
1568        cache
1569            .sparse_edges
1570            .entry((state, symbol))
1571            .or_insert(transition);
1572    }
1573
1574    pub(crate) fn cached_lexer_dfa_state(&self, state: usize) -> Option<Rc<LexerDfaCachedState>> {
1575        self.dfa_cache
1576            .borrow()
1577            .cached_states
1578            .get(state)
1579            .cloned()
1580            .flatten()
1581    }
1582
1583    pub(crate) fn cache_lexer_dfa_state(&self, state: usize, cached_state: LexerDfaCachedState) {
1584        let mut cache = self.dfa_cache.borrow_mut();
1585        if cache.cached_states.len() <= state {
1586            cache.cached_states.resize_with(state + 1, || None);
1587        }
1588        cache.cached_states[state].get_or_insert_with(|| Rc::new(cached_state));
1589    }
1590
1591    pub(crate) fn cached_lexer_mode_start(&self, mode: i32) -> Option<usize> {
1592        self.dfa_cache.borrow().mode_starts.get(&mode).copied()
1593    }
1594
1595    pub(crate) fn cache_lexer_mode_start(&self, mode: i32, state: usize) {
1596        self.dfa_cache
1597            .borrow_mut()
1598            .mode_starts
1599            .entry(mode)
1600            .or_insert(state);
1601    }
1602
1603    /// Serializes the observed default-mode lexer DFA in ANTLR's text shape.
1604    pub fn lexer_dfa_string(&self) -> String {
1605        let mut out = String::new();
1606        let cache = self.dfa_cache.borrow();
1607        for edge in &cache.edges {
1608            let Some(label) = lexer_dfa_edge_label(edge.symbol) else {
1609                continue;
1610            };
1611            out.push_str(&self.lexer_dfa_state_string(edge.from));
1612            out.push('-');
1613            out.push_str(&label);
1614            out.push_str("->");
1615            out.push_str(&self.lexer_dfa_state_string(edge.to));
1616            out.push('\n');
1617        }
1618        out
1619    }
1620
1621    fn lexer_dfa_state_string(&self, state: usize) -> String {
1622        self.dfa_cache
1623            .borrow()
1624            .accept_predictions
1625            .get(&state)
1626            .map_or_else(
1627                || format!("s{state}"),
1628                |prediction| format!(":s{state}=>{prediction}"),
1629            )
1630    }
1631}
1632
1633fn lexer_dfa_edge_label(symbol: i32) -> Option<String> {
1634    char::from_u32(symbol.cast_unsigned()).map(|ch| format!("'{ch}'"))
1635}
1636
1637#[cfg(test)]
1638mod tests {
1639    use super::*;
1640    use crate::char_stream::InputStream;
1641    use crate::int_stream::IntStream;
1642    use crate::recognizer::RecognizerData;
1643    use crate::token::{DEFAULT_CHANNEL, Token, TokenStore};
1644    use crate::vocabulary::Vocabulary;
1645
1646    #[derive(Clone, Debug)]
1647    struct UnsharedInput(InputStream);
1648
1649    impl IntStream for UnsharedInput {
1650        fn consume(&mut self) {
1651            self.0.consume();
1652        }
1653
1654        fn la(&mut self, offset: isize) -> i32 {
1655            self.0.la(offset)
1656        }
1657
1658        fn index(&self) -> usize {
1659            self.0.index()
1660        }
1661
1662        fn seek(&mut self, index: usize) {
1663            self.0.seek(index);
1664        }
1665
1666        fn size(&self) -> usize {
1667            self.0.size()
1668        }
1669
1670        fn source_name(&self) -> &str {
1671            self.0.source_name()
1672        }
1673    }
1674
1675    impl CharStream for UnsharedInput {
1676        fn text(&self, interval: TextInterval) -> String {
1677            self.0.text(interval)
1678        }
1679
1680        fn byte_interval(&self, interval: TextInterval) -> Option<(usize, usize)> {
1681            self.0.byte_interval(interval)
1682        }
1683    }
1684
1685    #[test]
1686    fn eof_token_uses_utf8_byte_offset_after_non_ascii_input() {
1687        let data = RecognizerData::new(
1688            "T",
1689            Vocabulary::new(
1690                std::iter::empty::<Option<&str>>(),
1691                std::iter::empty::<Option<&str>>(),
1692                std::iter::empty::<Option<&str>>(),
1693            ),
1694        );
1695        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1696        lexer.consume_char();
1697
1698        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1699        let mut sink = TokenSink::new(&mut store);
1700        let id = lexer.eof_token(&mut sink).expect("test token should fit");
1701        let token = sink.view(id).expect("emitted token should exist");
1702
1703        assert_eq!(token.start(), 1);
1704        assert_eq!(token.stop(), 0);
1705        assert_eq!(token.text(), "<EOF>");
1706        assert_eq!(token.byte_span(), 2..2);
1707    }
1708
1709    #[test]
1710    fn eof_rule_token_uses_utf8_byte_offset_after_non_ascii_input() {
1711        let data = RecognizerData::new(
1712            "T",
1713            Vocabulary::new(
1714                std::iter::empty::<Option<&str>>(),
1715                std::iter::empty::<Option<&str>>(),
1716                std::iter::empty::<Option<&str>>(),
1717            ),
1718        );
1719        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1720        lexer.consume_char();
1721        lexer.begin_token();
1722
1723        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1724        let mut sink = TokenSink::new(&mut store);
1725        let id = lexer
1726            .emit_with_stop(&mut sink, 1, DEFAULT_CHANNEL, 0, Some("<EOF>".to_owned()))
1727            .expect("test token should fit");
1728        let token = sink.view(id).expect("emitted token should exist");
1729
1730        assert_eq!(token.start(), 1);
1731        assert_eq!(token.stop(), 0);
1732        assert_eq!(token.text(), "<EOF>");
1733        assert_eq!(token.byte_span(), 2..2);
1734    }
1735
1736    #[test]
1737    fn emit_implicit_text_uses_utf8_byte_span_for_non_ascii_input() {
1738        let data = RecognizerData::new(
1739            "T",
1740            Vocabulary::new(
1741                std::iter::empty::<Option<&str>>(),
1742                std::iter::empty::<Option<&str>>(),
1743                std::iter::empty::<Option<&str>>(),
1744            ),
1745        );
1746        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
1747        lexer.begin_token();
1748        lexer.consume_char();
1749
1750        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1751        let mut sink = TokenSink::new(&mut store);
1752        let id = lexer
1753            .emit(&mut sink, 1, DEFAULT_CHANNEL, None)
1754            .expect("test token should fit");
1755        let token = sink.view(id).expect("emitted token should exist");
1756
1757        assert_eq!(token.start(), 0);
1758        assert_eq!(token.stop(), 0);
1759        assert_eq!(token.text(), "β");
1760        assert_eq!(token.byte_span(), 0..2);
1761    }
1762
1763    #[test]
1764    fn emit_falls_back_to_explicit_text_without_shareable_source() {
1765        let data = RecognizerData::new(
1766            "T",
1767            Vocabulary::new(
1768                std::iter::empty::<Option<&str>>(),
1769                std::iter::empty::<Option<&str>>(),
1770                std::iter::empty::<Option<&str>>(),
1771            ),
1772        );
1773        let mut lexer = BaseLexer::new(UnsharedInput(InputStream::new("β")), data);
1774        lexer.begin_token();
1775        lexer.consume_char();
1776
1777        let mut store = TokenStore::new(lexer.source_text(), lexer.source_name());
1778        let mut sink = TokenSink::new(&mut store);
1779        let id = lexer
1780            .emit(&mut sink, 1, DEFAULT_CHANNEL, None)
1781            .expect("unshared input should emit explicit token text");
1782        let token = sink.view(id).expect("emitted token should exist");
1783
1784        assert_eq!(token.text(), "β");
1785        assert_eq!(token.byte_span(), 0..2);
1786    }
1787
1788    #[test]
1789    fn position_commits_and_rewinds_preserve_line_and_column() {
1790        let data = RecognizerData::new(
1791            "T",
1792            Vocabulary::new(
1793                std::iter::empty::<Option<&str>>(),
1794                std::iter::empty::<Option<&str>>(),
1795                std::iter::empty::<Option<&str>>(),
1796            ),
1797        );
1798        let mut lexer = BaseLexer::new(InputStream::new("ab\nγd"), data);
1799        lexer.begin_token();
1800
1801        lexer.commit_position(0, 5);
1802        assert_eq!(lexer.input().index(), 5);
1803        assert_eq!((lexer.line(), lexer.column()), (2, 2));
1804        assert_eq!(lexer.column_at(2), 2);
1805        assert_eq!(lexer.column_at(4), 1);
1806
1807        lexer.reset_accept_position(3);
1808        assert_eq!(lexer.input().index(), 3);
1809        assert_eq!((lexer.line(), lexer.column()), (2, 0));
1810    }
1811
1812    #[test]
1813    fn custom_stream_position_commit_replays_without_fast_path_methods() {
1814        let data = RecognizerData::new(
1815            "T",
1816            Vocabulary::new(
1817                std::iter::empty::<Option<&str>>(),
1818                std::iter::empty::<Option<&str>>(),
1819                std::iter::empty::<Option<&str>>(),
1820            ),
1821        );
1822        let mut lexer = BaseLexer::new(UnsharedInput(InputStream::new("a\nb")), data);
1823        lexer.begin_token();
1824
1825        lexer.commit_position(0, 3);
1826        assert_eq!(lexer.input().index(), 3);
1827        assert_eq!((lexer.line(), lexer.column()), (2, 1));
1828    }
1829
1830    #[test]
1831    fn semantic_hook_errors_are_deduplicated_per_token_coordinate() {
1832        let data = RecognizerData::new(
1833            "T",
1834            Vocabulary::new(
1835                std::iter::empty::<Option<&str>>(),
1836                std::iter::empty::<Option<&str>>(),
1837                std::iter::empty::<Option<&str>>(),
1838            ),
1839        );
1840        let mut lexer = BaseLexer::new(InputStream::new("a"), data);
1841        lexer.begin_token();
1842        lexer.record_semantic_error(false, 3, 7);
1843        lexer.record_semantic_error(false, 3, 7);
1844
1845        let errors = lexer.drain_errors();
1846        assert_eq!(errors.len(), 1);
1847        assert_eq!(
1848            errors[0].message,
1849            "unhandled lexer semantic predicate: rule=3 index=7"
1850        );
1851
1852        lexer.begin_token();
1853        lexer.record_semantic_error(false, 3, 7);
1854        assert_eq!(
1855            lexer.drain_errors().len(),
1856            1,
1857            "deduplication resets at every token boundary, even after rewinding"
1858        );
1859    }
1860
1861    #[test]
1862    fn set_input_stream_replaces_input_and_resets_transient_state() {
1863        let data = RecognizerData::new(
1864            "T",
1865            Vocabulary::new(
1866                std::iter::empty::<Option<&str>>(),
1867                std::iter::empty::<Option<&str>>(),
1868                std::iter::empty::<Option<&str>>(),
1869            ),
1870        );
1871        let mut lexer = BaseLexer::new(InputStream::new("old"), data);
1872        lexer.consume_char();
1873        lexer.set_mode(7);
1874        lexer.push_mode(9);
1875        lexer.set_type(3);
1876        lexer.record_error(1, 0, "stale");
1877
1878        lexer.set_input_stream(InputStream::with_source_name("new", "replacement"));
1879
1880        assert_eq!(lexer.input().index(), 0);
1881        assert_eq!(lexer.input().size(), 3);
1882        assert_eq!(lexer.source_name(), "replacement");
1883        assert_eq!(lexer.source_text().as_deref(), Some("new"));
1884        assert_eq!(lexer.mode(), DEFAULT_MODE);
1885        assert_eq!(lexer.token_type(), INVALID_TOKEN_TYPE);
1886        assert_eq!((lexer.line(), lexer.column()), (1, 0));
1887        assert!(!lexer.hit_eof());
1888        assert!(lexer.drain_errors().is_empty());
1889        assert!(lexer.pop_mode().is_none());
1890    }
1891
1892    #[test]
1893    fn clear_dfa_invalidates_all_lexers_sharing_the_cache() {
1894        let atn = Box::leak(Box::new(LexerAtn::new(1)));
1895        let data = || {
1896            RecognizerData::new(
1897                "T",
1898                Vocabulary::new(
1899                    std::iter::empty::<Option<&str>>(),
1900                    std::iter::empty::<Option<&str>>(),
1901                    std::iter::empty::<Option<&str>>(),
1902                ),
1903            )
1904        };
1905        let first = BaseLexer::new(InputStream::new("a"), data()).with_shared_dfa(atn);
1906        let second = BaseLexer::new(InputStream::new("a"), data()).with_shared_dfa(atn);
1907        let state = first.lexer_dfa_state(LexerDfaKey::new(Vec::new()), Some(1));
1908        first.record_lexer_dfa_edge(state, i32::from(b'a'), state);
1909
1910        assert!(!second.lexer_dfa_string().is_empty());
1911        first.clear_dfa();
1912        assert!(first.lexer_dfa_string().is_empty());
1913        assert!(second.lexer_dfa_string().is_empty());
1914    }
1915}