Skip to main content

antlr4_runtime/
lexer.rs

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