Skip to main content

antlr4_runtime/
lexer.rs

1use std::collections::{BTreeSet, HashMap};
2use std::hash::BuildHasherDefault;
3use std::rc::Rc;
4
5use crate::char_stream::{CharStream, TextInterval};
6use crate::int_stream::EOF;
7use crate::prediction::PredictionFxHasher;
8use crate::recognizer::{Recognizer, RecognizerData};
9use crate::token::{CommonToken, CommonTokenFactory, TokenFactory, TokenSourceError, TokenSpec};
10
11#[allow(clippy::disallowed_types)]
12type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PredictionFxHasher>>;
13
14pub const SKIP: i32 = -3;
15pub const MORE: i32 = -2;
16pub const DEFAULT_MODE: i32 = 0;
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub struct LexerMode(pub i32);
20
21/// Grammar-specific lexer action reached on the accepted ATN path.
22///
23/// ANTLR serializes embedded lexer actions as `(rule_index, action_index)`
24/// pairs. The runtime also records the input position where the action was
25/// reached so generated code can evaluate templates such as `Text()` at the
26/// same point as a generated ANTLR lexer, not only at the token end.
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub struct LexerCustomAction {
29    rule_index: i32,
30    action_index: i32,
31    position: usize,
32}
33
34impl LexerCustomAction {
35    /// Creates a custom lexer action event from serialized ATN metadata.
36    pub const fn new(rule_index: i32, action_index: i32, position: usize) -> Self {
37        Self {
38            rule_index,
39            action_index,
40            position,
41        }
42    }
43
44    /// Lexer rule index that owns the embedded action.
45    pub const fn rule_index(self) -> i32 {
46        self.rule_index
47    }
48
49    /// Per-rule action index assigned by ANTLR serialization.
50    pub const fn action_index(self) -> i32 {
51        self.action_index
52    }
53
54    /// Character-stream position at which the action transition was reached.
55    pub const fn position(self) -> usize {
56        self.position
57    }
58}
59
60/// Grammar-specific lexer predicate reached while exploring an ATN path.
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62pub struct LexerPredicate {
63    rule_index: usize,
64    pred_index: usize,
65    position: usize,
66}
67
68impl LexerPredicate {
69    /// Creates a lexer predicate event from serialized ATN metadata.
70    pub const fn new(rule_index: usize, pred_index: usize, position: usize) -> Self {
71        Self {
72            rule_index,
73            pred_index,
74            position,
75        }
76    }
77
78    /// Lexer rule index that owns the predicate transition.
79    pub const fn rule_index(self) -> usize {
80        self.rule_index
81    }
82
83    /// Per-rule predicate index assigned by ANTLR serialization.
84    pub const fn pred_index(self) -> usize {
85        self.pred_index
86    }
87
88    /// Character-stream position at which the predicate is evaluated.
89    pub const fn position(self) -> usize {
90        self.position
91    }
92}
93
94pub trait Lexer: Recognizer {
95    fn mode(&self) -> i32;
96    fn set_mode(&mut self, mode: i32);
97    fn push_mode(&mut self, mode: i32);
98    fn pop_mode(&mut self) -> Option<i32>;
99}
100
101#[derive(Clone, Debug)]
102pub struct BaseLexer<I, F = CommonTokenFactory> {
103    input: I,
104    data: RecognizerData,
105    factory: F,
106    mode: i32,
107    mode_stack: Vec<i32>,
108    token_start: usize,
109    token_start_line: usize,
110    token_start_column: usize,
111    line: usize,
112    column: usize,
113    hit_eof: bool,
114    errors: Vec<TokenSourceError>,
115    lexer_dfa: LexerDfaTrace,
116}
117
118/// Compact observation log for the default-mode lexer DFA printed by `showDFA`
119/// runtime-suite descriptors.
120#[derive(Clone, Debug, Default)]
121struct LexerDfaTrace {
122    state_numbers: FxHashMap<LexerDfaKey, usize>,
123    accept_predictions: FxHashMap<usize, i32>,
124    edges: BTreeSet<LexerDfaEdge>,
125    cached_states: FxHashMap<usize, Rc<LexerDfaCachedState>>,
126    transitions: FxHashMap<(usize, i32), LexerDfaCachedTransition>,
127    mode_starts: FxHashMap<i32, usize>,
128}
129
130impl LexerDfaTrace {
131    fn new() -> Self {
132        Self {
133            state_numbers: FxHashMap::default(),
134            accept_predictions: FxHashMap::default(),
135            edges: BTreeSet::new(),
136            cached_states: FxHashMap::default(),
137            transitions: FxHashMap::default(),
138            mode_starts: FxHashMap::default(),
139        }
140    }
141}
142
143/// Normalized lexer ATN config-set identity used for observed DFA traces.
144#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
145pub(crate) struct LexerDfaKey {
146    configs: Vec<LexerDfaConfigKey>,
147}
148
149impl LexerDfaKey {
150    pub(crate) fn new(mut configs: Vec<LexerDfaConfigKey>) -> Self {
151        configs.sort_unstable();
152        Self { configs }
153    }
154}
155
156/// One lexer ATN config identity with the absolute input position removed.
157#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
158pub(crate) struct LexerDfaConfigKey {
159    pub(crate) state: usize,
160    pub(crate) alt_rule_index: Option<usize>,
161    pub(crate) consumed_eof: bool,
162    pub(crate) passed_non_greedy: bool,
163    pub(crate) stack: Vec<usize>,
164    pub(crate) actions: Vec<LexerDfaActionKey>,
165}
166
167#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
168pub(crate) struct LexerDfaActionKey {
169    pub(crate) action_index: usize,
170    pub(crate) position_delta: usize,
171    pub(crate) rule_index: usize,
172}
173
174impl LexerDfaConfigKey {
175    pub(crate) const fn new(
176        state: usize,
177        alt_rule_index: Option<usize>,
178        consumed_eof: bool,
179        passed_non_greedy: bool,
180        stack: Vec<usize>,
181        actions: Vec<LexerDfaActionKey>,
182    ) -> Self {
183        Self {
184            state,
185            alt_rule_index,
186            consumed_eof,
187            passed_non_greedy,
188            stack,
189            actions,
190        }
191    }
192}
193
194#[derive(Clone, Debug)]
195pub(crate) struct LexerDfaCachedTransition {
196    pub(crate) target_state: usize,
197    pub(crate) position_delta: usize,
198}
199
200#[derive(Clone, Debug)]
201pub(crate) struct LexerDfaCachedAccept {
202    pub(crate) position_delta: usize,
203    pub(crate) rule_index: usize,
204    pub(crate) consumed_eof: bool,
205    pub(crate) actions: Vec<LexerDfaActionKey>,
206}
207
208#[derive(Clone, Debug)]
209pub(crate) struct LexerDfaCachedState {
210    pub(crate) has_semantic_context: bool,
211    pub(crate) configs: Vec<LexerDfaConfigKey>,
212    pub(crate) accept: Option<LexerDfaCachedAccept>,
213}
214
215/// One printable lexer DFA edge keyed so repeated matches keep deterministic
216/// output order.
217#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
218struct LexerDfaEdge {
219    from: usize,
220    symbol: i32,
221    to: usize,
222}
223
224impl<I> BaseLexer<I>
225where
226    I: CharStream,
227{
228    /// Creates a lexer base using `CommonTokenFactory`.
229    pub fn new(input: I, data: RecognizerData) -> Self {
230        Self::with_factory(input, data, CommonTokenFactory)
231    }
232}
233
234impl<I, F> BaseLexer<I, F>
235where
236    I: CharStream,
237    F: TokenFactory,
238{
239    /// Creates a lexer base with a custom token factory.
240    pub fn with_factory(input: I, data: RecognizerData, factory: F) -> Self {
241        Self {
242            input,
243            data,
244            factory,
245            mode: DEFAULT_MODE,
246            mode_stack: Vec::new(),
247            token_start: 0,
248            token_start_line: 1,
249            token_start_column: 0,
250            line: 1,
251            column: 0,
252            hit_eof: false,
253            errors: Vec::new(),
254            lexer_dfa: LexerDfaTrace::new(),
255        }
256    }
257
258    pub const fn input(&self) -> &I {
259        &self.input
260    }
261
262    pub const fn input_mut(&mut self) -> &mut I {
263        &mut self.input
264    }
265
266    /// Captures the input index and source position for the token currently
267    /// being matched.
268    pub fn begin_token(&mut self) {
269        self.token_start = self.input.index();
270        self.token_start_line = self.line;
271        self.token_start_column = self.column;
272    }
273
274    /// Returns the absolute character index where the current token began.
275    pub const fn token_start(&self) -> usize {
276        self.token_start
277    }
278
279    /// Returns the source line captured at the start of the current token.
280    pub const fn token_start_line(&self) -> usize {
281        self.token_start_line
282    }
283
284    /// Returns the source column captured at the start of the current token.
285    pub const fn token_start_column(&self) -> usize {
286        self.token_start_column
287    }
288
289    /// Consumes one character from the input stream and updates lexer line and
290    /// column counters.
291    ///
292    /// The input stream is indexed by Unicode scalar values. Newline handling
293    /// follows ANTLR's default convention of incrementing the line and resetting
294    /// the column after `\n`.
295    pub fn consume_char(&mut self) {
296        let la = self.input.la(1);
297        if la == EOF {
298            return;
299        }
300        self.input.consume();
301        if char::from_u32(la.cast_unsigned()) == Some('\n') {
302            self.line += 1;
303            self.column = 0;
304        } else {
305            self.column += 1;
306        }
307    }
308
309    /// Rewinds or advances the input cursor to a token accept boundary.
310    ///
311    /// Some generated lexers intentionally accept a longer path to disambiguate
312    /// a token, then emit only the prefix and leave the suffix for the next
313    /// token. Recomputing line/column from `token_start` keeps the visible lexer
314    /// position consistent after moving the cursor backwards.
315    pub fn reset_accept_position(&mut self, index: usize) {
316        let target = index.max(self.token_start);
317        self.input.seek(self.token_start);
318        self.line = self.token_start_line;
319        self.column = self.token_start_column;
320        while self.input.index() < target && self.input.la(1) != EOF {
321            self.consume_char();
322        }
323    }
324
325    /// Builds a token spanning from the current token start to the character
326    /// before the input cursor.
327    ///
328    /// When generated or interpreted lexer code does not supply explicit text,
329    /// the base lexer captures the matched source interval so downstream token
330    /// streams and parse trees can render token text without retaining a source
331    /// pair object.
332    pub fn emit(&self, token_type: i32, channel: i32, text: Option<String>) -> CommonToken {
333        let stop = self.input.index().checked_sub(1).unwrap_or(usize::MAX);
334        self.emit_with_stop(token_type, channel, stop, text)
335    }
336
337    /// Builds a token with an explicit stop index.
338    ///
339    /// EOF-matching lexer rules do not consume a Unicode scalar value, so their
340    /// stop index can be one before the current input index. The caller passes
341    /// `usize::MAX` to represent ANTLR's `-1` stop index at empty input.
342    pub fn emit_with_stop(
343        &self,
344        token_type: i32,
345        channel: i32,
346        stop: usize,
347        text: Option<String>,
348    ) -> CommonToken {
349        let text = text.or_else(|| {
350            if stop == usize::MAX {
351                Some("<EOF>".to_owned())
352            } else {
353                None
354            }
355        });
356        let source_interval = if text.is_none() && stop != usize::MAX && self.token_start <= stop {
357            self.input
358                .text_source_interval(TextInterval::new(self.token_start, stop))
359        } else {
360            None
361        };
362        let source_text = source_interval
363            .as_ref()
364            .and_then(|(input, start_byte, stop_byte)| {
365                Some(crate::token::TokenSourceText {
366                    input: Rc::clone(input),
367                    start_byte: u32::try_from(*start_byte).ok()?,
368                    stop_byte: u32::try_from(*stop_byte).ok()?,
369                })
370            });
371        let source_byte_span = source_text
372            .as_ref()
373            .map(|source_text| (source_text.start_byte, source_text.stop_byte));
374        let text = text.or_else(|| {
375            source_text
376                .is_none()
377                .then(|| self.input.text(TextInterval::new(self.token_start, stop)))
378        });
379        let mut token = self.factory.create(TokenSpec {
380            token_type,
381            channel,
382            start: self.token_start,
383            stop,
384            line: self.token_start_line,
385            column: self.token_start_column,
386            text,
387            source_text,
388            source_name: self.input.source_name(),
389        });
390        if let Some((start_byte, stop_byte)) =
391            source_byte_span.or_else(|| self.token_byte_span(stop))
392        {
393            token = token.with_byte_span(start_byte, stop_byte);
394        }
395        token
396    }
397
398    /// Returns the current token text from the token start through the input
399    /// cursor.
400    pub fn token_text(&self) -> String {
401        self.token_text_until(self.input.index())
402    }
403
404    /// Returns the current token text from the token start through
405    /// `stop_exclusive`.
406    ///
407    /// Lexer custom actions can occur before the accepted token is complete.
408    /// The action event records the position where the transition fired, and
409    /// generated action code uses this helper to render ANTLR's `Text()`
410    /// template at that exact point.
411    pub fn token_text_until(&self, stop_exclusive: usize) -> String {
412        if stop_exclusive <= self.token_start {
413            return String::new();
414        }
415        self.input
416            .text(TextInterval::new(self.token_start, stop_exclusive - 1))
417    }
418
419    /// Computes the zero-based source column at an absolute input position
420    /// reached during prediction of the current token.
421    pub fn column_at(&self, position: usize) -> usize {
422        let mut column = self.token_start_column;
423        if position <= self.token_start {
424            return column;
425        }
426        for ch in self
427            .input
428            .text(TextInterval::new(self.token_start, position - 1))
429            .chars()
430        {
431            if ch == '\n' {
432                column = 0;
433            } else {
434                column += 1;
435            }
436        }
437        column
438    }
439
440    /// Builds the synthetic EOF token at the current input cursor.
441    pub fn eof_token(&self) -> CommonToken {
442        let token = CommonToken::eof(
443            self.input.source_name(),
444            self.input.index(),
445            self.line,
446            self.column,
447        );
448        match self.eof_byte_offset() {
449            Some(byte_offset) => token.with_byte_span(byte_offset, byte_offset),
450            None => token,
451        }
452    }
453
454    fn eof_byte_offset(&self) -> Option<u32> {
455        self.byte_offset_at(self.input.index())
456    }
457
458    fn token_byte_span(&self, stop: usize) -> Option<(u32, u32)> {
459        if stop != usize::MAX && self.token_start <= stop {
460            let (_, start_byte, stop_byte) = self
461                .input
462                .text_source_interval(TextInterval::new(self.token_start, stop))?;
463            return Some((
464                u32::try_from(start_byte).ok()?,
465                u32::try_from(stop_byte).ok()?,
466            ));
467        }
468        let byte_offset = self.byte_offset_at(self.token_start)?;
469        Some((byte_offset, byte_offset))
470    }
471
472    fn byte_offset_at(&self, index: usize) -> Option<u32> {
473        let byte_offset = if index == 0 {
474            0
475        } else {
476            let previous = TextInterval::new(index - 1, index - 1);
477            self.input.text_source_interval(previous)?.2
478        };
479        u32::try_from(byte_offset).ok()
480    }
481}
482
483impl<I, F> Recognizer for BaseLexer<I, F>
484where
485    I: CharStream,
486    F: TokenFactory,
487{
488    fn data(&self) -> &RecognizerData {
489        &self.data
490    }
491
492    fn data_mut(&mut self) -> &mut RecognizerData {
493        &mut self.data
494    }
495}
496
497impl<I, F> Lexer for BaseLexer<I, F>
498where
499    I: CharStream,
500    F: TokenFactory,
501{
502    fn mode(&self) -> i32 {
503        self.mode
504    }
505
506    fn set_mode(&mut self, mode: i32) {
507        self.mode = mode;
508    }
509
510    fn push_mode(&mut self, mode: i32) {
511        self.mode_stack.push(self.mode);
512        self.mode = mode;
513    }
514
515    fn pop_mode(&mut self) -> Option<i32> {
516        let mode = self.mode_stack.pop()?;
517        self.mode = mode;
518        Some(mode)
519    }
520}
521
522impl<I, F> BaseLexer<I, F>
523where
524    I: CharStream,
525    F: TokenFactory,
526{
527    pub const fn line(&self) -> usize {
528        self.line
529    }
530
531    pub const fn column(&self) -> usize {
532        self.column
533    }
534
535    pub fn source_name(&self) -> &str {
536        self.input.source_name()
537    }
538
539    pub const fn hit_eof(&self) -> bool {
540        self.hit_eof
541    }
542
543    pub const fn set_hit_eof(&mut self, hit_eof: bool) {
544        self.hit_eof = hit_eof;
545    }
546
547    /// Buffers a lexer diagnostic until the token stream consumer is ready to
548    /// emit errors in parser-compatible order.
549    pub fn record_error(&mut self, line: usize, column: usize, message: impl Into<String>) {
550        self.errors
551            .push(TokenSourceError::new(line, column, message));
552    }
553
554    /// Returns and clears lexer diagnostics produced while fetching tokens.
555    pub fn drain_errors(&mut self) -> Vec<TokenSourceError> {
556        std::mem::take(&mut self.errors)
557    }
558
559    /// Returns the stable state number for a normalized lexer DFA config set,
560    /// creating one if this input path has not reached it before.
561    pub(crate) fn lexer_dfa_state(
562        &mut self,
563        key: LexerDfaKey,
564        accept_prediction: Option<i32>,
565    ) -> usize {
566        let next = self.lexer_dfa.state_numbers.len();
567        let state = *self.lexer_dfa.state_numbers.entry(key).or_insert(next);
568        if let Some(prediction) = accept_prediction {
569            self.lexer_dfa.accept_predictions.insert(state, prediction);
570        }
571        state
572    }
573
574    /// Records a visible lexer DFA edge unless it was already observed.
575    pub fn record_lexer_dfa_edge(&mut self, from: usize, symbol: i32, to: usize) {
576        self.lexer_dfa
577            .edges
578            .insert(LexerDfaEdge { from, symbol, to });
579    }
580
581    pub(crate) fn cached_lexer_dfa_transition(
582        &self,
583        state: usize,
584        symbol: i32,
585    ) -> Option<LexerDfaCachedTransition> {
586        self.lexer_dfa.transitions.get(&(state, symbol)).cloned()
587    }
588
589    pub(crate) fn cache_lexer_dfa_transition(
590        &mut self,
591        state: usize,
592        symbol: i32,
593        transition: LexerDfaCachedTransition,
594    ) {
595        self.lexer_dfa
596            .transitions
597            .entry((state, symbol))
598            .or_insert(transition);
599    }
600
601    pub(crate) fn cached_lexer_dfa_state(&self, state: usize) -> Option<Rc<LexerDfaCachedState>> {
602        self.lexer_dfa.cached_states.get(&state).cloned()
603    }
604
605    pub(crate) fn cache_lexer_dfa_state(
606        &mut self,
607        state: usize,
608        cached_state: LexerDfaCachedState,
609    ) {
610        self.lexer_dfa
611            .cached_states
612            .entry(state)
613            .or_insert_with(|| Rc::new(cached_state));
614    }
615
616    pub(crate) fn cached_lexer_mode_start(&self, mode: i32) -> Option<usize> {
617        self.lexer_dfa.mode_starts.get(&mode).copied()
618    }
619
620    pub(crate) fn cache_lexer_mode_start(&mut self, mode: i32, state: usize) {
621        self.lexer_dfa.mode_starts.entry(mode).or_insert(state);
622    }
623
624    /// Serializes the observed default-mode lexer DFA in ANTLR's text shape.
625    pub fn lexer_dfa_string(&self) -> String {
626        let mut out = String::new();
627        for edge in &self.lexer_dfa.edges {
628            let Some(label) = lexer_dfa_edge_label(edge.symbol) else {
629                continue;
630            };
631            out.push_str(&self.lexer_dfa_state_string(edge.from));
632            out.push('-');
633            out.push_str(&label);
634            out.push_str("->");
635            out.push_str(&self.lexer_dfa_state_string(edge.to));
636            out.push('\n');
637        }
638        out
639    }
640
641    fn lexer_dfa_state_string(&self, state: usize) -> String {
642        self.lexer_dfa.accept_predictions.get(&state).map_or_else(
643            || format!("s{state}"),
644            |prediction| format!(":s{state}=>{prediction}"),
645        )
646    }
647}
648
649fn lexer_dfa_edge_label(symbol: i32) -> Option<String> {
650    char::from_u32(symbol.cast_unsigned()).map(|ch| format!("'{ch}'"))
651}
652
653#[cfg(test)]
654mod tests {
655    use super::*;
656    use crate::char_stream::InputStream;
657    use crate::recognizer::RecognizerData;
658    use crate::token::{DEFAULT_CHANNEL, Token};
659    use crate::vocabulary::Vocabulary;
660
661    #[test]
662    fn eof_token_uses_utf8_byte_offset_after_non_ascii_input() {
663        let data = RecognizerData::new(
664            "T",
665            Vocabulary::new(
666                std::iter::empty::<Option<&str>>(),
667                std::iter::empty::<Option<&str>>(),
668                std::iter::empty::<Option<&str>>(),
669            ),
670        );
671        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
672        lexer.consume_char();
673
674        let token = lexer.eof_token();
675
676        assert_eq!(token.start(), 1);
677        assert_eq!(token.stop(), 0);
678        assert_eq!(token.text(), Some("<EOF>"));
679        assert_eq!(token.byte_span(), 2..2);
680    }
681
682    #[test]
683    fn eof_rule_token_uses_utf8_byte_offset_after_non_ascii_input() {
684        let data = RecognizerData::new(
685            "T",
686            Vocabulary::new(
687                std::iter::empty::<Option<&str>>(),
688                std::iter::empty::<Option<&str>>(),
689                std::iter::empty::<Option<&str>>(),
690            ),
691        );
692        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
693        lexer.consume_char();
694        lexer.begin_token();
695
696        let token = lexer.emit_with_stop(1, DEFAULT_CHANNEL, 0, Some("<EOF>".to_owned()));
697
698        assert_eq!(token.start(), 1);
699        assert_eq!(token.stop(), 0);
700        assert_eq!(token.text(), Some("<EOF>"));
701        assert_eq!(token.byte_span(), 2..2);
702    }
703
704    #[test]
705    fn emit_implicit_text_uses_utf8_byte_span_for_non_ascii_input() {
706        let data = RecognizerData::new(
707            "T",
708            Vocabulary::new(
709                std::iter::empty::<Option<&str>>(),
710                std::iter::empty::<Option<&str>>(),
711                std::iter::empty::<Option<&str>>(),
712            ),
713        );
714        let mut lexer = BaseLexer::new(InputStream::new("β"), data);
715        lexer.begin_token();
716        lexer.consume_char();
717
718        let token = lexer.emit(1, DEFAULT_CHANNEL, None);
719
720        assert_eq!(token.start(), 0);
721        assert_eq!(token.stop(), 0);
722        assert_eq!(token.text(), Some("β"));
723        assert_eq!(token.byte_span(), 0..2);
724    }
725}