Skip to main content

antlr4_runtime/
parser.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::rc::Rc;
3
4use crate::atn::{Atn, AtnState, AtnStateKind, Transition};
5use crate::errors::AntlrError;
6use crate::int_stream::IntStream;
7use crate::recognizer::{Recognizer, RecognizerData};
8use crate::token::{CommonToken, TOKEN_EOF, Token, TokenSource, TokenSourceError};
9use crate::token_stream::CommonTokenStream;
10use crate::tree::{ErrorNode, ParseTree, ParserRuleContext, RuleNode, TerminalNode};
11use crate::vocabulary::Vocabulary;
12
13/// Upper bound for the recursive metadata recognizer before it treats a path as
14/// non-viable. Long expression-regression descriptors legitimately walk tens
15/// of thousands of ATN edges.
16const RECOGNITION_DEPTH_LIMIT: usize = 100_000;
17
18/// Parser semantic action reached while recognizing one ATN path.
19///
20/// Generated parsers use `source_state` to dispatch back to the grammar action
21/// rendered for that ATN action transition. The token interval is the current
22/// rule's input span at the action site, which covers common target templates
23/// such as `$text`. Rule-init actions do not have an ATN action source state,
24/// so they are marked separately and may carry an ATN state for expected-token
25/// rendering.
26#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
27pub struct ParserAction {
28    source_state: usize,
29    rule_index: usize,
30    start_index: usize,
31    stop_index: Option<usize>,
32    rule_init: bool,
33    expected_state: Option<usize>,
34}
35
36impl ParserAction {
37    /// Creates an action event for a recognized parser path.
38    pub const fn new(
39        source_state: usize,
40        rule_index: usize,
41        start_index: usize,
42        stop_index: Option<usize>,
43    ) -> Self {
44        Self {
45            source_state,
46            rule_index,
47            start_index,
48            stop_index,
49            rule_init: false,
50            expected_state: None,
51        }
52    }
53
54    /// Creates an action event for a rule-level `@init` action.
55    pub const fn new_rule_init(
56        rule_index: usize,
57        start_index: usize,
58        expected_state: Option<usize>,
59    ) -> Self {
60        Self {
61            source_state: usize::MAX,
62            rule_index,
63            start_index,
64            stop_index: None,
65            rule_init: true,
66            expected_state,
67        }
68    }
69
70    /// ATN state that owns the semantic-action transition.
71    pub const fn source_state(&self) -> usize {
72        self.source_state
73    }
74
75    /// Grammar rule index recorded by the serialized ATN action transition.
76    pub const fn rule_index(&self) -> usize {
77        self.rule_index
78    }
79
80    /// Token-stream index where the active rule began.
81    pub const fn start_index(&self) -> usize {
82        self.start_index
83    }
84
85    /// Last token-stream index consumed before the action was reached.
86    pub const fn stop_index(&self) -> Option<usize> {
87        self.stop_index
88    }
89
90    /// Reports whether this event represents a rule-level `@init` action.
91    pub const fn is_rule_init(&self) -> bool {
92        self.rule_init
93    }
94
95    /// ATN state used to compute expected-token display for this action.
96    pub const fn expected_state(&self) -> Option<usize> {
97        self.expected_state
98    }
99}
100
101/// Parser semantic predicate rendered from a supported target template.
102///
103/// The metadata recognizer evaluates these at the token-stream index where the
104/// predicate transition is reached. Unsupported or absent predicate templates
105/// remain unconditional so existing generated parsers keep their previous
106/// behavior unless the generator opts into this table.
107#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
108pub enum ParserPredicate {
109    True,
110    False,
111    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
112    FalseWithMessage {
113        message: &'static str,
114    },
115    /// Target-template test helper that reports predicate evaluation before
116    /// returning the wrapped boolean value.
117    Invoke {
118        value: bool,
119    },
120    LookaheadTextEquals {
121        offset: isize,
122        text: &'static str,
123    },
124    LookaheadNotEquals {
125        offset: isize,
126        token_type: i32,
127    },
128    /// Compares the current rule invocation's integer argument with a literal
129    /// value from a supported `ValEquals("$i", "...")` target template.
130    LocalIntEquals {
131        value: i64,
132    },
133    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
134    /// rule invocation's integer argument.
135    LocalIntLessOrEqual {
136        value: i64,
137    },
138    /// Compares a generated parser integer member modulo a literal value.
139    MemberModuloEquals {
140        member: usize,
141        modulus: i64,
142        value: i64,
143        equals: bool,
144    },
145}
146
147/// Prediction strategy requested by generated parser harnesses.
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum PredictionMode {
150    /// Prefer the clean full-context outcome when alternatives reach the same
151    /// input position.
152    Ll,
153    /// Preserve SLL's first-viable alternative bias at a decision, even when a
154    /// later full-context alternative could avoid recovery.
155    Sll,
156}
157
158/// Integer argument metadata for a generated parser rule invocation.
159///
160/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
161/// the generator records the rule-transition source state and the value that
162/// should be visible to semantic predicates inside the callee.
163#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
164pub struct ParserRuleArg {
165    /// ATN state containing the rule transition that receives this argument.
166    pub source_state: usize,
167    /// Callee rule index for the transition.
168    pub rule_index: usize,
169    /// Literal fallback value to expose in the callee.
170    pub value: i64,
171    /// Whether the callee should inherit the caller's current integer argument.
172    pub inherit_local: bool,
173}
174
175/// Integer member mutation attached to an ATN action transition.
176#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
177pub struct ParserMemberAction {
178    /// ATN state containing the action transition.
179    pub source_state: usize,
180    /// Generator-assigned integer member id.
181    pub member: usize,
182    /// Delta applied when the action is reached on one speculative path.
183    pub delta: i64,
184}
185
186/// Integer return-value assignment attached to an ATN action transition.
187///
188/// Generated parsers use this metadata when target actions assign a simple
189/// return field such as `$y=1000;`. The interpreter applies it while selecting
190/// the recognized path so the finished parse tree can answer later
191/// `$label.y` action templates.
192#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
193pub struct ParserReturnAction {
194    /// ATN state containing the action transition.
195    pub source_state: usize,
196    /// Rule index recorded by the serialized action transition.
197    pub rule_index: usize,
198    /// Return-field name as it appears in the grammar.
199    pub name: &'static str,
200    /// Literal integer value assigned by the action.
201    pub value: i64,
202}
203
204/// Optional generated-runtime metadata for metadata-driven parser execution.
205#[derive(Clone, Copy, Debug, Default)]
206pub struct ParserRuntimeOptions<'a> {
207    /// Rule indexes whose `@init` actions should be replayed.
208    pub init_action_rules: &'a [usize],
209    /// Whether generated parse-tree contexts should retain alternative numbers.
210    pub track_alt_numbers: bool,
211    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
212    pub predicates: &'a [(usize, usize, ParserPredicate)],
213    /// Rule-call integer argument table keyed by ATN source state.
214    pub rule_args: &'a [ParserRuleArg],
215    /// Integer member mutations keyed by ATN action source state.
216    pub member_actions: &'a [ParserMemberAction],
217    /// Integer return assignments keyed by ATN action source state.
218    pub return_actions: &'a [ParserReturnAction],
219}
220
221pub trait Parser: Recognizer {
222    /// Reports whether generated parser rules should build parse-tree nodes
223    /// while recognizing input.
224    fn build_parse_trees(&self) -> bool;
225
226    /// Enables or disables parse-tree construction for subsequent rule calls.
227    fn set_build_parse_trees(&mut self, build: bool);
228
229    /// Reports whether prediction diagnostic-listener messages are emitted
230    /// during parser ATN recognition.
231    fn report_diagnostic_errors(&self) -> bool {
232        false
233    }
234
235    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
236    /// rule calls.
237    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
238
239    /// Reports the prediction strategy used when selecting among alternatives.
240    fn prediction_mode(&self) -> PredictionMode {
241        PredictionMode::Ll
242    }
243
244    /// Sets the prediction strategy for subsequent rule calls.
245    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
246}
247
248#[derive(Debug)]
249pub struct BaseParser<S> {
250    input: CommonTokenStream<S>,
251    data: RecognizerData,
252    build_parse_trees: bool,
253    report_diagnostic_errors: bool,
254    prediction_mode: PredictionMode,
255    prediction_diagnostics: Vec<ParserDiagnostic>,
256    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
257    int_members: BTreeMap<usize, i64>,
258    /// Predicate side effects are observable in a few target-template tests;
259    /// speculative recognition may revisit the same coordinate, so replay it
260    /// once per parser instance.
261    invoked_predicates: Vec<(usize, usize)>,
262    /// FIRST-set cache shared across all speculative rule-call lookups in a
263    /// single parser instance. The fast recognizer consults FIRST sets on
264    /// every rule transition; without caching the same DFS would repeat for
265    /// every speculative path that visits the same rule.
266    first_set_cache: FirstSetCache,
267    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
268    /// prefilter trims speculative rule calls whose called rule cannot
269    /// match the current lookahead, but it also bypasses single-token
270    /// insertion / deletion recovery that ANTLR runs at the rule's first
271    /// consuming transition. `parse_atn_rule` flips this off and retries
272    /// when the first pass produces no clean outcome so the runtime can
273    /// repair inputs the reference parser would have repaired.
274    fast_first_set_prefilter: bool,
275}
276
277#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
278struct RecognizeOutcome {
279    index: usize,
280    consumed_eof: bool,
281    alt_number: usize,
282    member_values: BTreeMap<usize, i64>,
283    return_values: BTreeMap<String, i64>,
284    diagnostics: Vec<ParserDiagnostic>,
285    decisions: Vec<usize>,
286    actions: Vec<ParserAction>,
287    nodes: Vec<RecognizedNode>,
288}
289
290#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
291enum RecognizedNode {
292    Token {
293        index: usize,
294    },
295    ErrorToken {
296        index: usize,
297    },
298    MissingToken {
299        token_type: i32,
300        at_index: usize,
301        text: String,
302    },
303    Rule {
304        rule_index: usize,
305        invoking_state: isize,
306        alt_number: usize,
307        start_index: usize,
308        stop_index: Option<usize>,
309        return_values: BTreeMap<String, i64>,
310        children: Vec<Self>,
311    },
312    LeftRecursiveBoundary {
313        rule_index: usize,
314    },
315}
316
317#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
318struct FastRecognizeOutcome {
319    index: usize,
320    consumed_eof: bool,
321    diagnostics: Vec<ParserDiagnostic>,
322    /// Speculative parse-tree fragment built up as the recognizer climbs.
323    /// The list is held as a persistent cons-list of `Rc`-wrapped nodes so
324    /// prepending while chaining recognition outcomes is `O(1)` and cloning
325    /// an outcome (memo lookup, dedup, or when fanning a child's tree out
326    /// to every follow outcome) only bumps a reference count rather than
327    /// deep-copying. On left-recursive grammars the unfolded list can carry
328    /// thousands of nodes per speculative path; without the persistent-list
329    /// shape recognition becomes super-linear in path length.
330    nodes: NodeList,
331}
332
333/// Persistent cons-list of fast-recognizer nodes. The list keeps nodes in the
334/// same head-first order as the original `Vec<FastRecognizedNode>` they
335/// replaced. Shared tails across speculative outcomes amortize the cost of
336/// chaining a child rule's nodes onto every follow outcome.
337#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
338enum NodeList {
339    #[default]
340    Empty,
341    Cons {
342        head: Rc<FastRecognizedNode>,
343        tail: Rc<Self>,
344    },
345}
346
347impl NodeList {
348    /// Creates an empty list.
349    const fn new() -> Self {
350        Self::Empty
351    }
352
353    /// Prepends `node` and returns the new list. Both shared tails and the
354    /// new head are reference-counted so this is `O(1)`.
355    fn cons(self, node: Rc<FastRecognizedNode>) -> Self {
356        Self::Cons {
357            head: node,
358            tail: Rc::new(self),
359        }
360    }
361
362    /// In-place prepend that takes ownership of `self` via [`std::mem::take`]
363    /// so existing call sites can keep using `&mut` access.
364    fn prepend(&mut self, node: Rc<FastRecognizedNode>) {
365        let owned = std::mem::take(self);
366        *self = owned.cons(node);
367    }
368
369    /// Materializes the list into a `Vec` in head-first order. Used at the
370    /// boundaries that need random-access traversal (the public rule entry
371    /// when building the final parse tree, and
372    /// `fold_fast_left_recursive_boundaries`).
373    fn to_vec(&self) -> Vec<Rc<FastRecognizedNode>> {
374        let mut out = Vec::new();
375        let mut cursor = self;
376        while let Self::Cons { head, tail } = cursor {
377            out.push(Rc::clone(head));
378            cursor = tail.as_ref();
379        }
380        out
381    }
382}
383
384/// Minimal parse-tree fragment retained by the fast recognizer so the public
385/// rule entry can build nested rule contexts without paying for
386/// action/decision bookkeeping.
387#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
388enum FastRecognizedNode {
389    Token {
390        index: usize,
391    },
392    ErrorToken {
393        index: usize,
394    },
395    MissingToken {
396        token_type: i32,
397        at_index: usize,
398        text: String,
399    },
400    Rule {
401        rule_index: usize,
402        invoking_state: isize,
403        start_index: usize,
404        stop_index: Option<usize>,
405        children: Vec<Rc<Self>>,
406    },
407    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
408    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
409    /// public rule entry hands the tree to the caller.
410    LeftRecursiveBoundary {
411        rule_index: usize,
412    },
413}
414
415#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
416struct ParserDiagnostic {
417    line: usize,
418    column: usize,
419    message: String,
420}
421
422#[derive(Clone, Debug, Default, Eq, PartialEq)]
423struct ExpectedTokens {
424    index: Option<usize>,
425    symbols: BTreeSet<i32>,
426    no_viable: Option<NoViableAlternative>,
427}
428
429#[derive(Clone, Copy, Debug, Eq, PartialEq)]
430struct NoViableAlternative {
431    start_index: usize,
432    error_index: usize,
433}
434
435impl ExpectedTokens {
436    /// Records the expected symbols for the farthest token index reached by any
437    /// failed ATN path.
438    fn record_transition(&mut self, index: usize, transition: &Transition, max_token_type: i32) {
439        let symbols = transition_expected_symbols(transition, max_token_type);
440        match self.index {
441            Some(current) if index < current => {}
442            Some(current) if index == current => self.symbols.extend(symbols),
443            _ => {
444                self.index = Some(index);
445                self.symbols = symbols;
446            }
447        }
448    }
449
450    /// Records an ambiguous decision that failed after consuming a shared
451    /// prefix, which ANTLR reports as `no viable alternative`.
452    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
453        match self.no_viable {
454            Some(current) if error_index < current.error_index => {}
455            _ => {
456                self.no_viable = Some(NoViableAlternative {
457                    start_index,
458                    error_index,
459                });
460            }
461        }
462    }
463}
464
465/// Converts one consuming transition into the token types that would satisfy it
466/// for diagnostic reporting.
467fn transition_expected_symbols(transition: &Transition, max_token_type: i32) -> BTreeSet<i32> {
468    let mut symbols = BTreeSet::new();
469    match transition {
470        Transition::Atom { label, .. } => {
471            symbols.insert(*label);
472        }
473        Transition::Range { start, stop, .. } => {
474            symbols.extend(*start..=*stop);
475        }
476        Transition::Set { set, .. } => {
477            for (start, stop) in set.ranges() {
478                symbols.extend(*start..=*stop);
479            }
480        }
481        Transition::NotSet { set, .. } => {
482            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
483        }
484        Transition::Wildcard { .. } => {
485            symbols.extend(1..=max_token_type);
486        }
487        Transition::Epsilon { .. }
488        | Transition::Rule { .. }
489        | Transition::Predicate { .. }
490        | Transition::Action { .. }
491        | Transition::Precedence { .. } => {}
492    }
493    symbols
494}
495
496/// Returns the consuming-token expectations reachable from an ATN state through
497/// epsilon transitions. Recovery diagnostics need this closure so alternatives
498/// and loop exits report the same expectation set ANTLR users see.
499fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
500    let mut symbols = BTreeSet::new();
501    let mut stack = vec![state_number];
502    let mut visited = BTreeSet::new();
503    while let Some(current) = stack.pop() {
504        if !visited.insert(current) {
505            continue;
506        }
507        let Some(state) = atn.state(current) else {
508            continue;
509        };
510        for transition in &state.transitions {
511            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
512            if transition_symbols.is_empty() {
513                if transition.is_epsilon() {
514                    stack.push(transition.target());
515                }
516            } else {
517                symbols.extend(transition_symbols);
518            }
519        }
520    }
521    symbols
522}
523
524/// FIRST set for a rule entry plus whether the rule is nullable.
525///
526/// Walks epsilon, predicate, action, and rule-call transitions until it finds
527/// a consuming transition or reaches the rule's stop state. Used by the fast
528/// recognizer to skip rule alternatives whose first-consumed token cannot
529/// possibly match the current lookahead.
530#[derive(Clone, Debug, Default, Eq, PartialEq)]
531struct FirstSet {
532    symbols: BTreeSet<i32>,
533    nullable: bool,
534}
535
536/// Per-parser cache of FIRST sets computed during recognition. The fast path
537/// consults this on every speculative `Transition::Rule` encounter, so the
538/// computation must amortize across all of those calls — the FIRST set is a
539/// pure function of the ATN, not of the input position.
540type FirstSetCache = BTreeMap<(usize, usize), FirstSet>;
541
542/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
543/// rarely-touched fields keeps the recursive helpers below the function-arity
544/// lint and lets every nested call thread the same cache and cycle guards.
545struct FirstSetCtx<'a> {
546    cache: &'a mut FirstSetCache,
547    in_progress: BTreeSet<(usize, usize)>,
548    hit_cycle: bool,
549}
550
551/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
552/// shared cache and tolerating recursive nullable rule chains. Mutually
553/// recursive rules cannot stack-overflow because callers in flight are tracked
554/// in `ctx.in_progress`; revisits return without recursing, and the partial
555/// result is cached only when no cycle was detected during its computation.
556fn rule_first_set(
557    atn: &Atn,
558    target: usize,
559    rule_stop_state: usize,
560    cache: &mut FirstSetCache,
561) -> FirstSet {
562    let mut ctx = FirstSetCtx {
563        cache,
564        in_progress: BTreeSet::new(),
565        hit_cycle: false,
566    };
567    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
568}
569
570fn rule_first_set_cached(
571    atn: &Atn,
572    target: usize,
573    rule_stop_state: usize,
574    ctx: &mut FirstSetCtx<'_>,
575) -> FirstSet {
576    let key = (target, rule_stop_state);
577    if let Some(cached) = ctx.cache.get(&key) {
578        return cached.clone();
579    }
580    if !ctx.in_progress.insert(key) {
581        // Cycle: a caller above is already computing this entry. Return an
582        // empty FIRST set; that caller's traversal supplies the contributions
583        // from the rule's other alternatives.
584        return FirstSet::default();
585    }
586    let saved_hit_cycle = ctx.hit_cycle;
587    ctx.hit_cycle = false;
588    let mut first = FirstSet::default();
589    let mut visited = BTreeSet::new();
590    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
591    ctx.in_progress.remove(&key);
592    if !ctx.hit_cycle {
593        ctx.cache.insert(key, first.clone());
594    }
595    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
596    first
597}
598
599fn rule_first_set_inner(
600    atn: &Atn,
601    state_number: usize,
602    rule_stop_state: usize,
603    ctx: &mut FirstSetCtx<'_>,
604    visited: &mut BTreeSet<usize>,
605    first: &mut FirstSet,
606) {
607    if !visited.insert(state_number) {
608        return;
609    }
610    if state_number == rule_stop_state {
611        first.nullable = true;
612        return;
613    }
614    let Some(state) = atn.state(state_number) else {
615        return;
616    };
617    for transition in &state.transitions {
618        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
619        if !transition_symbols.is_empty() {
620            first.symbols.extend(transition_symbols);
621            continue;
622        }
623        match transition {
624            Transition::Epsilon { target }
625            | Transition::Action { target, .. }
626            | Transition::Predicate { target, .. }
627            | Transition::Precedence { target, .. } => {
628                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
629            }
630            Transition::Rule {
631                target,
632                rule_index,
633                follow_state,
634                ..
635            } => {
636                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
637                    continue;
638                };
639                let child_key = (*target, child_stop);
640                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
641                    ctx.hit_cycle = true;
642                }
643                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
644                first.symbols.extend(child.symbols);
645                if child.nullable {
646                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
647                }
648            }
649            Transition::Atom { .. }
650            | Transition::Range { .. }
651            | Transition::Set { .. }
652            | Transition::NotSet { .. }
653            | Transition::Wildcard { .. } => {}
654        }
655    }
656}
657
658/// Returns token types that can resume parsing from `state_number` after a
659/// failed child rule, following rule calls as well as epsilon transitions.
660fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
661    let mut symbols = BTreeSet::new();
662    state_sync_symbols_inner(
663        atn,
664        state_number,
665        stop_state,
666        &mut BTreeSet::new(),
667        &mut symbols,
668    );
669    symbols
670}
671
672/// Walks epsilon-like continuations from a parent follow state until it finds
673/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
674fn state_sync_symbols_inner(
675    atn: &Atn,
676    state_number: usize,
677    stop_state: usize,
678    visited: &mut BTreeSet<usize>,
679    symbols: &mut BTreeSet<i32>,
680) {
681    if !visited.insert(state_number) {
682        return;
683    }
684    if state_number == stop_state {
685        symbols.insert(TOKEN_EOF);
686        return;
687    }
688    let Some(state) = atn.state(state_number) else {
689        return;
690    };
691    for transition in &state.transitions {
692        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
693        if transition_symbols.is_empty() {
694            match transition {
695                Transition::Rule { target, .. }
696                | Transition::Epsilon { target }
697                | Transition::Action { target, .. }
698                | Transition::Predicate { target, .. }
699                | Transition::Precedence { target, .. } => {
700                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
701                }
702                Transition::Atom { .. }
703                | Transition::Range { .. }
704                | Transition::Set { .. }
705                | Transition::NotSet { .. }
706                | Transition::Wildcard { .. } => {}
707            }
708        } else {
709            symbols.extend(transition_symbols);
710        }
711    }
712}
713
714/// Carries recovery expectations and their restart state through epsilon-only
715/// paths. ANTLR can report and repair at the decision state even when the
716/// failed consuming transition is nested under block or loop epsilon edges.
717fn next_recovery_context(
718    atn: &Atn,
719    state: &AtnState,
720    inherited: &BTreeSet<i32>,
721    inherited_state: Option<usize>,
722) -> (BTreeSet<i32>, Option<usize>) {
723    let state_symbols = state_expected_symbols(atn, state.state_number);
724    if state.transitions.len() > 1 && !state_symbols.is_empty() {
725        let mut symbols = state_symbols;
726        symbols.extend(inherited.iter().copied());
727        return (symbols, Some(state.state_number));
728    }
729    (inherited.clone(), inherited_state)
730}
731
732fn recovery_expected_symbols(
733    atn: &Atn,
734    state_number: usize,
735    inherited: &BTreeSet<i32>,
736) -> BTreeSet<i32> {
737    let mut symbols = state_expected_symbols(atn, state_number);
738    symbols.extend(inherited.iter().copied());
739    symbols
740}
741
742/// Applies generated integer-member side effects to one speculative path.
743fn apply_member_actions(
744    source_state: usize,
745    actions: &[ParserMemberAction],
746    values: &mut BTreeMap<usize, i64>,
747) {
748    for action in actions
749        .iter()
750        .filter(|action| action.source_state == source_state)
751    {
752        *values.entry(action.member).or_default() += action.delta;
753    }
754}
755
756/// Returns the speculative member state after replaying one ATN action state.
757fn member_values_after_action(
758    source_state: usize,
759    actions: &[ParserMemberAction],
760    values: &BTreeMap<usize, i64>,
761) -> BTreeMap<usize, i64> {
762    let mut values = values.clone();
763    apply_member_actions(source_state, actions, &mut values);
764    values
765}
766
767/// Returns the speculative rule-return state after replaying one ATN action.
768fn return_values_after_action(
769    source_state: usize,
770    rule_index: usize,
771    actions: &[ParserReturnAction],
772    values: &BTreeMap<String, i64>,
773) -> BTreeMap<String, i64> {
774    let mut values = values.clone();
775    for action in actions
776        .iter()
777        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
778    {
779        values.insert(action.name.to_owned(), action.value);
780    }
781    values
782}
783
784/// Resolves the integer argument visible to a child rule invocation.
785fn rule_local_int_arg(
786    rule_args: &[ParserRuleArg],
787    source_state: usize,
788    rule_index: usize,
789    local_int_arg: Option<(usize, i64)>,
790) -> Option<(usize, i64)> {
791    rule_args
792        .iter()
793        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
794        .map(|arg| {
795            let value = if arg.inherit_local {
796                local_int_arg.map_or(arg.value, |(_, value)| value)
797            } else {
798                arg.value
799            };
800            (rule_index, value)
801        })
802}
803
804/// Builds the terminal recognition outcome for a path that reached its stop
805/// state.
806fn stop_outcome(
807    index: usize,
808    consumed_eof: bool,
809    rule_alt_number: usize,
810    member_values: BTreeMap<usize, i64>,
811    return_values: BTreeMap<String, i64>,
812) -> Vec<RecognizeOutcome> {
813    vec![RecognizeOutcome {
814        index,
815        consumed_eof,
816        alt_number: rule_alt_number,
817        member_values,
818        return_values,
819        diagnostics: Vec::new(),
820        decisions: Vec::new(),
821        actions: Vec::new(),
822        nodes: Vec::new(),
823    }]
824}
825
826#[derive(Clone, Debug, Eq, PartialEq)]
827struct RecognizeRequest<'a> {
828    state_number: usize,
829    stop_state: usize,
830    index: usize,
831    rule_start_index: usize,
832    decision_start_index: Option<usize>,
833    init_action_rules: &'a BTreeSet<usize>,
834    predicates: &'a [(usize, usize, ParserPredicate)],
835    rule_args: &'a [ParserRuleArg],
836    member_actions: &'a [ParserMemberAction],
837    return_actions: &'a [ParserReturnAction],
838    local_int_arg: Option<(usize, i64)>,
839    member_values: BTreeMap<usize, i64>,
840    return_values: BTreeMap<String, i64>,
841    rule_alt_number: usize,
842    track_alt_numbers: bool,
843    consumed_eof: bool,
844    /// Current left-recursive precedence threshold, matching ANTLR's
845    /// `precpred(_ctx, k)` check for generated precedence rules.
846    precedence: i32,
847    depth: usize,
848    recovery_symbols: BTreeSet<i32>,
849    recovery_state: Option<usize>,
850}
851
852#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
853struct RecognizeKey {
854    state_number: usize,
855    stop_state: usize,
856    index: usize,
857    rule_start_index: usize,
858    decision_start_index: Option<usize>,
859    local_int_arg: Option<(usize, i64)>,
860    member_values: BTreeMap<usize, i64>,
861    return_values: BTreeMap<String, i64>,
862    rule_alt_number: usize,
863    track_alt_numbers: bool,
864    consumed_eof: bool,
865    precedence: i32,
866    recovery_symbols: BTreeSet<i32>,
867    recovery_state: Option<usize>,
868}
869
870#[derive(Clone, Debug, Eq, PartialEq)]
871struct EpsilonActionStep {
872    source_state: usize,
873    target: usize,
874    action_rule_index: Option<usize>,
875    left_recursive_boundary: Option<usize>,
876    decision: Option<usize>,
877    decision_start_index: Option<usize>,
878    alt_number: usize,
879    recovery_symbols: BTreeSet<i32>,
880    recovery_state: Option<usize>,
881}
882
883struct RecognizeScratch<'a> {
884    visiting: &'a mut BTreeSet<RecognizeKey>,
885    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
886    expected: &'a mut ExpectedTokens,
887}
888
889#[derive(Clone, Debug, Eq, PartialEq)]
890struct FastRecognizeRequest {
891    state_number: usize,
892    stop_state: usize,
893    index: usize,
894    rule_start_index: usize,
895    decision_start_index: Option<usize>,
896    precedence: i32,
897    depth: usize,
898    recovery_symbols: BTreeSet<i32>,
899    recovery_state: Option<usize>,
900}
901
902#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
903struct FastRecognizeKey {
904    state_number: usize,
905    stop_state: usize,
906    index: usize,
907    rule_start_index: usize,
908    decision_start_index: Option<usize>,
909    precedence: i32,
910    recovery_symbols: BTreeSet<i32>,
911    recovery_state: Option<usize>,
912}
913
914struct FastRecoveryRequest<'a, 'b> {
915    atn: &'a Atn,
916    transition: &'a Transition,
917    expected_symbols: BTreeSet<i32>,
918    target: usize,
919    request: FastRecognizeRequest,
920    visiting: &'b mut BTreeSet<FastRecognizeKey>,
921    memo: &'b mut BTreeMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
922    expected: &'b mut ExpectedTokens,
923}
924
925struct FastCurrentTokenDeletionRequest<'a, 'b> {
926    atn: &'a Atn,
927    expected_symbols: BTreeSet<i32>,
928    request: FastRecognizeRequest,
929    visiting: &'b mut BTreeSet<FastRecognizeKey>,
930    memo: &'b mut BTreeMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
931    expected: &'b mut ExpectedTokens,
932}
933
934struct RecoveryRequest<'a, 'b> {
935    atn: &'a Atn,
936    transition: &'a Transition,
937    expected_symbols: BTreeSet<i32>,
938    target: usize,
939    request: RecognizeRequest<'a>,
940    visiting: &'b mut BTreeSet<RecognizeKey>,
941    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
942    expected: &'b mut ExpectedTokens,
943}
944
945struct CurrentTokenDeletionRequest<'a, 'b> {
946    atn: &'a Atn,
947    expected_symbols: BTreeSet<i32>,
948    request: RecognizeRequest<'a>,
949    visiting: &'b mut BTreeSet<RecognizeKey>,
950    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
951    expected: &'b mut ExpectedTokens,
952}
953
954/// Carries the state needed after the normal token-recovery strategies fail
955/// for a consuming transition.
956struct ConsumingFailureFallback<'a> {
957    atn: &'a Atn,
958    target: usize,
959    request: RecognizeRequest<'a>,
960    symbol: i32,
961    expected_symbols: BTreeSet<i32>,
962    decision_start_index: Option<usize>,
963    decision: Option<usize>,
964}
965
966/// Captures the parent-rule context needed when a called rule fails before it
967/// can produce a normal outcome.
968struct ChildRuleFailureRecovery<'a> {
969    atn: &'a Atn,
970    rule_index: usize,
971    start_index: usize,
972    follow_state: usize,
973    stop_state: usize,
974    member_values: BTreeMap<usize, i64>,
975    expected: &'a ExpectedTokens,
976}
977
978/// Bundles the context needed to evaluate one semantic predicate transition.
979#[derive(Clone, Copy, Debug)]
980struct PredicateEval<'a> {
981    index: usize,
982    rule_index: usize,
983    pred_index: usize,
984    predicates: &'a [(usize, usize, ParserPredicate)],
985    local_int_arg: Option<(usize, i64)>,
986    member_values: &'a BTreeMap<usize, i64>,
987}
988
989/// Captures predicate-failure recovery metadata for fail-option predicates.
990struct PredicateFailureRecovery<'a> {
991    rule_index: usize,
992    index: usize,
993    message: &'a str,
994    member_values: BTreeMap<usize, i64>,
995    return_values: BTreeMap<String, i64>,
996    rule_alt_number: usize,
997}
998
999impl<S> BaseParser<S>
1000where
1001    S: TokenSource,
1002{
1003    /// Creates a parser base over a buffered token stream and recognizer
1004    /// metadata.
1005    pub const fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
1006        Self {
1007            input,
1008            data,
1009            build_parse_trees: true,
1010            report_diagnostic_errors: false,
1011            prediction_mode: PredictionMode::Ll,
1012            prediction_diagnostics: Vec::new(),
1013            reported_prediction_diagnostics: BTreeSet::new(),
1014            int_members: BTreeMap::new(),
1015            invoked_predicates: Vec::new(),
1016            first_set_cache: BTreeMap::new(),
1017            fast_first_set_prefilter: true,
1018        }
1019    }
1020
1021    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
1022        &mut self.input
1023    }
1024
1025    pub fn la(&mut self, offset: isize) -> i32 {
1026        self.input.la_token(offset)
1027    }
1028
1029    pub fn consume(&mut self) {
1030        IntStream::consume(&mut self.input);
1031    }
1032
1033    /// Sets a generated integer member value used by target-template tests.
1034    pub fn set_int_member(&mut self, member: usize, value: i64) {
1035        self.int_members.insert(member, value);
1036    }
1037
1038    /// Reads a generated integer member value.
1039    pub fn int_member(&self, member: usize) -> Option<i64> {
1040        self.int_members.get(&member).copied()
1041    }
1042
1043    /// Adds `delta` to a generated integer member and returns the new value.
1044    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
1045        let value = self.int_members.entry(member).or_default();
1046        *value += delta;
1047        *value
1048    }
1049
1050    /// Matches and consumes the current token when it has the expected token
1051    /// type.
1052    ///
1053    /// On success the consumed token is wrapped as a terminal parse-tree node.
1054    /// On mismatch the error carries vocabulary display names so diagnostics are
1055    /// stable across literal and symbolic token naming.
1056    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
1057        let current = self
1058            .input
1059            .lt(1)
1060            .cloned()
1061            .ok_or_else(|| AntlrError::ParserError {
1062                line: 0,
1063                column: 0,
1064                message: "missing current token".to_owned(),
1065            })?;
1066        if current.token_type() == token_type {
1067            self.consume();
1068            Ok(ParseTree::Terminal(TerminalNode::new(current)))
1069        } else {
1070            Err(AntlrError::MismatchedInput {
1071                expected: self.vocabulary().display_name(token_type),
1072                found: self.vocabulary().display_name(current.token_type()),
1073            })
1074        }
1075    }
1076
1077    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
1078        self.match_token(TOKEN_EOF)
1079    }
1080
1081    pub const fn rule_node(&self, context: ParserRuleContext) -> ParseTree {
1082        ParseTree::Rule(RuleNode::new(context))
1083    }
1084
1085    /// Parses a generated rule by interpreting the parser ATN from the rule's
1086    /// start state to its stop state.
1087    ///
1088    /// The recognizer backtracks across alternatives and loop exits using token
1089    /// stream indices instead of committing to input consumption immediately.
1090    /// Once a viable ATN path is found, the parser commits the accepted token
1091    /// interval and returns a rule node whose children mirror every grammar
1092    /// rule invocation reached on that path, matching ANTLR's parse-tree
1093    /// shape.
1094    pub fn parse_atn_rule(
1095        &mut self,
1096        atn: &Atn,
1097        rule_index: usize,
1098    ) -> Result<ParseTree, AntlrError> {
1099        let start_state = atn
1100            .rule_to_start_state()
1101            .get(rule_index)
1102            .copied()
1103            .ok_or_else(|| {
1104                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
1105            })?;
1106        let stop_state = atn
1107            .rule_to_stop_state()
1108            .get(rule_index)
1109            .copied()
1110            .filter(|state| *state != usize::MAX)
1111            .ok_or_else(|| {
1112                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
1113            })?;
1114
1115        let start_index = self.current_visible_index();
1116        self.clear_prediction_diagnostics();
1117        let first_pass = self.fast_recognize_top(atn, start_state, stop_state, start_index);
1118        let needs_retry = match &first_pass {
1119            // The FIRST-set prefilter trims speculative rule calls that can't
1120            // match the current lookahead — useful for perf on grammars with
1121            // many epsilon-reachable rules, but the trim also bypasses
1122            // single-token insertion / deletion recovery that ANTLR's
1123            // reference parser runs at the child rule's first consuming
1124            // transition. Retry without the prefilter whenever the first pass
1125            // either produced no outcome at all or produced a recovered
1126            // outcome (diagnostics non-empty), since the second pass might
1127            // surface a child-level recovery with cleaner diagnostics or
1128            // closer parity to ANTLR's tree shape.
1129            Err(_) => true,
1130            Ok((outcome, _)) => !outcome.diagnostics.is_empty(),
1131        };
1132        let (outcome, _expected) = if needs_retry {
1133            self.fast_first_set_prefilter = false;
1134            let retry = self.fast_recognize_top(atn, start_state, stop_state, start_index);
1135            self.fast_first_set_prefilter = true;
1136            select_better_top_outcome(first_pass, retry).map_err(|expected| {
1137                let error = self.recognition_error(rule_index, start_index, &expected);
1138                report_token_source_errors(&self.input.drain_source_errors());
1139                error
1140            })?
1141        } else {
1142            first_pass.expect("first_pass is Ok in the no-retry branch")
1143        };
1144
1145        report_parser_diagnostics(&self.prediction_diagnostics);
1146        report_parser_diagnostics(&outcome.diagnostics);
1147        report_token_source_errors(&self.input.drain_source_errors());
1148        let mut context = ParserRuleContext::new(rule_index, self.state());
1149        if let Some(token) = self.token_at(start_index) {
1150            context.set_start(token);
1151        }
1152        if let Some(token) = self.rule_stop_token(outcome.index, outcome.consumed_eof) {
1153            context.set_stop(token);
1154        }
1155        if self.build_parse_trees {
1156            let folded = fold_fast_left_recursive_boundaries(outcome.nodes.to_vec());
1157            for node in &folded {
1158                context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
1159            }
1160        }
1161        self.input.seek(outcome.index);
1162
1163        Ok(self.rule_node(context))
1164    }
1165
1166    /// Runs the fast recognizer once from the rule's start state and returns
1167    /// the best outcome or the per-attempt expected-token accumulator. The
1168    /// caller flips `fast_first_set_prefilter` between calls when a retry is
1169    /// needed, so the FIRST-set cache is left intact across both passes.
1170    fn fast_recognize_top(
1171        &mut self,
1172        atn: &Atn,
1173        start_state: usize,
1174        stop_state: usize,
1175        start_index: usize,
1176    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
1177        let mut visiting = BTreeSet::new();
1178        let mut memo = BTreeMap::new();
1179        let mut expected = ExpectedTokens::default();
1180        let outcomes = self.recognize_state_fast(
1181            atn,
1182            FastRecognizeRequest {
1183                state_number: start_state,
1184                stop_state,
1185                index: start_index,
1186                rule_start_index: start_index,
1187                decision_start_index: None,
1188                precedence: 0,
1189                depth: 0,
1190                recovery_symbols: BTreeSet::new(),
1191                recovery_state: None,
1192            },
1193            &mut visiting,
1194            &mut memo,
1195            &mut expected,
1196        );
1197        match select_best_fast_outcome(outcomes.into_iter(), self.prediction_mode) {
1198            Some(outcome) => Ok((outcome, expected)),
1199            None => Err(expected),
1200        }
1201    }
1202
1203    /// Converts a recognized fast-recognizer node into a public parse-tree
1204    /// node, mirroring [`Self::recognized_node_tree`] for the slow path.
1205    fn fast_recognized_node_tree(
1206        &mut self,
1207        node: &FastRecognizedNode,
1208    ) -> Result<ParseTree, AntlrError> {
1209        match node {
1210            FastRecognizedNode::Token { index } => {
1211                let token =
1212                    self.input
1213                        .get(*index)
1214                        .cloned()
1215                        .ok_or_else(|| AntlrError::ParserError {
1216                            line: 0,
1217                            column: 0,
1218                            message: format!("missing token at index {index}"),
1219                        })?;
1220                Ok(ParseTree::Terminal(TerminalNode::new(token)))
1221            }
1222            FastRecognizedNode::ErrorToken { index } => {
1223                let token =
1224                    self.input
1225                        .get(*index)
1226                        .cloned()
1227                        .ok_or_else(|| AntlrError::ParserError {
1228                            line: 0,
1229                            column: 0,
1230                            message: format!("missing error token at index {index}"),
1231                        })?;
1232                Ok(ParseTree::Error(ErrorNode::new(token)))
1233            }
1234            FastRecognizedNode::MissingToken {
1235                token_type,
1236                at_index,
1237                text,
1238            } => {
1239                let current = self.token_at(*at_index);
1240                let token = CommonToken::new(*token_type)
1241                    .with_text(text)
1242                    .with_span(usize::MAX, usize::MAX)
1243                    .with_position(
1244                        current.as_ref().map(Token::line).unwrap_or_default(),
1245                        current.as_ref().map(Token::column).unwrap_or_default(),
1246                    );
1247                Ok(ParseTree::Error(ErrorNode::new(token)))
1248            }
1249            FastRecognizedNode::Rule {
1250                rule_index,
1251                invoking_state,
1252                start_index,
1253                stop_index,
1254                children,
1255            } => {
1256                let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
1257                if let Some(token) = self.token_at(*start_index) {
1258                    context.set_start(token);
1259                }
1260                if let Some(token) = stop_index.and_then(|index| self.token_at(index)) {
1261                    context.set_stop(token);
1262                }
1263                for child in children {
1264                    context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
1265                }
1266                Ok(self.rule_node(context))
1267            }
1268            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
1269                format!("unfolded left-recursive boundary for rule {rule_index}"),
1270            )),
1271        }
1272    }
1273
1274    /// Parses a generated rule and returns semantic actions reached on the
1275    /// selected ATN path.
1276    ///
1277    /// This slower path preserves action ordering and token intervals for
1278    /// generated code that replays target-specific action templates after the
1279    /// recognizer has chosen one viable parse path.
1280    pub fn parse_atn_rule_with_actions(
1281        &mut self,
1282        atn: &Atn,
1283        rule_index: usize,
1284    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
1285        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
1286    }
1287
1288    /// Parses a generated rule and emits ATN actions plus selected rule-init
1289    /// actions reached on the chosen path.
1290    ///
1291    /// Generated parsers use this when a grammar contains rule-level `@init`
1292    /// templates that must run for nested rule invocations. The runtime keeps
1293    /// the action list path-sensitive, so init templates are replayed only for
1294    /// rules that were actually entered by the selected parse.
1295    pub fn parse_atn_rule_with_action_inits(
1296        &mut self,
1297        atn: &Atn,
1298        rule_index: usize,
1299        init_action_rules: &[usize],
1300    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
1301        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
1302    }
1303
1304    /// Parses a generated rule with optional semantic-action replay features.
1305    ///
1306    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
1307    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
1308    /// unchanged for grammars that do not request that target template.
1309    pub fn parse_atn_rule_with_action_options(
1310        &mut self,
1311        atn: &Atn,
1312        rule_index: usize,
1313        init_action_rules: &[usize],
1314        track_alt_numbers: bool,
1315    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
1316        self.parse_atn_rule_with_runtime_options(
1317            atn,
1318            rule_index,
1319            ParserRuntimeOptions {
1320                init_action_rules,
1321                track_alt_numbers,
1322                ..ParserRuntimeOptions::default()
1323            },
1324        )
1325    }
1326
1327    /// Parses a generated rule with action replay and parser predicate support.
1328    ///
1329    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
1330    /// target-template predicate semantics emitted by the generator. Missing
1331    /// entries are treated as true so unsupported predicate-free grammars keep
1332    /// the previous unconditional transition behavior.
1333    pub fn parse_atn_rule_with_runtime_options(
1334        &mut self,
1335        atn: &Atn,
1336        rule_index: usize,
1337        options: ParserRuntimeOptions<'_>,
1338    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
1339        let ParserRuntimeOptions {
1340            init_action_rules,
1341            track_alt_numbers,
1342            predicates,
1343            rule_args,
1344            member_actions,
1345            return_actions,
1346        } = options;
1347        let start_state = atn
1348            .rule_to_start_state()
1349            .get(rule_index)
1350            .copied()
1351            .ok_or_else(|| {
1352                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
1353            })?;
1354        let stop_state = atn
1355            .rule_to_stop_state()
1356            .get(rule_index)
1357            .copied()
1358            .filter(|state| *state != usize::MAX)
1359            .ok_or_else(|| {
1360                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
1361            })?;
1362
1363        let start_index = self.current_visible_index();
1364        self.clear_prediction_diagnostics();
1365        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
1366        let mut visiting = BTreeSet::new();
1367        let mut memo = BTreeMap::new();
1368        let mut expected = ExpectedTokens::default();
1369        let member_values = self.int_members.clone();
1370        let return_values = BTreeMap::new();
1371        let outcomes = self.recognize_state(
1372            atn,
1373            RecognizeRequest {
1374                state_number: start_state,
1375                stop_state,
1376                index: start_index,
1377                rule_start_index: start_index,
1378                decision_start_index: None,
1379                init_action_rules: &init_action_rules,
1380                predicates,
1381                rule_args,
1382                member_actions,
1383                return_actions,
1384                local_int_arg: None,
1385                member_values,
1386                return_values,
1387                rule_alt_number: 0,
1388                track_alt_numbers,
1389                consumed_eof: false,
1390                precedence: 0,
1391                depth: 0,
1392                recovery_symbols: BTreeSet::new(),
1393                recovery_state: None,
1394            },
1395            &mut visiting,
1396            &mut memo,
1397            &mut expected,
1398        );
1399        let Some(outcome) = select_best_outcome(outcomes.into_iter(), self.prediction_mode) else {
1400            let error = self.recognition_error(rule_index, start_index, &expected);
1401            report_token_source_errors(&self.input.drain_source_errors());
1402            return Err(error);
1403        };
1404
1405        report_parser_diagnostics(&self.prediction_diagnostics);
1406        report_parser_diagnostics(&outcome.diagnostics);
1407        report_token_source_errors(&self.input.drain_source_errors());
1408        let mut actions = outcome.actions;
1409        if init_action_rules.contains(&rule_index) {
1410            actions.insert(
1411                0,
1412                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
1413            );
1414        }
1415        let mut context = ParserRuleContext::new(rule_index, self.state());
1416        if track_alt_numbers {
1417            context.set_alt_number(outcome.alt_number);
1418        }
1419        for (name, value) in outcome.return_values {
1420            context.set_int_return(name, value);
1421        }
1422        if let Some(token) = self.token_at(start_index) {
1423            context.set_start(token);
1424        }
1425        if let Some(token) = self.rule_stop_token(outcome.index, outcome.consumed_eof) {
1426            context.set_stop(token);
1427        }
1428        if self.build_parse_trees {
1429            let nodes = fold_left_recursive_boundaries(outcome.nodes);
1430            for node in &nodes {
1431                context.add_child(self.recognized_node_tree(node, track_alt_numbers)?);
1432            }
1433        }
1434        self.input.seek(outcome.index);
1435
1436        Ok((self.rule_node(context), actions))
1437    }
1438
1439    /// Temporary parser entry used by generated parser methods while the parser
1440    /// ATN simulator is being implemented.
1441    ///
1442    /// This keeps generated parser crates buildable and gives us a stable method
1443    /// surface for every grammar rule. It intentionally accepts all remaining
1444    /// tokens into one rule context; it is not the final parser semantics.
1445    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
1446        let mut context = ParserRuleContext::new(rule_index, self.state());
1447        while self.la(1) != TOKEN_EOF {
1448            let token_type = self.la(1);
1449            let child = self.match_token(token_type)?;
1450            if self.build_parse_trees {
1451                context.add_child(child);
1452            }
1453        }
1454        if self.build_parse_trees {
1455            context.add_child(self.match_eof()?);
1456        }
1457        Ok(self.rule_node(context))
1458    }
1459
1460    /// Builds the parser error reported when no ATN path can reach the active
1461    /// rule stop state.
1462    fn recognition_error(
1463        &mut self,
1464        rule_index: usize,
1465        start_index: usize,
1466        expected: &ExpectedTokens,
1467    ) -> AntlrError {
1468        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
1469        self.input.seek(index);
1470        let current = self.input.lt(1).cloned();
1471        let line = current.as_ref().map(Token::line).unwrap_or_default();
1472        let column = current.as_ref().map(Token::column).unwrap_or_default();
1473        AntlrError::ParserError {
1474            line,
1475            column,
1476            message,
1477        }
1478    }
1479
1480    /// Builds the token index and ANTLR-compatible message for a failed rule.
1481    fn expected_error_message(
1482        &mut self,
1483        rule_index: usize,
1484        start_index: usize,
1485        expected: &ExpectedTokens,
1486    ) -> (usize, String) {
1487        let index = expected
1488            .index
1489            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
1490            .unwrap_or_else(|| self.input.index());
1491        self.input.seek(index);
1492        let current = self.input.lt(1).cloned();
1493        let message = if expected
1494            .no_viable
1495            .as_ref()
1496            .is_some_and(|no_viable| no_viable.error_index == index)
1497        {
1498            let start = expected
1499                .no_viable
1500                .as_ref()
1501                .map_or(start_index, |no_viable| no_viable.start_index);
1502            let text = display_input_text(&self.input.text(start, index));
1503            format!("no viable alternative at input '{text}'")
1504        } else if expected.symbols.is_empty() {
1505            if expected.index.is_some() {
1506                let found = current
1507                    .as_ref()
1508                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
1509                if current
1510                    .as_ref()
1511                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
1512                {
1513                    format!(
1514                        "missing {} at {found}",
1515                        self.expected_symbols_display(&expected.symbols)
1516                    )
1517                } else {
1518                    format!("mismatched input {found}")
1519                }
1520            } else {
1521                format!("no viable alternative while parsing rule {rule_index}")
1522            }
1523        } else {
1524            format!(
1525                "mismatched input {} expecting {}",
1526                current
1527                    .as_ref()
1528                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
1529                self.expected_symbols_display(&expected.symbols)
1530            )
1531        };
1532        (index, message)
1533    }
1534
1535    /// Converts a failed child rule into a recovered outcome so the parent can
1536    /// continue after reporting the child diagnostic.
1537    fn child_rule_failure_recovery(
1538        &mut self,
1539        rule_index: usize,
1540        start_index: usize,
1541        sync_symbols: &BTreeSet<i32>,
1542        member_values: BTreeMap<usize, i64>,
1543        expected: &ExpectedTokens,
1544    ) -> Option<RecognizeOutcome> {
1545        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
1546        let token = self.token_at(error_index);
1547        let mut next_index = error_index;
1548        loop {
1549            let symbol = self.token_type_at(next_index);
1550            if sync_symbols.contains(&symbol) {
1551                if next_index == error_index {
1552                    return None;
1553                }
1554                break;
1555            }
1556            if symbol == TOKEN_EOF {
1557                break;
1558            }
1559            let after = self.consume_index(next_index, symbol);
1560            if after == next_index {
1561                break;
1562            }
1563            next_index = after;
1564        }
1565        Some(RecognizeOutcome {
1566            index: next_index,
1567            consumed_eof: false,
1568            alt_number: 0,
1569            member_values,
1570            return_values: BTreeMap::new(),
1571            diagnostics: vec![diagnostic_for_token(token.as_ref(), message)],
1572            decisions: Vec::new(),
1573            actions: Vec::new(),
1574            nodes: vec![RecognizedNode::ErrorToken { index: error_index }],
1575        })
1576    }
1577
1578    /// Adapts the optional recovery result to the normal outcome list used by
1579    /// rule-call transitions.
1580    fn child_rule_failure_recovery_outcomes(
1581        &mut self,
1582        request: ChildRuleFailureRecovery<'_>,
1583    ) -> Vec<RecognizeOutcome> {
1584        let sync_symbols =
1585            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
1586        self.child_rule_failure_recovery(
1587            request.rule_index,
1588            request.start_index,
1589            &sync_symbols,
1590            request.member_values,
1591            request.expected,
1592        )
1593        .into_iter()
1594        .collect()
1595    }
1596
1597    /// Formats expected token types using ANTLR's single-token or set syntax.
1598    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
1599        expected_symbols_display(symbols, self.vocabulary())
1600    }
1601
1602    /// Returns the single-token deletion repair if the token after `index`
1603    /// satisfies the failed consuming transition.
1604    fn single_token_deletion(
1605        &mut self,
1606        transition: &Transition,
1607        index: usize,
1608        max_token_type: i32,
1609        expected_symbols: &BTreeSet<i32>,
1610    ) -> Option<(ParserDiagnostic, usize, i32)> {
1611        let current_symbol = self.token_type_at(index);
1612        if current_symbol == TOKEN_EOF {
1613            return None;
1614        }
1615        let next_index = self.consume_index(index, current_symbol);
1616        if next_index == index {
1617            return None;
1618        }
1619        let next_symbol = self.token_type_at(next_index);
1620        if !transition.matches(next_symbol, 1, max_token_type) {
1621            return None;
1622        }
1623        let transition_expected = transition_expected_symbols(transition, max_token_type);
1624        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
1625            &transition_expected
1626        } else {
1627            expected_symbols
1628        });
1629        let current = self.token_at(index);
1630        let message = format!(
1631            "extraneous input {} expecting {expected_display}",
1632            current
1633                .as_ref()
1634                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
1635        );
1636        Some((
1637            diagnostic_for_token(current.as_ref(), message),
1638            next_index,
1639            next_symbol,
1640        ))
1641    }
1642
1643    /// Returns the repair used when deleting the current token lets a recovery
1644    /// state continue with the following token.
1645    fn current_token_deletion(
1646        &mut self,
1647        index: usize,
1648        expected_symbols: &BTreeSet<i32>,
1649    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
1650        if expected_symbols.is_empty() {
1651            return None;
1652        }
1653        let current_symbol = self.token_type_at(index);
1654        if current_symbol == TOKEN_EOF {
1655            return None;
1656        }
1657        let current = self.token_at(index);
1658        let message = format!(
1659            "extraneous input {} expecting {}",
1660            current
1661                .as_ref()
1662                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
1663            self.expected_symbols_display(expected_symbols)
1664        );
1665        let diagnostic = diagnostic_for_token(current.as_ref(), message);
1666        let mut skipped = Vec::new();
1667        let mut cursor = index;
1668        loop {
1669            let symbol = self.token_type_at(cursor);
1670            if symbol == TOKEN_EOF {
1671                return None;
1672            }
1673            skipped.push(cursor);
1674            let next_index = self.consume_index(cursor, symbol);
1675            if next_index == cursor {
1676                return None;
1677            }
1678            let next_symbol = self.token_type_at(next_index);
1679            if expected_symbols.contains(&next_symbol) {
1680                return Some((diagnostic, next_index, skipped));
1681            }
1682            cursor = next_index;
1683        }
1684    }
1685
1686    /// Returns the single-token insertion repair for a failed consuming
1687    /// transition. The caller validates the repair by continuing from the
1688    /// transition target at the same input index.
1689    fn single_token_insertion(
1690        &mut self,
1691        transition: &Transition,
1692        index: usize,
1693        max_token_type: i32,
1694        expected_symbols: &BTreeSet<i32>,
1695        follow_symbols: &BTreeSet<i32>,
1696    ) -> Option<(ParserDiagnostic, i32, String)> {
1697        let current_symbol = self.token_type_at(index);
1698        if !follow_symbols.contains(&current_symbol) {
1699            return None;
1700        }
1701        let transition_expected = transition_expected_symbols(transition, max_token_type);
1702        let token_type = transition_expected.iter().next().copied()?;
1703        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
1704            &transition_expected
1705        } else {
1706            expected_symbols
1707        });
1708        let mut token_symbols = BTreeSet::new();
1709        token_symbols.insert(token_type);
1710        let missing_token_display = self.expected_symbols_display(&token_symbols);
1711        let current = self.token_at(index);
1712        let message = format!(
1713            "missing {expected_display} at {}",
1714            current
1715                .as_ref()
1716                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
1717        );
1718        let text = format!("<missing {missing_token_display}>");
1719        Some((
1720            diagnostic_for_token(current.as_ref(), message),
1721            token_type,
1722            text,
1723        ))
1724    }
1725
1726    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
1727    /// skip the unexpected current token when the following token satisfies the
1728    /// transition that failed.
1729    fn fast_single_token_deletion_recovery(
1730        &mut self,
1731        recovery: FastRecoveryRequest<'_, '_>,
1732    ) -> Vec<FastRecognizeOutcome> {
1733        let FastRecoveryRequest {
1734            atn,
1735            transition,
1736            expected_symbols,
1737            target,
1738            request,
1739            visiting,
1740            memo,
1741            expected,
1742        } = recovery;
1743        let FastRecognizeRequest {
1744            stop_state,
1745            index,
1746            rule_start_index,
1747            decision_start_index,
1748            precedence,
1749            depth,
1750            ..
1751        } = request;
1752        let Some((diagnostic, next_index, next_symbol)) =
1753            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
1754        else {
1755            return Vec::new();
1756        };
1757        let after_next = self.consume_index(next_index, next_symbol);
1758        self.recognize_state_fast(
1759            atn,
1760            FastRecognizeRequest {
1761                state_number: target,
1762                stop_state,
1763                index: after_next,
1764                rule_start_index,
1765                decision_start_index,
1766                precedence,
1767                depth: depth + 1,
1768                recovery_symbols: BTreeSet::new(),
1769                recovery_state: None,
1770            },
1771            visiting,
1772            memo,
1773            expected,
1774        )
1775        .into_iter()
1776        .map(|mut outcome| {
1777            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
1778            outcome.diagnostics.insert(0, diagnostic.clone());
1779            outcome
1780                .nodes
1781                .prepend(Rc::new(FastRecognizedNode::Token { index: next_index }));
1782            outcome
1783                .nodes
1784                .prepend(Rc::new(FastRecognizedNode::ErrorToken { index }));
1785            outcome
1786        })
1787        .collect()
1788    }
1789
1790    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
1791    /// pretend the expected transition token was present and continue without
1792    /// consuming the current token.
1793    fn fast_single_token_insertion_recovery(
1794        &mut self,
1795        recovery: FastRecoveryRequest<'_, '_>,
1796    ) -> Vec<FastRecognizeOutcome> {
1797        let FastRecoveryRequest {
1798            atn,
1799            transition,
1800            expected_symbols,
1801            target,
1802            request,
1803            visiting,
1804            memo,
1805            expected,
1806        } = recovery;
1807        let FastRecognizeRequest {
1808            stop_state,
1809            index,
1810            rule_start_index,
1811            decision_start_index,
1812            precedence,
1813            depth,
1814            ..
1815        } = request;
1816        let follow_symbols = state_expected_symbols(atn, transition.target());
1817        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
1818            transition,
1819            index,
1820            atn.max_token_type(),
1821            &expected_symbols,
1822            &follow_symbols,
1823        ) else {
1824            return Vec::new();
1825        };
1826        self.recognize_state_fast(
1827            atn,
1828            FastRecognizeRequest {
1829                state_number: target,
1830                stop_state,
1831                index,
1832                rule_start_index,
1833                decision_start_index,
1834                precedence,
1835                depth: depth + 1,
1836                recovery_symbols: BTreeSet::new(),
1837                recovery_state: None,
1838            },
1839            visiting,
1840            memo,
1841            expected,
1842        )
1843        .into_iter()
1844        .map(|mut outcome| {
1845            outcome.diagnostics.insert(0, diagnostic.clone());
1846            outcome.nodes.prepend(Rc::new(FastRecognizedNode::MissingToken {
1847                token_type,
1848                at_index: index,
1849                text: text.clone(),
1850            }));
1851            outcome
1852        })
1853        .collect()
1854    }
1855
1856    /// Retries the current fast-recognition state after deleting one
1857    /// unexpected token that precedes a valid loop or block continuation.
1858    fn fast_current_token_deletion_recovery(
1859        &mut self,
1860        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
1861    ) -> Vec<FastRecognizeOutcome> {
1862        let FastCurrentTokenDeletionRequest {
1863            atn,
1864            expected_symbols,
1865            mut request,
1866            visiting,
1867            memo,
1868            expected,
1869        } = recovery;
1870        if request.index == request.rule_start_index {
1871            return Vec::new();
1872        }
1873        let Some((diagnostic, next_index, skipped)) =
1874            self.current_token_deletion(request.index, &expected_symbols)
1875        else {
1876            return Vec::new();
1877        };
1878        request.state_number = request.recovery_state.unwrap_or(request.state_number);
1879        request.index = next_index;
1880        request.depth += 1;
1881        request.recovery_state = None;
1882        self.recognize_state_fast(atn, request, visiting, memo, expected)
1883            .into_iter()
1884            .map(|mut outcome| {
1885                outcome.diagnostics.insert(0, diagnostic.clone());
1886                for index in skipped.iter().rev() {
1887                    outcome
1888                        .nodes
1889                        .prepend(Rc::new(FastRecognizedNode::ErrorToken { index: *index }));
1890                }
1891                outcome
1892            })
1893            .collect()
1894    }
1895
1896    /// Attempts to reach `stop_state` from `state_number` without committing
1897    /// token consumption to the parser's public stream position.
1898    fn recognize_state_fast(
1899        &mut self,
1900        atn: &Atn,
1901        request: FastRecognizeRequest,
1902        visiting: &mut BTreeSet<FastRecognizeKey>,
1903        memo: &mut BTreeMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
1904        expected: &mut ExpectedTokens,
1905    ) -> Vec<FastRecognizeOutcome> {
1906        let FastRecognizeRequest {
1907            state_number,
1908            stop_state,
1909            index,
1910            rule_start_index,
1911            decision_start_index,
1912            precedence,
1913            depth,
1914            recovery_symbols,
1915            recovery_state,
1916        } = request;
1917        if depth > RECOGNITION_DEPTH_LIMIT {
1918            return Vec::new();
1919        }
1920        if state_number == stop_state {
1921            return vec![FastRecognizeOutcome {
1922                index,
1923                consumed_eof: false,
1924                diagnostics: Vec::new(),
1925                nodes: NodeList::new(),
1926            }];
1927        }
1928        let key = FastRecognizeKey {
1929            state_number,
1930            stop_state,
1931            index,
1932            rule_start_index,
1933            decision_start_index,
1934            precedence,
1935            recovery_symbols: recovery_symbols.clone(),
1936            recovery_state,
1937        };
1938        if let Some(outcomes) = memo.get(&key) {
1939            return outcomes.clone();
1940        }
1941
1942        let visit_key = key.clone();
1943        if !visiting.insert(visit_key.clone()) {
1944            return Vec::new();
1945        }
1946
1947        let Some(state) = atn.state(state_number) else {
1948            visiting.remove(&visit_key);
1949            return Vec::new();
1950        };
1951        let next_decision_start_index = if starts_prediction_decision(state) {
1952            Some(index)
1953        } else {
1954            decision_start_index
1955        };
1956        let (epsilon_recovery_symbols, epsilon_recovery_state) =
1957            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
1958        let mut outcomes = Vec::new();
1959        for transition in &state.transitions {
1960            match transition {
1961                Transition::Epsilon { target }
1962                | Transition::Predicate { target, .. }
1963                | Transition::Action { target, .. } => {
1964                    let boundary = left_recursive_boundary(atn, state, *target);
1965                    outcomes.extend(
1966                        self.recognize_state_fast(
1967                            atn,
1968                            FastRecognizeRequest {
1969                                state_number: *target,
1970                                stop_state,
1971                                index,
1972                                rule_start_index,
1973                                decision_start_index: next_decision_start_index,
1974                                precedence,
1975                                depth: depth + 1,
1976                                recovery_symbols: epsilon_recovery_symbols.clone(),
1977                                recovery_state: epsilon_recovery_state,
1978                            },
1979                            visiting,
1980                            memo,
1981                            expected,
1982                        )
1983                        .into_iter()
1984                        .map(|mut outcome| {
1985                            if let Some(rule_index) = boundary {
1986                                outcome.nodes.prepend(Rc::new(
1987                                    FastRecognizedNode::LeftRecursiveBoundary { rule_index },
1988                                ));
1989                            }
1990                            outcome
1991                        }),
1992                    );
1993                }
1994                Transition::Precedence {
1995                    target,
1996                    precedence: transition_precedence,
1997                } => {
1998                    if *transition_precedence >= precedence {
1999                        let boundary = left_recursive_boundary(atn, state, *target);
2000                        outcomes.extend(
2001                            self.recognize_state_fast(
2002                                atn,
2003                                FastRecognizeRequest {
2004                                    state_number: *target,
2005                                    stop_state,
2006                                    index,
2007                                    rule_start_index,
2008                                    decision_start_index: next_decision_start_index,
2009                                    precedence,
2010                                    depth: depth + 1,
2011                                    recovery_symbols: epsilon_recovery_symbols.clone(),
2012                                    recovery_state: epsilon_recovery_state,
2013                                },
2014                                visiting,
2015                                memo,
2016                                expected,
2017                            )
2018                            .into_iter()
2019                            .map(|mut outcome| {
2020                                if let Some(rule_index) = boundary {
2021                                    outcome.nodes.prepend(Rc::new(
2022                                        FastRecognizedNode::LeftRecursiveBoundary {
2023                                            rule_index,
2024                                        },
2025                                    ));
2026                                }
2027                                outcome
2028                            }),
2029                        );
2030                    }
2031                }
2032                Transition::Rule {
2033                    target,
2034                    rule_index,
2035                    follow_state,
2036                    precedence: rule_precedence,
2037                    ..
2038                } => {
2039                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
2040                    else {
2041                        continue;
2042                    };
2043                    // Lookahead-based pruning. The recognizer would otherwise
2044                    // explore every speculative rule call, producing exponential
2045                    // work on grammars with many epsilon-reachable rules. When
2046                    // the rule is non-nullable and its FIRST set excludes the
2047                    // current lookahead, recursion can't find a clean path
2048                    // *through this rule*. Skipping is only safe if some sibling
2049                    // transition can still consume the lookahead — otherwise the
2050                    // rule call is the sole continuation and must run so the
2051                    // single-token insertion / deletion recovery inside the
2052                    // called rule can fire (mirroring ANTLR's reference behavior
2053                    // of conjuring a missing token at child-rule entry).
2054                    let symbol = self.token_type_at(index);
2055                    let first =
2056                        rule_first_set(atn, *target, child_stop, &mut self.first_set_cache);
2057                    if self.fast_first_set_prefilter
2058                        && !first.nullable
2059                        && !first.symbols.contains(&symbol)
2060                    {
2061                        if !first.symbols.is_empty() {
2062                            match expected.index {
2063                                Some(current) if index < current => {}
2064                                Some(current) if index == current => {
2065                                    expected.symbols.extend(first.symbols);
2066                                }
2067                                _ => {
2068                                    expected.index = Some(index);
2069                                    expected.symbols = first.symbols;
2070                                }
2071                            }
2072                        }
2073                        continue;
2074                    }
2075                    let expected_before_child = expected.clone();
2076                    let children = self.recognize_state_fast(
2077                        atn,
2078                        FastRecognizeRequest {
2079                            state_number: *target,
2080                            stop_state: child_stop,
2081                            index,
2082                            rule_start_index: index,
2083                            decision_start_index: None,
2084                            precedence: *rule_precedence,
2085                            depth: depth + 1,
2086                            recovery_symbols: epsilon_recovery_symbols.clone(),
2087                            recovery_state: epsilon_recovery_state,
2088                        },
2089                        visiting,
2090                        memo,
2091                        expected,
2092                    );
2093                    if children
2094                        .iter()
2095                        .any(|child| child.diagnostics.is_empty() && child.index > index)
2096                    {
2097                        *expected = expected_before_child;
2098                    }
2099                    for child in children {
2100                        let child_index = child.index;
2101                        let child_consumed_eof = child.consumed_eof;
2102                        let child_diagnostics = child.diagnostics;
2103                        let child_node = Rc::new(FastRecognizedNode::Rule {
2104                            rule_index: *rule_index,
2105                            invoking_state: invoking_state_number(state_number),
2106                            start_index: index,
2107                            stop_index: self.rule_stop_token_index(child_index, child_consumed_eof),
2108                            children: fold_fast_left_recursive_boundaries(child.nodes.to_vec()),
2109                        });
2110                        outcomes.extend(
2111                            self.recognize_state_fast(
2112                                atn,
2113                                FastRecognizeRequest {
2114                                    state_number: *follow_state,
2115                                    stop_state,
2116                                    index: child_index,
2117                                    rule_start_index,
2118                                    decision_start_index: next_decision_start_index,
2119                                    precedence,
2120                                    depth: depth + 1,
2121                                    recovery_symbols: BTreeSet::new(),
2122                                    recovery_state: None,
2123                                },
2124                                visiting,
2125                                memo,
2126                                expected,
2127                            )
2128                            .into_iter()
2129                            .map(|mut outcome| {
2130                                outcome.consumed_eof |= child_consumed_eof;
2131                                let mut diagnostics = child_diagnostics.clone();
2132                                diagnostics.append(&mut outcome.diagnostics);
2133                                outcome.diagnostics = diagnostics;
2134                                outcome.nodes.prepend(Rc::clone(&child_node));
2135                                outcome
2136                            }),
2137                        );
2138                    }
2139                }
2140                Transition::Atom { target, .. }
2141                | Transition::Range { target, .. }
2142                | Transition::Set { target, .. }
2143                | Transition::NotSet { target, .. }
2144                | Transition::Wildcard { target, .. } => {
2145                    let symbol = self.token_type_at(index);
2146                    if transition.matches(symbol, 1, atn.max_token_type()) {
2147                        let next_index = self.consume_index(index, symbol);
2148                        outcomes.extend(
2149                            self.recognize_state_fast(
2150                                atn,
2151                                FastRecognizeRequest {
2152                                    state_number: *target,
2153                                    stop_state,
2154                                    index: next_index,
2155                                    rule_start_index,
2156                                    decision_start_index: next_decision_start_index,
2157                                    precedence,
2158                                    depth: depth + 1,
2159                                    recovery_symbols: BTreeSet::new(),
2160                                    recovery_state: None,
2161                                },
2162                                visiting,
2163                                memo,
2164                                expected,
2165                            )
2166                            .into_iter()
2167                            .map(|mut outcome| {
2168                                outcome.consumed_eof |= symbol == TOKEN_EOF;
2169                                outcome
2170                                    .nodes
2171                                    .prepend(Rc::new(FastRecognizedNode::Token { index }));
2172                                outcome
2173                            }),
2174                        );
2175                    } else {
2176                        let expected_symbols =
2177                            recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
2178                        if expected_symbols.contains(&symbol) {
2179                            continue;
2180                        }
2181                        expected.record_transition(index, transition, atn.max_token_type());
2182                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
2183                        outcomes.extend(self.fast_single_token_deletion_recovery(
2184                            FastRecoveryRequest {
2185                                atn,
2186                                transition,
2187                                expected_symbols: expected_symbols.clone(),
2188                                target: *target,
2189                                request: FastRecognizeRequest {
2190                                    state_number,
2191                                    stop_state,
2192                                    index,
2193                                    rule_start_index,
2194                                    decision_start_index,
2195                                    precedence,
2196                                    depth,
2197                                    recovery_symbols: recovery_symbols.clone(),
2198                                    recovery_state,
2199                                },
2200                                visiting,
2201                                memo,
2202                                expected,
2203                            },
2204                        ));
2205                        if !state_is_left_recursive_rule(atn, state) {
2206                            outcomes.extend(self.fast_single_token_insertion_recovery(
2207                                FastRecoveryRequest {
2208                                    atn,
2209                                    transition,
2210                                    expected_symbols: expected_symbols.clone(),
2211                                    target: *target,
2212                                    request: FastRecognizeRequest {
2213                                        state_number,
2214                                        stop_state,
2215                                        index,
2216                                        rule_start_index,
2217                                        decision_start_index,
2218                                        precedence,
2219                                        depth,
2220                                        recovery_symbols: recovery_symbols.clone(),
2221                                        recovery_state,
2222                                    },
2223                                    visiting,
2224                                    memo,
2225                                    expected,
2226                                },
2227                            ));
2228                        }
2229                        outcomes.extend(self.fast_current_token_deletion_recovery(
2230                            FastCurrentTokenDeletionRequest {
2231                                atn,
2232                                expected_symbols,
2233                                request: FastRecognizeRequest {
2234                                    state_number,
2235                                    stop_state,
2236                                    index,
2237                                    rule_start_index,
2238                                    decision_start_index,
2239                                    precedence,
2240                                    depth,
2241                                    recovery_symbols: recovery_symbols.clone(),
2242                                    recovery_state,
2243                                },
2244                                visiting,
2245                                memo,
2246                                expected,
2247                            },
2248                        ));
2249                    }
2250                }
2251            }
2252        }
2253
2254        visiting.remove(&visit_key);
2255        if self.prediction_mode == PredictionMode::Ll {
2256            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
2257        }
2258        dedupe_fast_outcomes(&mut outcomes);
2259        memo.insert(key, outcomes.clone());
2260        outcomes
2261    }
2262
2263    /// Explores single-token deletion recovery while preserving the matched
2264    /// token and skipped error token in the selected parse tree path.
2265    fn single_token_deletion_recovery(
2266        &mut self,
2267        recovery: RecoveryRequest<'_, '_>,
2268    ) -> Vec<RecognizeOutcome> {
2269        let RecoveryRequest {
2270            atn,
2271            transition,
2272            expected_symbols,
2273            target,
2274            request,
2275            visiting,
2276            memo,
2277            expected,
2278        } = recovery;
2279        let RecognizeRequest {
2280            stop_state,
2281            index,
2282            rule_start_index,
2283            decision_start_index,
2284            init_action_rules,
2285            predicates,
2286            rule_args,
2287            member_actions,
2288            return_actions,
2289            local_int_arg,
2290            member_values,
2291            return_values,
2292            rule_alt_number,
2293            track_alt_numbers,
2294            consumed_eof,
2295            precedence,
2296            depth,
2297            ..
2298        } = request;
2299        let Some((diagnostic, next_index, next_symbol)) =
2300            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
2301        else {
2302            return Vec::new();
2303        };
2304        let after_next = self.consume_index(next_index, next_symbol);
2305        self.recognize_state(
2306            atn,
2307            RecognizeRequest {
2308                state_number: target,
2309                stop_state,
2310                index: after_next,
2311                rule_start_index,
2312                decision_start_index,
2313                init_action_rules,
2314                predicates,
2315                rule_args,
2316                member_actions,
2317                return_actions,
2318                local_int_arg,
2319                member_values,
2320                return_values,
2321                rule_alt_number,
2322                track_alt_numbers,
2323                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
2324                precedence,
2325                depth: depth + 1,
2326                recovery_symbols: BTreeSet::new(),
2327                recovery_state: None,
2328            },
2329            visiting,
2330            memo,
2331            expected,
2332        )
2333        .into_iter()
2334        .map(|mut outcome| {
2335            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
2336            outcome.diagnostics.insert(0, diagnostic.clone());
2337            outcome
2338                .nodes
2339                .insert(0, RecognizedNode::Token { index: next_index });
2340            outcome
2341                .nodes
2342                .insert(0, RecognizedNode::ErrorToken { index });
2343            outcome
2344        })
2345        .collect()
2346    }
2347
2348    /// Retries the current recognition state after deleting one unexpected
2349    /// token, preserving the deleted token as an error node in the parse tree.
2350    fn current_token_deletion_recovery(
2351        &mut self,
2352        recovery: CurrentTokenDeletionRequest<'_, '_>,
2353    ) -> Vec<RecognizeOutcome> {
2354        let CurrentTokenDeletionRequest {
2355            atn,
2356            expected_symbols,
2357            mut request,
2358            visiting,
2359            memo,
2360            expected,
2361        } = recovery;
2362        let error_index = request.index;
2363        if error_index == request.rule_start_index {
2364            return Vec::new();
2365        }
2366        let Some((diagnostic, next_index, skipped)) =
2367            self.current_token_deletion(error_index, &expected_symbols)
2368        else {
2369            return Vec::new();
2370        };
2371        request.state_number = request.recovery_state.unwrap_or(request.state_number);
2372        request.index = next_index;
2373        request.depth += 1;
2374        request.recovery_state = None;
2375        self.recognize_state(atn, request, visiting, memo, expected)
2376            .into_iter()
2377            .map(|mut outcome| {
2378                outcome.diagnostics.insert(0, diagnostic.clone());
2379                for index in skipped.iter().rev() {
2380                    outcome
2381                        .nodes
2382                        .insert(0, RecognizedNode::ErrorToken { index: *index });
2383                }
2384                outcome
2385            })
2386            .collect()
2387    }
2388
2389    /// Falls back after deletion/insertion repairs cannot continue from a
2390    /// failed consuming transition.
2391    fn consuming_failure_fallback(
2392        &mut self,
2393        fallback: ConsumingFailureFallback<'_>,
2394        visiting: &mut BTreeSet<RecognizeKey>,
2395        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2396        expected: &mut ExpectedTokens,
2397    ) -> Vec<RecognizeOutcome> {
2398        if fallback.expected_symbols.is_empty() {
2399            return Vec::new();
2400        }
2401        if fallback.symbol == TOKEN_EOF {
2402            return self.eof_consuming_failure_fallback(fallback, expected);
2403        }
2404        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
2405    }
2406
2407    /// Keeps unexpected non-EOF input visible as an error node when no repair
2408    /// path can otherwise reach the transition target.
2409    fn non_eof_consuming_failure_fallback(
2410        &mut self,
2411        fallback: ConsumingFailureFallback<'_>,
2412        visiting: &mut BTreeSet<RecognizeKey>,
2413        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2414        expected: &mut ExpectedTokens,
2415    ) -> Vec<RecognizeOutcome> {
2416        let ConsumingFailureFallback {
2417            atn,
2418            target,
2419            request,
2420            symbol,
2421            expected_symbols,
2422            decision_start_index,
2423            decision,
2424        } = fallback;
2425        let error_index = request.index;
2426        let diagnostic =
2427            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
2428        let next_index = self.consume_index(error_index, symbol);
2429        self.recognize_state(
2430            atn,
2431            RecognizeRequest {
2432                state_number: target,
2433                stop_state: request.stop_state,
2434                index: next_index,
2435                rule_start_index: request.rule_start_index,
2436                decision_start_index,
2437                init_action_rules: request.init_action_rules,
2438                predicates: request.predicates,
2439                rule_args: request.rule_args,
2440                member_actions: request.member_actions,
2441                return_actions: request.return_actions,
2442                local_int_arg: request.local_int_arg,
2443                member_values: request.member_values,
2444                return_values: request.return_values,
2445                rule_alt_number: request.rule_alt_number,
2446                track_alt_numbers: request.track_alt_numbers,
2447                consumed_eof: request.consumed_eof,
2448                precedence: request.precedence,
2449                depth: request.depth + 1,
2450                recovery_symbols: BTreeSet::new(),
2451                recovery_state: None,
2452            },
2453            visiting,
2454            memo,
2455            expected,
2456        )
2457        .into_iter()
2458        .map(|mut outcome| {
2459            prepend_decision(&mut outcome, decision);
2460            outcome.diagnostics.insert(0, diagnostic.clone());
2461            outcome
2462                .nodes
2463                .insert(0, RecognizedNode::ErrorToken { index: error_index });
2464            outcome
2465        })
2466        .collect()
2467    }
2468
2469    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
2470    /// behavior of unwinding instead of inserting caller tokens at EOF.
2471    fn eof_consuming_failure_fallback(
2472        &mut self,
2473        fallback: ConsumingFailureFallback<'_>,
2474        expected: &ExpectedTokens,
2475    ) -> Vec<RecognizeOutcome> {
2476        let request = fallback.request;
2477        if request.index == request.rule_start_index {
2478            return Vec::new();
2479        }
2480        let diagnostic =
2481            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
2482        vec![RecognizeOutcome {
2483            index: request.index,
2484            consumed_eof: request.consumed_eof,
2485            alt_number: request.rule_alt_number,
2486            member_values: request.member_values,
2487            return_values: request.return_values,
2488            diagnostics: vec![diagnostic],
2489            decisions: Vec::new(),
2490            actions: Vec::new(),
2491            nodes: Vec::new(),
2492        }]
2493    }
2494
2495    /// Explores single-token insertion recovery while adding a conjured
2496    /// missing-token error node to the selected parse tree path.
2497    fn single_token_insertion_recovery(
2498        &mut self,
2499        recovery: RecoveryRequest<'_, '_>,
2500    ) -> Vec<RecognizeOutcome> {
2501        let RecoveryRequest {
2502            atn,
2503            transition,
2504            expected_symbols,
2505            target,
2506            request,
2507            visiting,
2508            memo,
2509            expected,
2510        } = recovery;
2511        let RecognizeRequest {
2512            stop_state,
2513            index,
2514            rule_start_index,
2515            decision_start_index,
2516            init_action_rules,
2517            predicates,
2518            rule_args,
2519            member_actions,
2520            return_actions,
2521            local_int_arg,
2522            member_values,
2523            return_values,
2524            rule_alt_number,
2525            track_alt_numbers,
2526            consumed_eof,
2527            precedence,
2528            depth,
2529            ..
2530        } = request;
2531        let follow_symbols = state_expected_symbols(atn, transition.target());
2532        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
2533            transition,
2534            index,
2535            atn.max_token_type(),
2536            &expected_symbols,
2537            &follow_symbols,
2538        ) else {
2539            return Vec::new();
2540        };
2541        self.recognize_state(
2542            atn,
2543            RecognizeRequest {
2544                state_number: target,
2545                stop_state,
2546                index,
2547                rule_start_index,
2548                decision_start_index,
2549                init_action_rules,
2550                predicates,
2551                rule_args,
2552                member_actions,
2553                return_actions,
2554                local_int_arg,
2555                member_values,
2556                return_values,
2557                rule_alt_number,
2558                track_alt_numbers,
2559                consumed_eof,
2560                precedence,
2561                depth: depth + 1,
2562                recovery_symbols: BTreeSet::new(),
2563                recovery_state: None,
2564            },
2565            visiting,
2566            memo,
2567            expected,
2568        )
2569        .into_iter()
2570        .map(|mut outcome| {
2571            outcome.diagnostics.insert(0, diagnostic.clone());
2572            outcome.nodes.insert(
2573                0,
2574                RecognizedNode::MissingToken {
2575                    token_type,
2576                    at_index: index,
2577                    text: text.clone(),
2578                },
2579            );
2580            outcome
2581        })
2582        .collect()
2583    }
2584
2585    /// Attempts to reach `stop_state` and carries semantic actions for the
2586    /// selected parser path.
2587    fn recognize_state(
2588        &mut self,
2589        atn: &Atn,
2590        request: RecognizeRequest<'_>,
2591        visiting: &mut BTreeSet<RecognizeKey>,
2592        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2593        expected: &mut ExpectedTokens,
2594    ) -> Vec<RecognizeOutcome> {
2595        let request_template = request.clone();
2596        let RecognizeRequest {
2597            state_number,
2598            stop_state,
2599            index,
2600            rule_start_index,
2601            decision_start_index,
2602            init_action_rules,
2603            predicates,
2604            rule_args,
2605            member_actions,
2606            return_actions,
2607            local_int_arg,
2608            member_values,
2609            return_values,
2610            rule_alt_number,
2611            track_alt_numbers,
2612            consumed_eof,
2613            precedence,
2614            depth,
2615            recovery_symbols,
2616            recovery_state,
2617        } = request;
2618        if depth > RECOGNITION_DEPTH_LIMIT {
2619            return Vec::new();
2620        }
2621        if state_number == stop_state {
2622            return stop_outcome(
2623                index,
2624                consumed_eof,
2625                rule_alt_number,
2626                member_values,
2627                return_values,
2628            );
2629        }
2630        let key = RecognizeKey {
2631            state_number,
2632            stop_state,
2633            index,
2634            rule_start_index,
2635            decision_start_index,
2636            local_int_arg,
2637            member_values: member_values.clone(),
2638            return_values: return_values.clone(),
2639            rule_alt_number,
2640            track_alt_numbers,
2641            consumed_eof,
2642            precedence,
2643            recovery_symbols: recovery_symbols.clone(),
2644            recovery_state,
2645        };
2646        if let Some(outcomes) = memo.get(&key) {
2647            return outcomes.clone();
2648        }
2649
2650        let visit_key = key.clone();
2651        if !visiting.insert(visit_key.clone()) {
2652            return Vec::new();
2653        }
2654
2655        let Some(state) = atn.state(state_number) else {
2656            visiting.remove(&visit_key);
2657            return Vec::new();
2658        };
2659        let next_decision_start_index = if starts_prediction_decision(state) {
2660            Some(index)
2661        } else {
2662            decision_start_index
2663        };
2664        let (epsilon_recovery_symbols, epsilon_recovery_state) =
2665            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
2666        let mut outcomes = Vec::new();
2667        for (transition_index, transition) in state.transitions.iter().enumerate() {
2668            let decision = transition_decision(atn, state, transition_index, predicates);
2669            let next_alt_number =
2670                next_alt_number(state, transition_index, rule_alt_number, track_alt_numbers);
2671            match transition {
2672                Transition::Epsilon { target } | Transition::Action { target, .. } => {
2673                    let action_rule_index = match transition {
2674                        Transition::Action { rule_index, .. } => Some(*rule_index),
2675                        _ => None,
2676                    };
2677                    outcomes.extend(self.recognize_epsilon_or_action_step(
2678                        atn,
2679                        &request_template,
2680                        EpsilonActionStep {
2681                            source_state: state_number,
2682                            target: *target,
2683                            action_rule_index,
2684                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
2685                            decision,
2686                            decision_start_index: next_decision_start_index,
2687                            alt_number: next_alt_number,
2688                            recovery_symbols: epsilon_recovery_symbols.clone(),
2689                            recovery_state: epsilon_recovery_state,
2690                        },
2691                        RecognizeScratch {
2692                            visiting,
2693                            memo,
2694                            expected,
2695                        },
2696                    ));
2697                }
2698                Transition::Predicate {
2699                    target,
2700                    rule_index,
2701                    pred_index,
2702                    ..
2703                } => {
2704                    let predicate = PredicateEval {
2705                        index,
2706                        rule_index: *rule_index,
2707                        pred_index: *pred_index,
2708                        predicates,
2709                        local_int_arg,
2710                        member_values: &member_values,
2711                    };
2712                    if self.parser_predicate_matches(predicate) {
2713                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
2714                        outcomes.extend(
2715                            self.recognize_state(
2716                                atn,
2717                                RecognizeRequest {
2718                                    state_number: *target,
2719                                    stop_state,
2720                                    index,
2721                                    rule_start_index,
2722                                    decision_start_index: next_decision_start_index,
2723                                    init_action_rules,
2724                                    predicates,
2725                                    rule_args,
2726                                    member_actions,
2727                                    return_actions,
2728                                    local_int_arg,
2729                                    member_values: member_values.clone(),
2730                                    return_values: return_values.clone(),
2731                                    rule_alt_number: next_alt_number,
2732                                    track_alt_numbers,
2733                                    consumed_eof,
2734                                    precedence,
2735                                    depth: depth + 1,
2736                                    recovery_symbols: epsilon_recovery_symbols.clone(),
2737                                    recovery_state: epsilon_recovery_state,
2738                                },
2739                                visiting,
2740                                memo,
2741                                expected,
2742                            )
2743                            .into_iter()
2744                            .map(|mut outcome| {
2745                                prepend_decision(&mut outcome, decision);
2746                                if let Some(rule_index) = left_recursive_boundary {
2747                                    outcome.nodes.insert(
2748                                        0,
2749                                        RecognizedNode::LeftRecursiveBoundary { rule_index },
2750                                    );
2751                                }
2752                                outcome
2753                            }),
2754                        );
2755                    } else if let Some(message) =
2756                        self.parser_predicate_failure_message(*rule_index, *pred_index, predicates)
2757                    {
2758                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
2759                            rule_index: *rule_index,
2760                            index,
2761                            message,
2762                            member_values: member_values.clone(),
2763                            return_values: return_values.clone(),
2764                            rule_alt_number,
2765                        }));
2766                    } else {
2767                        record_predicate_no_viable(expected, next_decision_start_index, index);
2768                    }
2769                }
2770                Transition::Precedence {
2771                    target,
2772                    precedence: transition_precedence,
2773                } => {
2774                    if *transition_precedence >= precedence {
2775                        outcomes.extend(
2776                            self.recognize_state(
2777                                atn,
2778                                RecognizeRequest {
2779                                    state_number: *target,
2780                                    stop_state,
2781                                    index,
2782                                    rule_start_index,
2783                                    decision_start_index: next_decision_start_index,
2784                                    init_action_rules,
2785                                    predicates,
2786                                    rule_args,
2787                                    member_actions,
2788                                    return_actions,
2789                                    local_int_arg,
2790                                    member_values: member_values.clone(),
2791                                    return_values: return_values.clone(),
2792                                    rule_alt_number: next_alt_number,
2793                                    track_alt_numbers,
2794                                    consumed_eof,
2795                                    precedence,
2796                                    depth: depth + 1,
2797                                    recovery_symbols: epsilon_recovery_symbols.clone(),
2798                                    recovery_state: epsilon_recovery_state,
2799                                },
2800                                visiting,
2801                                memo,
2802                                expected,
2803                            )
2804                            .into_iter()
2805                            .map(|mut outcome| {
2806                                prepend_decision(&mut outcome, decision);
2807                                outcome
2808                            }),
2809                        );
2810                    }
2811                }
2812                Transition::Rule {
2813                    target,
2814                    rule_index,
2815                    follow_state,
2816                    precedence: rule_precedence,
2817                    ..
2818                } => {
2819                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
2820                    else {
2821                        continue;
2822                    };
2823                    let child_local_int_arg =
2824                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
2825                    let expected_before_child = expected.clone();
2826                    let children = self.recognize_state(
2827                        atn,
2828                        RecognizeRequest {
2829                            state_number: *target,
2830                            stop_state: child_stop,
2831                            index,
2832                            rule_start_index: index,
2833                            decision_start_index: None,
2834                            init_action_rules,
2835                            predicates,
2836                            rule_args,
2837                            member_actions,
2838                            return_actions,
2839                            local_int_arg: child_local_int_arg,
2840                            member_values: member_values.clone(),
2841                            return_values: BTreeMap::new(),
2842                            rule_alt_number: 0,
2843                            track_alt_numbers,
2844                            consumed_eof: false,
2845                            precedence: *rule_precedence,
2846                            depth: depth + 1,
2847                            recovery_symbols: epsilon_recovery_symbols.clone(),
2848                            recovery_state: epsilon_recovery_state,
2849                        },
2850                        visiting,
2851                        memo,
2852                        expected,
2853                    );
2854                    let children = if children.is_empty() {
2855                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
2856                            atn,
2857                            rule_index: *rule_index,
2858                            start_index: index,
2859                            follow_state: *follow_state,
2860                            stop_state,
2861                            member_values: member_values.clone(),
2862                            expected,
2863                        })
2864                    } else {
2865                        children
2866                    };
2867                    let preserve_child_expected =
2868                        self.child_expected_reaches_clean_eof(&children, expected);
2869                    restore_expected(
2870                        &children,
2871                        index,
2872                        expected,
2873                        expected_before_child,
2874                        preserve_child_expected,
2875                    );
2876                    for child in children {
2877                        let child_node = RecognizedNode::Rule {
2878                            rule_index: *rule_index,
2879                            invoking_state: invoking_state_number(state_number),
2880                            alt_number: child.alt_number,
2881                            start_index: index,
2882                            stop_index: self.rule_stop_token_index(child.index, child.consumed_eof),
2883                            return_values: child.return_values.clone(),
2884                            children: fold_left_recursive_boundaries(child.nodes.clone()),
2885                        };
2886                        outcomes.extend(
2887                            self.recognize_state(
2888                                atn,
2889                                RecognizeRequest {
2890                                    state_number: *follow_state,
2891                                    stop_state,
2892                                    index: child.index,
2893                                    rule_start_index,
2894                                    decision_start_index: next_decision_start_index,
2895                                    init_action_rules,
2896                                    predicates,
2897                                    rule_args,
2898                                    member_actions,
2899                                    return_actions,
2900                                    local_int_arg,
2901                                    member_values: child.member_values.clone(),
2902                                    return_values: return_values.clone(),
2903                                    rule_alt_number,
2904                                    track_alt_numbers,
2905                                    consumed_eof: consumed_eof || child.consumed_eof,
2906                                    precedence,
2907                                    depth: depth + 1,
2908                                    recovery_symbols: BTreeSet::new(),
2909                                    recovery_state: None,
2910                                },
2911                                visiting,
2912                                memo,
2913                                expected,
2914                            )
2915                            .into_iter()
2916                            .map(|mut outcome| {
2917                                outcome.consumed_eof |= child.consumed_eof;
2918                                let mut diagnostics = child.diagnostics.clone();
2919                                diagnostics.append(&mut outcome.diagnostics);
2920                                outcome.diagnostics = diagnostics;
2921                                let mut decisions = child.decisions.clone();
2922                                decisions.append(&mut outcome.decisions);
2923                                outcome.decisions = decisions;
2924                                prepend_decision(&mut outcome, decision);
2925                                let mut actions = child.actions.clone();
2926                                if init_action_rules.contains(rule_index) {
2927                                    actions.insert(
2928                                        0,
2929                                        ParserAction::new_rule_init(
2930                                            *rule_index,
2931                                            index,
2932                                            Some(*follow_state),
2933                                        ),
2934                                    );
2935                                }
2936                                actions.append(&mut outcome.actions);
2937                                outcome.actions = actions;
2938                                outcome.nodes.insert(0, child_node.clone());
2939                                outcome
2940                            }),
2941                        );
2942                    }
2943                }
2944                Transition::Atom { target, .. }
2945                | Transition::Range { target, .. }
2946                | Transition::Set { target, .. }
2947                | Transition::NotSet { target, .. }
2948                | Transition::Wildcard { target, .. } => {
2949                    let symbol = self.token_type_at(index);
2950                    if transition.matches(symbol, 1, atn.max_token_type()) {
2951                        let next_index = self.consume_index(index, symbol);
2952                        outcomes.extend(
2953                            self.recognize_state(
2954                                atn,
2955                                RecognizeRequest {
2956                                    state_number: *target,
2957                                    stop_state,
2958                                    index: next_index,
2959                                    rule_start_index,
2960                                    decision_start_index: next_decision_start_index,
2961                                    init_action_rules,
2962                                    predicates,
2963                                    rule_args,
2964                                    member_actions,
2965                                    return_actions,
2966                                    local_int_arg,
2967                                    member_values: member_values.clone(),
2968                                    return_values: return_values.clone(),
2969                                    rule_alt_number: next_alt_number,
2970                                    track_alt_numbers,
2971                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
2972                                    precedence,
2973                                    depth: depth + 1,
2974                                    recovery_symbols: BTreeSet::new(),
2975                                    recovery_state: None,
2976                                },
2977                                visiting,
2978                                memo,
2979                                expected,
2980                            )
2981                            .into_iter()
2982                            .map(|mut outcome| {
2983                                prepend_decision(&mut outcome, decision);
2984                                outcome.consumed_eof |= symbol == TOKEN_EOF;
2985                                outcome.nodes.insert(0, RecognizedNode::Token { index });
2986                                outcome
2987                            }),
2988                        );
2989                    } else {
2990                        let expected_symbols =
2991                            recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
2992                        if expected_symbols.contains(&symbol) {
2993                            continue;
2994                        }
2995                        expected.record_transition(index, transition, atn.max_token_type());
2996                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
2997                        let before_recovery = outcomes.len();
2998                        let recovery_request = request_template.clone();
2999                        outcomes.extend(
3000                            self.single_token_deletion_recovery(RecoveryRequest {
3001                                atn,
3002                                transition,
3003                                expected_symbols: expected_symbols.clone(),
3004                                target: *target,
3005                                request: recovery_request.clone(),
3006                                visiting,
3007                                memo,
3008                                expected,
3009                            })
3010                            .into_iter()
3011                            .map(|mut outcome| {
3012                                prepend_decision(&mut outcome, decision);
3013                                outcome
3014                            }),
3015                        );
3016                        if !state_is_left_recursive_rule(atn, state) {
3017                            outcomes.extend(
3018                                self.single_token_insertion_recovery(RecoveryRequest {
3019                                    atn,
3020                                    transition,
3021                                    expected_symbols: expected_symbols.clone(),
3022                                    target: *target,
3023                                    request: recovery_request.clone(),
3024                                    visiting,
3025                                    memo,
3026                                    expected,
3027                                })
3028                                .into_iter()
3029                                .map(|mut outcome| {
3030                                    prepend_decision(&mut outcome, decision);
3031                                    outcome
3032                                }),
3033                            );
3034                        }
3035                        outcomes.extend(self.current_token_deletion_recovery(
3036                            CurrentTokenDeletionRequest {
3037                                atn,
3038                                expected_symbols: expected_symbols.clone(),
3039                                request: recovery_request.clone(),
3040                                visiting,
3041                                memo,
3042                                expected,
3043                            },
3044                        ));
3045                        if outcomes.len() == before_recovery {
3046                            outcomes.extend(self.consuming_failure_fallback(
3047                                ConsumingFailureFallback {
3048                                    atn,
3049                                    target: *target,
3050                                    request: recovery_request,
3051                                    symbol,
3052                                    expected_symbols,
3053                                    decision_start_index: next_decision_start_index,
3054                                    decision,
3055                                },
3056                                visiting,
3057                                memo,
3058                                expected,
3059                            ));
3060                        }
3061                    }
3062                }
3063            }
3064        }
3065
3066        visiting.remove(&visit_key);
3067        self.record_prediction_diagnostics(atn, state, index, &outcomes);
3068        if self.prediction_mode == PredictionMode::Ll {
3069            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes);
3070        }
3071        dedupe_outcomes(&mut outcomes);
3072        memo.insert(key, outcomes.clone());
3073        outcomes
3074    }
3075
3076    /// Follows an epsilon or semantic-action transition while preserving the
3077    /// path-local side effects that may later become generated action output.
3078    fn recognize_epsilon_or_action_step(
3079        &mut self,
3080        atn: &Atn,
3081        request: &RecognizeRequest<'_>,
3082        step: EpsilonActionStep,
3083        scratch: RecognizeScratch<'_>,
3084    ) -> Vec<RecognizeOutcome> {
3085        let RecognizeScratch {
3086            visiting,
3087            memo,
3088            expected,
3089        } = scratch;
3090        let action = step.action_rule_index.map(|rule_index| {
3091            ParserAction::new(
3092                step.source_state,
3093                rule_index,
3094                request.rule_start_index,
3095                self.rule_stop_token_index(request.index, request.consumed_eof),
3096            )
3097        });
3098        let next_member_values = if action.is_some() {
3099            member_values_after_action(
3100                step.source_state,
3101                request.member_actions,
3102                &request.member_values,
3103            )
3104        } else {
3105            request.member_values.clone()
3106        };
3107        let next_return_values = action.map_or_else(
3108            || request.return_values.clone(),
3109            |action| {
3110                return_values_after_action(
3111                    step.source_state,
3112                    action.rule_index(),
3113                    request.return_actions,
3114                    &request.return_values,
3115                )
3116            },
3117        );
3118
3119        self.recognize_state(
3120            atn,
3121            RecognizeRequest {
3122                state_number: step.target,
3123                stop_state: request.stop_state,
3124                index: request.index,
3125                rule_start_index: request.rule_start_index,
3126                decision_start_index: step.decision_start_index,
3127                init_action_rules: request.init_action_rules,
3128                predicates: request.predicates,
3129                rule_args: request.rule_args,
3130                member_actions: request.member_actions,
3131                return_actions: request.return_actions,
3132                local_int_arg: request.local_int_arg,
3133                member_values: next_member_values,
3134                return_values: next_return_values,
3135                rule_alt_number: step.alt_number,
3136                track_alt_numbers: request.track_alt_numbers,
3137                consumed_eof: request.consumed_eof,
3138                precedence: request.precedence,
3139                depth: request.depth + 1,
3140                recovery_symbols: step.recovery_symbols,
3141                recovery_state: step.recovery_state,
3142            },
3143            visiting,
3144            memo,
3145            expected,
3146        )
3147        .into_iter()
3148        .map(|mut outcome| {
3149            prepend_decision(&mut outcome, step.decision);
3150            if let Some(rule_index) = step.left_recursive_boundary {
3151                outcome
3152                    .nodes
3153                    .insert(0, RecognizedNode::LeftRecursiveBoundary { rule_index });
3154            }
3155            if let Some(action) = action {
3156                outcome.actions.insert(0, action);
3157            }
3158            outcome
3159        })
3160        .collect()
3161    }
3162
3163    /// Reads the token type at an absolute token-stream index.
3164    fn token_type_at(&mut self, index: usize) -> i32 {
3165        self.input.seek(index);
3166        self.input.la_token(1)
3167    }
3168
3169    /// Clones the visible token at an absolute token-stream index.
3170    fn token_at(&mut self, index: usize) -> Option<CommonToken> {
3171        self.input.get(index).cloned()
3172    }
3173
3174    /// Normalizes the current token-stream cursor to the next parser-visible
3175    /// token before capturing a rule start boundary.
3176    fn current_visible_index(&mut self) -> usize {
3177        let index = self.input.index();
3178        self.input.seek(index);
3179        self.input.index()
3180    }
3181
3182    /// Reports whether a child rule reached EOF cleanly while also recording
3183    /// an EOF expectation from a longer path inside that child.
3184    fn child_expected_reaches_clean_eof(
3185        &mut self,
3186        children: &[RecognizeOutcome],
3187        expected: &ExpectedTokens,
3188    ) -> bool {
3189        let Some(index) = expected.index else {
3190            return false;
3191        };
3192        self.token_type_at(index) == TOKEN_EOF
3193            && children
3194                .iter()
3195                .any(|child| child.diagnostics.is_empty() && child.index == index)
3196    }
3197
3198    /// Finds the previous token visible to the parser before `index`.
3199    ///
3200    /// The token stream cursor skips hidden-channel tokens, so subtracting one
3201    /// from a visible-token index can point at whitespace. Parser intervals use
3202    /// this helper to stop at the previous visible token while preserving hidden
3203    /// text inside the rendered interval.
3204    fn previous_token_index(&mut self, index: usize) -> Option<usize> {
3205        self.input.previous_visible_token_index(index)
3206    }
3207
3208    /// Returns the token-stream index used as a rule stop boundary.
3209    ///
3210    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
3211    /// stop at `index` rather than at the previous visible token.
3212    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
3213        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
3214            Some(index)
3215        } else {
3216            self.previous_token_index(index)
3217        }
3218    }
3219
3220    /// Returns the rule stop token for a selected parse path.
3221    ///
3222    /// EOF transitions do not advance the token-stream cursor, so an EOF match
3223    /// must use the current token rather than the previous visible token.
3224    fn rule_stop_token(&mut self, index: usize, consumed_eof: bool) -> Option<CommonToken> {
3225        self.rule_stop_token_index(index, consumed_eof)
3226            .and_then(|token_index| self.token_at(token_index))
3227    }
3228
3229    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
3230    ///
3231    /// Generated Java reports the failed-predicate message at the current
3232    /// lookahead, then consumes until rule recovery can resume. The metadata
3233    /// runtime models the same visible tree shape by keeping skipped tokens as
3234    /// error nodes and returning from the active rule at EOF.
3235    fn predicate_failure_recovery(
3236        &mut self,
3237        request: PredicateFailureRecovery<'_>,
3238    ) -> RecognizeOutcome {
3239        let PredicateFailureRecovery {
3240            rule_index,
3241            index,
3242            message,
3243            member_values,
3244            return_values,
3245            rule_alt_number,
3246        } = request;
3247        let rule_name = self
3248            .rule_names()
3249            .get(rule_index)
3250            .map_or_else(|| rule_index.to_string(), Clone::clone);
3251        let diagnostic = diagnostic_for_token(
3252            self.token_at(index).as_ref(),
3253            format!("rule {rule_name} {message}"),
3254        );
3255        let mut nodes = Vec::new();
3256        let mut next_index = index;
3257        loop {
3258            let symbol = self.token_type_at(next_index);
3259            if symbol == TOKEN_EOF {
3260                break;
3261            }
3262            nodes.push(RecognizedNode::ErrorToken { index: next_index });
3263            let after = self.consume_index(next_index, symbol);
3264            if after == next_index {
3265                break;
3266            }
3267            next_index = after;
3268        }
3269        RecognizeOutcome {
3270            index: next_index,
3271            consumed_eof: false,
3272            alt_number: rule_alt_number,
3273            member_values,
3274            return_values,
3275            diagnostics: vec![diagnostic],
3276            decisions: Vec::new(),
3277            actions: Vec::new(),
3278            nodes,
3279        }
3280    }
3281
3282    /// Evaluates a supported parser predicate at a speculative input index.
3283    ///
3284    /// Parser ATN simulation is index-based, so predicate evaluation seeks to
3285    /// the candidate index before applying lookahead. A missing predicate entry
3286    /// means the generator did not opt into runtime evaluation for that
3287    /// coordinate and the transition remains viable.
3288    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
3289        let PredicateEval {
3290            index,
3291            rule_index,
3292            pred_index,
3293            predicates,
3294            local_int_arg,
3295            member_values,
3296        } = eval;
3297        let Some((_, _, predicate)) = predicates
3298            .iter()
3299            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
3300        else {
3301            return true;
3302        };
3303        self.input.seek(index);
3304        match predicate {
3305            ParserPredicate::True => true,
3306            ParserPredicate::False => false,
3307            ParserPredicate::FalseWithMessage { .. } => false,
3308            ParserPredicate::Invoke { value } => {
3309                let key = (rule_index, pred_index);
3310                if !self.invoked_predicates.contains(&key) {
3311                    self.invoked_predicates.push(key);
3312                    use std::io::Write as _;
3313                    let mut stdout = std::io::stdout().lock();
3314                    let _ = writeln!(stdout, "eval={value}");
3315                }
3316                *value
3317            }
3318            ParserPredicate::LookaheadTextEquals { offset, text } => {
3319                self.input.lt(*offset).and_then(Token::text) == Some(*text)
3320            }
3321            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
3322                self.la(*offset) != *token_type
3323            }
3324            ParserPredicate::LocalIntEquals { value } => {
3325                local_int_arg.is_none_or(|(_, actual)| actual == *value)
3326            }
3327            ParserPredicate::LocalIntLessOrEqual { value } => {
3328                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
3329            }
3330            ParserPredicate::MemberModuloEquals {
3331                member,
3332                modulus,
3333                value,
3334                equals,
3335            } => {
3336                if *modulus == 0 {
3337                    return false;
3338                }
3339                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
3340                (actual == *value) == *equals
3341            }
3342        }
3343    }
3344
3345    /// Returns a generated fail-option message for a predicate coordinate.
3346    fn parser_predicate_failure_message(
3347        &self,
3348        rule_index: usize,
3349        pred_index: usize,
3350        predicates: &[(usize, usize, ParserPredicate)],
3351    ) -> Option<&'static str> {
3352        predicates
3353            .iter()
3354            .find_map(|(rule, pred, predicate)| match predicate {
3355                ParserPredicate::FalseWithMessage { message }
3356                    if *rule == rule_index && *pred == pred_index =>
3357                {
3358                    Some(*message)
3359                }
3360                _ => None,
3361            })
3362    }
3363
3364    /// Returns the token-stream index after consuming `symbol` at `index`.
3365    ///
3366    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
3367    /// index stable and rely on `consumed_eof` to record that EOF was matched.
3368    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
3369        self.input.seek(index);
3370        if symbol != TOKEN_EOF {
3371            self.consume();
3372        }
3373        self.input.index()
3374    }
3375
3376    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
3377    /// decision that failed after consuming a shared prefix.
3378    fn no_viable_alternative(
3379        &mut self,
3380        start_index: usize,
3381        error_index: usize,
3382    ) -> ParserDiagnostic {
3383        let text = display_input_text(&self.input.text(start_index, error_index));
3384        diagnostic_for_token(
3385            self.token_at(error_index).as_ref(),
3386            format!("no viable alternative at input '{text}'"),
3387        )
3388    }
3389
3390    /// Selects the diagnostic for a failed consuming transition after all
3391    /// recovery repairs have been ruled out.
3392    fn recovery_failure_diagnostic(
3393        &mut self,
3394        index: usize,
3395        decision_start_index: Option<usize>,
3396        expected_symbols: &BTreeSet<i32>,
3397    ) -> ParserDiagnostic {
3398        if expected_symbols.len() > 1 {
3399            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
3400                return self.no_viable_alternative(decision_start, index);
3401            }
3402        }
3403        diagnostic_for_token(
3404            self.token_at(index).as_ref(),
3405            format!(
3406                "mismatched input {} expecting {}",
3407                self.token_at(index)
3408                    .as_ref()
3409                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
3410                self.expected_symbols_display(expected_symbols)
3411            ),
3412        )
3413    }
3414
3415    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
3416    /// instead of inserting missing tokens in the caller.
3417    fn eof_rule_recovery_diagnostic(
3418        &mut self,
3419        index: usize,
3420        expected_symbols: &BTreeSet<i32>,
3421        expected: &ExpectedTokens,
3422    ) -> ParserDiagnostic {
3423        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
3424            &expected.symbols
3425        } else {
3426            expected_symbols
3427        };
3428        diagnostic_for_token(
3429            self.token_at(index).as_ref(),
3430            format!(
3431                "mismatched input {} expecting {}",
3432                self.token_at(index)
3433                    .as_ref()
3434                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
3435                self.expected_symbols_display(symbols)
3436            ),
3437        )
3438    }
3439
3440    /// Returns token text for a buffered token interval used by generated
3441    /// `$text` actions.
3442    ///
3443    /// ANTLR treats EOF as a range boundary rather than printable input text,
3444    /// even when an action interval explicitly stops at the EOF token.
3445    pub fn text_interval(&mut self, start: usize, stop: Option<usize>) -> String {
3446        let Some(stop) = stop else {
3447            return String::new();
3448        };
3449        let stop = if self
3450            .token_at(stop)
3451            .is_some_and(|token| token.token_type() == TOKEN_EOF)
3452        {
3453            let Some(previous) = self.previous_token_index(stop) else {
3454                return String::new();
3455            };
3456            previous
3457        } else {
3458            stop
3459        };
3460        self.input.text(start, stop)
3461    }
3462
3463    /// Resets per-parse prediction diagnostics while keeping the parser-level
3464    /// reporting flag configured by generated harness code.
3465    fn clear_prediction_diagnostics(&mut self) {
3466        self.prediction_diagnostics.clear();
3467        self.reported_prediction_diagnostics.clear();
3468    }
3469
3470    /// Buffers ANTLR-style diagnostic-listener messages for decision states
3471    /// where multiple clean alternatives survive full-context recognition.
3472    fn record_prediction_diagnostics(
3473        &mut self,
3474        atn: &Atn,
3475        state: &AtnState,
3476        start_index: usize,
3477        outcomes: &[RecognizeOutcome],
3478    ) {
3479        if !self.report_diagnostic_errors || state.transitions.len() < 2 {
3480            return;
3481        }
3482        let Some(decision) = atn
3483            .decision_to_state()
3484            .iter()
3485            .position(|state_number| *state_number == state.state_number)
3486        else {
3487            return;
3488        };
3489        let Some(rule_index) = state.rule_index else {
3490            return;
3491        };
3492        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
3493        for outcome in outcomes
3494            .iter()
3495            .filter(|outcome| outcome.diagnostics.is_empty())
3496        {
3497            let Some(alt) = outcome.decisions.first() else {
3498                continue;
3499            };
3500            alts_by_end
3501                .entry(outcome.index)
3502                .or_default()
3503                .insert(alt + 1);
3504        }
3505        let Some((&end_index, ambig_alts)) = alts_by_end
3506            .iter()
3507            .filter(|(_, alts)| alts.len() > 1)
3508            .max_by_key(|(end, _)| *end)
3509        else {
3510            return;
3511        };
3512        let rule_name = self
3513            .rule_names()
3514            .get(rule_index)
3515            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
3516        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
3517        let input = display_input_text(&self.input.text(start_index, stop_index));
3518        let alts = ambig_alts
3519            .iter()
3520            .map(usize::to_string)
3521            .collect::<Vec<_>>()
3522            .join(", ");
3523        let key = (decision, start_index, format!("{alts}:{input}"));
3524        if !self.reported_prediction_diagnostics.insert(key) {
3525            return;
3526        }
3527        let start_token = self.token_at(start_index);
3528        let stop_token = self.token_at(stop_index);
3529        self.prediction_diagnostics.push(diagnostic_for_token(
3530            start_token.as_ref(),
3531            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
3532        ));
3533        self.prediction_diagnostics.push(diagnostic_for_token(
3534            stop_token.as_ref(),
3535            format!(
3536                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
3537            ),
3538        ));
3539    }
3540
3541    /// Formats the tokens expected from an ATN state using ANTLR display names.
3542    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
3543        expected_symbols_display(
3544            &state_expected_symbols(atn, state_number),
3545            self.vocabulary(),
3546        )
3547    }
3548
3549    /// Formats a buffered token in ANTLR's diagnostic token display form.
3550    pub fn token_display_at(&mut self, index: usize) -> Option<String> {
3551        self.token_at(index).map(|token| format!("{token}"))
3552    }
3553
3554    /// Converts a recognized internal node into a public parse-tree node.
3555    fn recognized_node_tree(
3556        &mut self,
3557        node: &RecognizedNode,
3558        track_alt_numbers: bool,
3559    ) -> Result<ParseTree, AntlrError> {
3560        match node {
3561            RecognizedNode::Token { index } => {
3562                let token =
3563                    self.input
3564                        .get(*index)
3565                        .cloned()
3566                        .ok_or_else(|| AntlrError::ParserError {
3567                            line: 0,
3568                            column: 0,
3569                            message: format!("missing token at index {index}"),
3570                        })?;
3571                Ok(ParseTree::Terminal(TerminalNode::new(token)))
3572            }
3573            RecognizedNode::ErrorToken { index } => {
3574                let token =
3575                    self.input
3576                        .get(*index)
3577                        .cloned()
3578                        .ok_or_else(|| AntlrError::ParserError {
3579                            line: 0,
3580                            column: 0,
3581                            message: format!("missing error token at index {index}"),
3582                        })?;
3583                Ok(ParseTree::Error(ErrorNode::new(token)))
3584            }
3585            RecognizedNode::MissingToken {
3586                token_type,
3587                at_index,
3588                text,
3589            } => {
3590                let current = self.token_at(*at_index);
3591                let token = CommonToken::new(*token_type)
3592                    .with_text(text)
3593                    .with_span(usize::MAX, usize::MAX)
3594                    .with_position(
3595                        current.as_ref().map(Token::line).unwrap_or_default(),
3596                        current.as_ref().map(Token::column).unwrap_or_default(),
3597                    );
3598                Ok(ParseTree::Error(ErrorNode::new(token)))
3599            }
3600            RecognizedNode::Rule {
3601                rule_index,
3602                invoking_state,
3603                alt_number,
3604                start_index,
3605                stop_index,
3606                return_values,
3607                children,
3608            } => {
3609                let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
3610                if track_alt_numbers {
3611                    context.set_alt_number(*alt_number);
3612                }
3613                for (name, value) in return_values {
3614                    context.set_int_return(name.clone(), *value);
3615                }
3616                if let Some(token) = self.token_at(*start_index) {
3617                    context.set_start(token);
3618                }
3619                if let Some(token) = stop_index.and_then(|index| self.token_at(index)) {
3620                    context.set_stop(token);
3621                }
3622                for child in children {
3623                    context.add_child(self.recognized_node_tree(child, track_alt_numbers)?);
3624                }
3625                Ok(self.rule_node(context))
3626            }
3627            RecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
3628                format!("unfolded left-recursive boundary for rule {rule_index}"),
3629            )),
3630        }
3631    }
3632}
3633
3634/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
3635/// transformed left-recursive rule.
3636fn left_recursive_boundary(atn: &Atn, state: &AtnState, target: usize) -> Option<usize> {
3637    if !state.precedence_rule_decision {
3638        return None;
3639    }
3640    let target_state = atn.state(target)?;
3641    if target_state.kind == AtnStateKind::LoopEnd {
3642        return None;
3643    }
3644    state.rule_index
3645}
3646
3647/// Selects the first outer alternative observed for a rule path.
3648///
3649/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
3650/// outer decision. The metadata recognizer only needs this when a generated
3651/// grammar opts into that target template; otherwise the value remains `0` and
3652/// parse-tree rendering is unchanged.
3653const fn next_alt_number(
3654    state: &AtnState,
3655    transition_index: usize,
3656    current_alt_number: usize,
3657    track_alt_numbers: bool,
3658) -> usize {
3659    if !track_alt_numbers || current_alt_number != 0 || state.transitions.len() <= 1 {
3660        return current_alt_number;
3661    }
3662    if matches!(
3663        state.kind,
3664        AtnStateKind::Basic
3665            | AtnStateKind::BlockStart
3666            | AtnStateKind::PlusBlockStart
3667            | AtnStateKind::StarBlockStart
3668            | AtnStateKind::StarLoopEntry
3669    ) && !state.precedence_rule_decision
3670    {
3671        return transition_index + 1;
3672    }
3673    current_alt_number
3674}
3675
3676/// Folds boundary markers emitted at precedence-loop entries into nested rule
3677/// nodes, matching ANTLR's recursive-context parse-tree shape.
3678fn fold_left_recursive_boundaries(nodes: Vec<RecognizedNode>) -> Vec<RecognizedNode> {
3679    let mut folded = Vec::new();
3680    for node in nodes {
3681        match node {
3682            RecognizedNode::LeftRecursiveBoundary { rule_index } => {
3683                if !folded.is_empty() {
3684                    let children = std::mem::take(&mut folded);
3685                    let start_index = recognized_nodes_start_index(&children).unwrap_or_default();
3686                    let stop_index = recognized_nodes_stop_index(&children);
3687                    folded.push(RecognizedNode::Rule {
3688                        rule_index,
3689                        invoking_state: -1,
3690                        alt_number: 0,
3691                        start_index,
3692                        stop_index,
3693                        return_values: BTreeMap::new(),
3694                        children,
3695                    });
3696                }
3697            }
3698            node => folded.push(node),
3699        }
3700    }
3701    folded
3702}
3703
3704/// Mirrors [`fold_left_recursive_boundaries`] for [`FastRecognizedNode`].
3705fn fold_fast_left_recursive_boundaries(
3706    nodes: Vec<Rc<FastRecognizedNode>>,
3707) -> Vec<Rc<FastRecognizedNode>> {
3708    let mut folded: Vec<Rc<FastRecognizedNode>> = Vec::new();
3709    for node in nodes {
3710        match node.as_ref() {
3711            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
3712                if !folded.is_empty() {
3713                    let children = std::mem::take(&mut folded);
3714                    let start_index =
3715                        fast_recognized_nodes_start_index(&children).unwrap_or_default();
3716                    let stop_index = fast_recognized_nodes_stop_index(&children);
3717                    folded.push(Rc::new(FastRecognizedNode::Rule {
3718                        rule_index: *rule_index,
3719                        invoking_state: -1,
3720                        start_index,
3721                        stop_index,
3722                        children,
3723                    }));
3724                }
3725            }
3726            _ => folded.push(node),
3727        }
3728    }
3729    folded
3730}
3731
3732fn fast_recognized_nodes_start_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
3733    nodes
3734        .iter()
3735        .find_map(|node| fast_recognized_node_start_index(node.as_ref()))
3736}
3737
3738const fn fast_recognized_node_start_index(node: &FastRecognizedNode) -> Option<usize> {
3739    match node {
3740        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
3741            Some(*index)
3742        }
3743        FastRecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
3744        FastRecognizedNode::Rule { start_index, .. } => Some(*start_index),
3745        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
3746    }
3747}
3748
3749fn fast_recognized_nodes_stop_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
3750    nodes
3751        .iter()
3752        .rev()
3753        .find_map(|node| fast_recognized_node_stop_index(node.as_ref()))
3754}
3755
3756const fn fast_recognized_node_stop_index(node: &FastRecognizedNode) -> Option<usize> {
3757    match node {
3758        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
3759            Some(*index)
3760        }
3761        FastRecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
3762        FastRecognizedNode::Rule { stop_index, .. } => *stop_index,
3763        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
3764    }
3765}
3766
3767fn recognized_nodes_start_index(nodes: &[RecognizedNode]) -> Option<usize> {
3768    nodes.iter().find_map(recognized_node_start_index)
3769}
3770
3771const fn recognized_node_start_index(node: &RecognizedNode) -> Option<usize> {
3772    match node {
3773        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
3774        RecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
3775        RecognizedNode::Rule { start_index, .. } => Some(*start_index),
3776        RecognizedNode::LeftRecursiveBoundary { .. } => None,
3777    }
3778}
3779
3780fn recognized_nodes_stop_index(nodes: &[RecognizedNode]) -> Option<usize> {
3781    nodes.iter().rev().find_map(recognized_node_stop_index)
3782}
3783
3784/// Converts an ATN state number into the signed invoking-state slot used by
3785/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
3786fn invoking_state_number(state_number: usize) -> isize {
3787    isize::try_from(state_number).unwrap_or(isize::MAX)
3788}
3789
3790const fn recognized_node_stop_index(node: &RecognizedNode) -> Option<usize> {
3791    match node {
3792        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
3793        RecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
3794        RecognizedNode::Rule { stop_index, .. } => *stop_index,
3795        RecognizedNode::LeftRecursiveBoundary { .. } => None,
3796    }
3797}
3798
3799fn token_input_display(token: &impl Token) -> String {
3800    format!("'{}'", token.text().unwrap_or("<EOF>"))
3801}
3802
3803fn display_input_text(text: &str) -> String {
3804    let mut out = String::new();
3805    for ch in text.chars() {
3806        match ch {
3807            '\n' => out.push_str("\\n"),
3808            '\r' => out.push_str("\\r"),
3809            '\t' => out.push_str("\\t"),
3810            other => out.push(other),
3811        }
3812    }
3813    out
3814}
3815
3816fn diagnostic_for_token(token: Option<&impl Token>, message: String) -> ParserDiagnostic {
3817    ParserDiagnostic {
3818        line: token.map(Token::line).unwrap_or_default(),
3819        column: token.map(Token::column).unwrap_or_default(),
3820        message,
3821    }
3822}
3823
3824/// Emits parser diagnostics for the selected recovered parse path.
3825#[allow(clippy::print_stderr)]
3826fn report_parser_diagnostics(diagnostics: &[ParserDiagnostic]) {
3827    for diagnostic in diagnostics {
3828        eprintln!(
3829            "line {}:{} {}",
3830            diagnostic.line, diagnostic.column, diagnostic.message
3831        );
3832    }
3833}
3834
3835/// Emits buffered token-source diagnostics after parser diagnostics that were
3836/// discovered while speculatively reading the same token stream.
3837#[allow(clippy::print_stderr)]
3838fn report_token_source_errors(errors: &[TokenSourceError]) {
3839    for error in errors {
3840        eprintln!("line {}:{} {}", error.line, error.column, error.message);
3841    }
3842}
3843
3844fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
3845    let items = symbols
3846        .iter()
3847        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
3848        .collect::<Vec<_>>();
3849    if let [single] = items.as_slice() {
3850        return single.clone();
3851    }
3852    format!("{{{}}}", items.join(", "))
3853}
3854
3855fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
3856    if symbol == TOKEN_EOF {
3857        return "<EOF>".to_owned();
3858    }
3859    vocabulary.display_name(symbol)
3860}
3861
3862/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
3863/// Inline insertion in those precedence loops can synthesize a missing operand
3864/// before an operator and then block the legitimate loop-exit path.
3865fn state_is_left_recursive_rule(atn: &Atn, state: &AtnState) -> bool {
3866    let Some(rule_index) = state.rule_index else {
3867        return false;
3868    };
3869    atn.rule_to_start_state()
3870        .get(rule_index)
3871        .and_then(|state_number| atn.state(*state_number))
3872        .is_some_and(|rule_start| rule_start.left_recursive_rule)
3873}
3874
3875/// Chooses the outermost parse result that consumed the most input.
3876///
3877/// The recognizer intentionally keeps shorter endpoints available while walking
3878/// Picks the better of two `parse_atn_rule` passes (with and without the
3879/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
3880/// recovered one; among recovered outcomes the second pass is preferred
3881/// because the no-prefilter walk reaches ANTLR-style recovery inside child
3882/// rules. If both passes failed, the second pass's expected-token snapshot
3883/// is returned so the caller renders the same diagnostic ANTLR would.
3884fn select_better_top_outcome(
3885    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
3886    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
3887) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
3888    match (first, second) {
3889        (Ok(first), Ok(second)) => {
3890            if first.0.diagnostics.is_empty() {
3891                Ok(first)
3892            } else {
3893                Ok(second)
3894            }
3895        }
3896        (Ok(first), Err(_)) => Ok(first),
3897        (Err(_), Ok(second)) => Ok(second),
3898        (Err(_), Err(second_expected)) => Err(second_expected),
3899    }
3900}
3901
3902/// nested rule transitions so callers can satisfy following tokens such as
3903/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
3904fn select_best_fast_outcome(
3905    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
3906    prediction_mode: PredictionMode,
3907) -> Option<FastRecognizeOutcome> {
3908    outcomes.reduce(|best, outcome| {
3909        let outcome_position = (outcome.index, outcome.consumed_eof);
3910        let best_position = (best.index, best.consumed_eof);
3911        let better = match prediction_mode {
3912            PredictionMode::Ll => outcome_is_better(
3913                outcome_position,
3914                &outcome.diagnostics,
3915                best_position,
3916                &best.diagnostics,
3917            ),
3918            PredictionMode::Sll => outcome.index > best.index,
3919        };
3920        if better {
3921            return outcome;
3922        }
3923        best
3924    })
3925}
3926
3927fn select_best_outcome(
3928    outcomes: impl Iterator<Item = RecognizeOutcome>,
3929    prediction_mode: PredictionMode,
3930) -> Option<RecognizeOutcome> {
3931    let outcomes = outcomes.collect::<Vec<_>>();
3932    let prefer_first_tie = outcomes
3933        .iter()
3934        .any(|outcome| nodes_need_stable_tie(&outcome.nodes));
3935    outcomes.into_iter().reduce(|best, outcome| {
3936        let outcome_position = (outcome.index, outcome.consumed_eof);
3937        let best_position = (best.index, best.consumed_eof);
3938        let better = match prediction_mode {
3939            PredictionMode::Ll => {
3940                outcome_is_better(
3941                    outcome_position,
3942                    &outcome.diagnostics,
3943                    best_position,
3944                    &best.diagnostics,
3945                ) || (!prefer_first_tie
3946                    && outcome_position == best_position
3947                    && outcome.diagnostics.len() == best.diagnostics.len()
3948                    && diagnostic_recovery_rank(&outcome.diagnostics)
3949                        == diagnostic_recovery_rank(&best.diagnostics)
3950                    && (outcome.decisions < best.decisions
3951                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
3952            }
3953            PredictionMode::Sll => {
3954                outcome_position > best_position
3955                    || (outcome_position == best_position
3956                        && !prefer_first_tie
3957                        && (outcome.decisions < best.decisions
3958                            || (outcome.decisions == best.decisions
3959                                && outcome_is_better(
3960                                    outcome_position,
3961                                    &outcome.diagnostics,
3962                                    best_position,
3963                                    &best.diagnostics,
3964                                ))))
3965            }
3966        };
3967        if better {
3968            return outcome;
3969        }
3970        best
3971    })
3972}
3973
3974/// Records the serialized transition order at parser decision states.
3975///
3976/// When two clean paths consume the same input, ANTLR's adaptive prediction
3977/// chooses by alternative order. Keeping this compact trace lets the metadata
3978/// recognizer distinguish greedy and non-greedy optional blocks without a full
3979/// prediction simulator.
3980fn transition_decision(
3981    atn: &Atn,
3982    state: &AtnState,
3983    transition_index: usize,
3984    predicates: &[(usize, usize, ParserPredicate)],
3985) -> Option<usize> {
3986    if state.transitions.len() <= 1
3987        || state.precedence_rule_decision
3988        || decision_reaches_unsupported_predicate(atn, state, predicates)
3989    {
3990        return None;
3991    }
3992    Some(transition_index)
3993}
3994
3995/// Reports whether a state should reset the active no-viable decision start.
3996///
3997/// Loop entry/back states are continuations of the surrounding adaptive
3998/// prediction; resetting at those states would turn LL-star failures back into
3999/// ordinary mismatches.
4000const fn starts_prediction_decision(state: &AtnState) -> bool {
4001    state.transitions.len() > 1
4002        && !matches!(
4003            state.kind,
4004            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
4005        )
4006}
4007
4008/// Marks a farthest expected-token set as no-viable when multiple alternatives
4009/// failed after the active decision had already consumed input.
4010fn record_no_viable_if_ambiguous(
4011    expected: &mut ExpectedTokens,
4012    decision_start_index: Option<usize>,
4013    index: usize,
4014) {
4015    if expected.index == Some(index) && expected.symbols.len() > 1 {
4016        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
4017            expected.record_no_viable(decision_start, index);
4018        }
4019    }
4020}
4021
4022/// Records a no-viable decision caused by a failed semantic predicate before
4023/// any consuming transition can contribute an expected-token set.
4024const fn record_predicate_no_viable(
4025    expected: &mut ExpectedTokens,
4026    decision_start_index: Option<usize>,
4027    index: usize,
4028) {
4029    if let Some(decision_start) = decision_start_index {
4030        expected.record_no_viable(decision_start, index);
4031    }
4032}
4033
4034/// Returns the active decision start only when the error is past that start.
4035const fn no_viable_decision_start(
4036    decision_start_index: Option<usize>,
4037    index: usize,
4038) -> Option<usize> {
4039    match decision_start_index {
4040        Some(start) if index > start => Some(start),
4041        _ => None,
4042    }
4043}
4044
4045/// Restores expected-token bookkeeping when a child rule found a clean
4046/// consuming path; failures in longer child alternatives should not pollute the
4047/// caller's final expectation set.
4048fn restore_expected(
4049    children: &[RecognizeOutcome],
4050    child_start_index: usize,
4051    expected: &mut ExpectedTokens,
4052    snapshot: ExpectedTokens,
4053    preserve_child_expected: bool,
4054) {
4055    if preserve_child_expected {
4056        return;
4057    }
4058    if children
4059        .iter()
4060        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
4061    {
4062        *expected = snapshot;
4063    }
4064}
4065
4066/// Reports whether a decision can reach a predicate the generator did not
4067/// translate. Static alternative order is unsafe for those context predicates.
4068fn decision_reaches_unsupported_predicate(
4069    atn: &Atn,
4070    state: &AtnState,
4071    predicates: &[(usize, usize, ParserPredicate)],
4072) -> bool {
4073    state.transitions.iter().any(|transition| {
4074        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
4075    })
4076}
4077
4078/// Walks epsilon-like edges from one transition to find unsupported predicates.
4079fn transition_reaches_unsupported_predicate(
4080    atn: &Atn,
4081    transition: &Transition,
4082    predicates: &[(usize, usize, ParserPredicate)],
4083    visited: &mut BTreeSet<usize>,
4084) -> bool {
4085    match transition {
4086        Transition::Predicate {
4087            rule_index,
4088            pred_index,
4089            ..
4090        } => !predicates
4091            .iter()
4092            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
4093        Transition::Epsilon { target }
4094        | Transition::Action { target, .. }
4095        | Transition::Rule { target, .. } => {
4096            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
4097        }
4098        Transition::Precedence { .. }
4099        | Transition::Atom { .. }
4100        | Transition::Range { .. }
4101        | Transition::Set { .. }
4102        | Transition::NotSet { .. }
4103        | Transition::Wildcard { .. } => false,
4104    }
4105}
4106
4107/// Finds an unsupported predicate reachable before a consuming transition.
4108fn state_reaches_unsupported_predicate(
4109    atn: &Atn,
4110    state_number: usize,
4111    predicates: &[(usize, usize, ParserPredicate)],
4112    visited: &mut BTreeSet<usize>,
4113) -> bool {
4114    if !visited.insert(state_number) {
4115        return false;
4116    }
4117    let Some(state) = atn.state(state_number) else {
4118        return false;
4119    };
4120    state.transitions.iter().any(|transition| {
4121        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
4122    })
4123}
4124
4125/// Adds a decision step to the front of an already-recognized suffix path.
4126fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
4127    if let Some(decision) = decision {
4128        outcome.decisions.insert(0, decision);
4129    }
4130}
4131
4132fn outcome_is_better(
4133    outcome_position: (usize, bool),
4134    outcome_diagnostics: &[ParserDiagnostic],
4135    best_position: (usize, bool),
4136    best_diagnostics: &[ParserDiagnostic],
4137) -> bool {
4138    outcome_position > best_position
4139        || (outcome_position == best_position
4140            && (outcome_diagnostics.len() < best_diagnostics.len()
4141                || (outcome_diagnostics.len() == best_diagnostics.len()
4142                    && diagnostic_recovery_rank(outcome_diagnostics)
4143                        < diagnostic_recovery_rank(best_diagnostics))))
4144}
4145
4146/// Ranks concrete recovery repairs ahead of generic non-EOF mismatch fallbacks
4147/// when speculative paths otherwise consume the same input.
4148fn diagnostic_recovery_rank(diagnostics: &[ParserDiagnostic]) -> usize {
4149    diagnostics
4150        .iter()
4151        .filter(|diagnostic| {
4152            diagnostic.message.starts_with("mismatched input ")
4153                && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
4154        })
4155        .count()
4156}
4157
4158fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
4159    if outcomes
4160        .iter()
4161        .any(|outcome| outcome.diagnostics.is_empty())
4162    {
4163        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
4164    }
4165}
4166
4167fn discard_recovered_outcomes_if_clean_path_exists(outcomes: &mut Vec<RecognizeOutcome>) {
4168    if outcomes.iter().any(outcome_has_rule_failure_diagnostic) {
4169        return;
4170    }
4171    if outcomes
4172        .iter()
4173        .any(|outcome| outcome.diagnostics.is_empty())
4174    {
4175        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
4176    }
4177}
4178
4179/// Reports whether a recovered outcome came from an explicit predicate
4180/// fail-option and therefore should compete with shorter clean loop exits.
4181fn outcome_has_rule_failure_diagnostic(outcome: &RecognizeOutcome) -> bool {
4182    outcome
4183        .diagnostics
4184        .iter()
4185        .any(|diagnostic| diagnostic.message.starts_with("rule "))
4186}
4187
4188/// Reports whether a candidate contains recursive tree structure where ANTLR's
4189/// first viable candidate preserves the correct left-recursive context shape.
4190fn nodes_need_stable_tie(nodes: &[RecognizedNode]) -> bool {
4191    nodes.iter().any(node_needs_stable_tie)
4192}
4193
4194fn node_needs_stable_tie(node: &RecognizedNode) -> bool {
4195    match node {
4196        RecognizedNode::Token { .. }
4197        | RecognizedNode::ErrorToken { .. }
4198        | RecognizedNode::MissingToken { .. } => false,
4199        RecognizedNode::LeftRecursiveBoundary { .. } => true,
4200        RecognizedNode::Rule {
4201            rule_index,
4202            children,
4203            ..
4204        } => children.iter().any(|child| {
4205            matches!(
4206                child,
4207                RecognizedNode::Rule {
4208                    rule_index: child_rule,
4209                    ..
4210                } if child_rule == rule_index
4211            ) || node_needs_stable_tie(child)
4212        }),
4213    }
4214}
4215
4216/// Removes equivalent endpoints before memoizing a state result while
4217/// preserving ATN transition-discovery order.
4218///
4219/// Outcomes are compared on observable recognition state — the input index,
4220/// EOF consumption, and diagnostics — without descending into the parse-tree
4221/// fragment carried by `nodes`. Two paths reaching the same point with
4222/// different node trees would otherwise prevent memoization from collapsing
4223/// equivalent suffixes and explode the speculative-path cache.
4224///
4225/// The first occurrence per recognition key wins, which matches ANTLR's
4226/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
4227/// transitions before loop-exit, so the first-discovered outcome carries the
4228/// greedy parse-tree fragment.
4229fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
4230    if outcomes.len() < 2 {
4231        return;
4232    }
4233    let mut keep = Vec::with_capacity(outcomes.len());
4234    let mut seen: BTreeMap<(usize, bool), Vec<usize>> = BTreeMap::new();
4235    'outcomes: for (index, outcome) in outcomes.iter().enumerate() {
4236        let bucket = seen
4237            .entry((outcome.index, outcome.consumed_eof))
4238            .or_default();
4239        for &previous in bucket.iter() {
4240            if outcomes[previous].diagnostics == outcome.diagnostics {
4241                continue 'outcomes;
4242            }
4243        }
4244        bucket.push(index);
4245        keep.push(index);
4246    }
4247    if keep.len() == outcomes.len() {
4248        return;
4249    }
4250    let mut iter = keep.into_iter();
4251    let mut next_keep = iter.next();
4252    let mut current = 0_usize;
4253    outcomes.retain(|_| {
4254        let result = next_keep == Some(current);
4255        if result {
4256            next_keep = iter.next();
4257        }
4258        current += 1;
4259        result
4260    });
4261}
4262
4263/// Sorts and removes equivalent endpoints, including their action traces.
4264fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>) {
4265    outcomes.sort_unstable();
4266    outcomes.dedup();
4267}
4268
4269impl<S> Recognizer for BaseParser<S>
4270where
4271    S: TokenSource,
4272{
4273    fn data(&self) -> &RecognizerData {
4274        &self.data
4275    }
4276
4277    fn data_mut(&mut self) -> &mut RecognizerData {
4278        &mut self.data
4279    }
4280}
4281
4282impl<S> Parser for BaseParser<S>
4283where
4284    S: TokenSource,
4285{
4286    fn build_parse_trees(&self) -> bool {
4287        self.build_parse_trees
4288    }
4289
4290    fn set_build_parse_trees(&mut self, build: bool) {
4291        self.build_parse_trees = build;
4292    }
4293
4294    fn report_diagnostic_errors(&self) -> bool {
4295        self.report_diagnostic_errors
4296    }
4297
4298    fn set_report_diagnostic_errors(&mut self, report: bool) {
4299        self.report_diagnostic_errors = report;
4300    }
4301
4302    fn prediction_mode(&self) -> PredictionMode {
4303        self.prediction_mode
4304    }
4305
4306    fn set_prediction_mode(&mut self, mode: PredictionMode) {
4307        self.prediction_mode = mode;
4308    }
4309}
4310
4311#[cfg(test)]
4312mod tests {
4313    use super::*;
4314    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
4315    use crate::token::{CommonToken, HIDDEN_CHANNEL, Token};
4316    use crate::token_stream::CommonTokenStream;
4317    use crate::vocabulary::Vocabulary;
4318
4319    #[derive(Debug)]
4320    struct Source {
4321        tokens: Vec<CommonToken>,
4322        index: usize,
4323    }
4324
4325    impl TokenSource for Source {
4326        fn next_token(&mut self) -> CommonToken {
4327            let token = self
4328                .tokens
4329                .get(self.index)
4330                .cloned()
4331                .unwrap_or_else(|| CommonToken::eof("parser-test", self.index, 1, self.index));
4332            self.index += 1;
4333            token
4334        }
4335
4336        fn line(&self) -> usize {
4337            1
4338        }
4339
4340        fn column(&self) -> usize {
4341            self.index
4342        }
4343
4344        fn source_name(&self) -> &'static str {
4345            "parser-test"
4346        }
4347    }
4348
4349    fn mini_parser(tokens: Vec<CommonToken>) -> BaseParser<Source> {
4350        let data = RecognizerData::new(
4351            "Mini.g4",
4352            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
4353        );
4354        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
4355    }
4356
4357    fn token_then_eof_atn() -> Atn {
4358        AtnDeserializer::new(&SerializedAtn::from_i32(&[
4359            4, 1, 2, // version, parser, max token type
4360            3, // states
4361            2, 0, // rule start
4362            1, 0, // basic
4363            7, 0, // rule stop
4364            0, // non-greedy states
4365            0, // precedence states
4366            1, // rules
4367            0, // rule 0 start
4368            0, // modes
4369            0, // sets
4370            2, // transitions
4371            0, 1, 5, 1, 0, 0, // match token 1
4372            1, 2, 5, -1, 0, 0, // match EOF
4373            0, // decisions
4374        ]))
4375        .deserialize()
4376        .expect("artificial parser ATN should deserialize")
4377    }
4378
4379    fn eof_then_action_atn() -> Atn {
4380        AtnDeserializer::new(&SerializedAtn::from_i32(&[
4381            4, 1, 1, // version, parser, max token type
4382            3, // states
4383            2, 0, // rule start
4384            1, 0, // basic
4385            7, 0, // rule stop
4386            0, // non-greedy states
4387            0, // precedence states
4388            1, // rules
4389            0, // rule 0 start
4390            0, // modes
4391            0, // sets
4392            2, // transitions
4393            0, 1, 5, -1, 0, 0, // match EOF
4394            1, 2, 6, 0, 0, 0, // parser action
4395            0, // decisions
4396        ]))
4397        .deserialize()
4398        .expect("artificial parser ATN should deserialize")
4399    }
4400
4401    #[test]
4402    fn parser_matches_token_and_reports_mismatch() {
4403        let source = Source {
4404            tokens: vec![
4405                CommonToken::new(1).with_text("x"),
4406                CommonToken::eof("parser-test", 1, 1, 1),
4407            ],
4408            index: 0,
4409        };
4410        let data = RecognizerData::new(
4411            "Mini.g4",
4412            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
4413        );
4414        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
4415        assert_eq!(
4416            parser.match_token(1).expect("token 1 should match").text(),
4417            "x"
4418        );
4419        assert!(parser.match_token(1).is_err());
4420    }
4421
4422    #[test]
4423    fn parser_interprets_simple_atn_rule() {
4424        let atn = token_then_eof_atn();
4425        let mut parser = mini_parser(vec![
4426            CommonToken::new(1).with_text("x"),
4427            CommonToken::eof("parser-test", 1, 1, 1),
4428        ]);
4429
4430        let tree = parser
4431            .parse_atn_rule(&atn, 0)
4432            .expect("artificial parser rule should parse");
4433        assert_eq!(tree.text(), "x<EOF>");
4434        assert_eq!(
4435            tree.first_rule_stop(0)
4436                .expect("rule should stop at EOF")
4437                .token_type(),
4438            TOKEN_EOF
4439        );
4440
4441        let mut parser = mini_parser(vec![
4442            CommonToken::new(1).with_text("x"),
4443            CommonToken::eof("parser-test", 1, 1, 1),
4444        ]);
4445        let (tree, actions) = parser
4446            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
4447            .expect("runtime-option parser rule should parse");
4448        assert!(actions.is_empty());
4449        assert_eq!(
4450            tree.first_rule_stop(0)
4451                .expect("rule should stop at EOF")
4452                .token_type(),
4453            TOKEN_EOF
4454        );
4455    }
4456
4457    #[test]
4458    fn parser_rule_start_skips_leading_hidden_tokens() {
4459        let atn = token_then_eof_atn();
4460        let mut parser = mini_parser(vec![
4461            CommonToken::new(99)
4462                .with_text(" ")
4463                .with_channel(HIDDEN_CHANNEL),
4464            CommonToken::new(1).with_text("x"),
4465            CommonToken::eof("parser-test", 2, 1, 2),
4466        ]);
4467
4468        let tree = parser
4469            .parse_atn_rule(&atn, 0)
4470            .expect("artificial parser rule should parse");
4471        let Some(ParseTree::Rule(rule)) = tree.first_rule(0) else {
4472            panic!("rule node should be present");
4473        };
4474        assert_eq!(
4475            rule.context()
4476                .start()
4477                .expect("rule should have a start token")
4478                .token_type(),
4479            1
4480        );
4481    }
4482
4483    #[test]
4484    fn parser_action_after_eof_stops_at_eof_token() {
4485        let atn = eof_then_action_atn();
4486        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
4487
4488        let (_, actions) = parser
4489            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
4490            .expect("EOF action rule should parse");
4491
4492        assert_eq!(actions.len(), 1);
4493        assert_eq!(actions[0].stop_index(), Some(0));
4494        assert_eq!(
4495            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
4496            ""
4497        );
4498    }
4499
4500    #[test]
4501    fn fast_outcome_selection_respects_sll_tie_order() {
4502        let first = FastRecognizeOutcome {
4503            index: 1,
4504            consumed_eof: false,
4505            diagnostics: vec![ParserDiagnostic {
4506                line: 1,
4507                column: 0,
4508                message: "mismatched input 'x'".to_owned(),
4509            }],
4510            nodes: NodeList::new(),
4511        };
4512        let second = FastRecognizeOutcome {
4513            index: first.index,
4514            consumed_eof: first.consumed_eof,
4515            diagnostics: Vec::new(),
4516            nodes: NodeList::new(),
4517        };
4518
4519        let selected = select_best_fast_outcome(
4520            [first.clone(), second.clone()].into_iter(),
4521            PredictionMode::Sll,
4522        )
4523        .expect("one outcome should be selected");
4524        assert_eq!(selected.diagnostics.len(), 1);
4525        let eof_second = FastRecognizeOutcome {
4526            index: second.index,
4527            consumed_eof: true,
4528            diagnostics: Vec::new(),
4529            nodes: NodeList::new(),
4530        };
4531        let selected =
4532            select_best_fast_outcome([first.clone(), eof_second].into_iter(), PredictionMode::Sll)
4533                .expect("one outcome should be selected");
4534        assert!(!selected.consumed_eof);
4535        let selected = select_best_fast_outcome([first, second].into_iter(), PredictionMode::Ll)
4536            .expect("one outcome should be selected");
4537        assert!(selected.diagnostics.is_empty());
4538    }
4539
4540    #[test]
4541    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
4542        let source = Source {
4543            tokens: vec![
4544                CommonToken::new(1).with_text("x"),
4545                CommonToken::eof("parser-test", 1, 1, 1),
4546            ],
4547            index: 0,
4548        };
4549        let data = RecognizerData::new(
4550            "Mini.g4",
4551            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
4552        );
4553        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
4554        let expected = ExpectedTokens {
4555            index: Some(0),
4556            symbols: BTreeSet::new(),
4557            no_viable: None,
4558        };
4559
4560        let (_, message) = parser.expected_error_message(0, 0, &expected);
4561
4562        assert_eq!(message, "mismatched input 'x'");
4563    }
4564
4565    #[test]
4566    fn eof_rule_stop_index_points_at_eof_token() {
4567        let source = Source {
4568            tokens: vec![
4569                CommonToken::new(1).with_text("x"),
4570                CommonToken::eof("parser-test", 1, 1, 1),
4571            ],
4572            index: 0,
4573        };
4574        let data = RecognizerData::new(
4575            "Mini.g4",
4576            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
4577        );
4578        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
4579
4580        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
4581        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
4582    }
4583
4584    #[test]
4585    fn folds_left_recursive_boundary_into_rule_node() {
4586        let nodes = fold_left_recursive_boundaries(vec![
4587            RecognizedNode::Token { index: 0 },
4588            RecognizedNode::LeftRecursiveBoundary { rule_index: 1 },
4589            RecognizedNode::Token { index: 1 },
4590        ]);
4591
4592        assert_eq!(
4593            nodes,
4594            vec![
4595                RecognizedNode::Rule {
4596                    rule_index: 1,
4597                    invoking_state: -1,
4598                    alt_number: 0,
4599                    start_index: 0,
4600                    stop_index: Some(0),
4601                    return_values: BTreeMap::new(),
4602                    children: vec![RecognizedNode::Token { index: 0 }],
4603                },
4604                RecognizedNode::Token { index: 1 },
4605            ]
4606        );
4607    }
4608
4609    #[test]
4610    fn outcome_ties_keep_later_non_recursive_alternative() {
4611        let first = RecognizeOutcome {
4612            index: 1,
4613            consumed_eof: false,
4614            alt_number: 0,
4615            member_values: BTreeMap::new(),
4616            return_values: BTreeMap::new(),
4617            diagnostics: Vec::new(),
4618            decisions: Vec::new(),
4619            actions: vec![ParserAction::new(1, 0, 0, None)],
4620            nodes: vec![RecognizedNode::Token { index: 0 }],
4621        };
4622        let second = RecognizeOutcome {
4623            actions: vec![ParserAction::new(2, 0, 0, None)],
4624            ..first.clone()
4625        };
4626
4627        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
4628            .expect("one outcome should be selected");
4629        assert_eq!(selected.actions[0].source_state(), 2);
4630    }
4631
4632    #[test]
4633    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
4634        let first = RecognizeOutcome {
4635            index: 1,
4636            consumed_eof: false,
4637            alt_number: 0,
4638            member_values: BTreeMap::new(),
4639            return_values: BTreeMap::new(),
4640            diagnostics: Vec::new(),
4641            decisions: Vec::new(),
4642            actions: vec![ParserAction::new(1, 0, 0, None)],
4643            nodes: vec![RecognizedNode::Token { index: 0 }],
4644        };
4645        let second = RecognizeOutcome {
4646            actions: vec![
4647                ParserAction::new(2, 0, 0, None),
4648                ParserAction::new(3, 0, 0, None),
4649            ],
4650            ..first.clone()
4651        };
4652
4653        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll)
4654            .expect("one outcome should be selected");
4655        assert_eq!(selected.actions.len(), 2);
4656    }
4657
4658    #[test]
4659    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
4660        let first = RecognizeOutcome {
4661            index: 7,
4662            consumed_eof: false,
4663            alt_number: 0,
4664            member_values: BTreeMap::new(),
4665            return_values: BTreeMap::new(),
4666            diagnostics: Vec::new(),
4667            decisions: vec![1, 0],
4668            actions: vec![
4669                ParserAction::new(23, 2, 2, Some(4)),
4670                ParserAction::new(23, 2, 0, Some(6)),
4671            ],
4672            nodes: vec![RecognizedNode::Token { index: 0 }],
4673        };
4674        let second = RecognizeOutcome {
4675            decisions: vec![0, 1],
4676            actions: vec![
4677                ParserAction::new(23, 2, 2, Some(6)),
4678                ParserAction::new(23, 2, 0, Some(6)),
4679            ],
4680            ..first.clone()
4681        };
4682
4683        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
4684            .expect("one outcome should be selected");
4685        assert_eq!(selected.actions[0].stop_index(), Some(6));
4686    }
4687
4688    #[test]
4689    fn outcome_ties_keep_first_recursive_tree_shape() {
4690        let recursive_nodes = vec![RecognizedNode::Rule {
4691            rule_index: 1,
4692            invoking_state: -1,
4693            alt_number: 0,
4694            start_index: 0,
4695            stop_index: Some(0),
4696            return_values: BTreeMap::new(),
4697            children: vec![RecognizedNode::Rule {
4698                rule_index: 1,
4699                invoking_state: -1,
4700                alt_number: 0,
4701                start_index: 0,
4702                stop_index: Some(0),
4703                return_values: BTreeMap::new(),
4704                children: vec![RecognizedNode::Token { index: 0 }],
4705            }],
4706        }];
4707        let first = RecognizeOutcome {
4708            index: 1,
4709            consumed_eof: false,
4710            alt_number: 0,
4711            member_values: BTreeMap::new(),
4712            return_values: BTreeMap::new(),
4713            diagnostics: Vec::new(),
4714            decisions: Vec::new(),
4715            actions: vec![ParserAction::new(1, 0, 0, None)],
4716            nodes: recursive_nodes.clone(),
4717        };
4718        let second = RecognizeOutcome {
4719            index: 1,
4720            consumed_eof: false,
4721            alt_number: 0,
4722            member_values: BTreeMap::new(),
4723            return_values: BTreeMap::new(),
4724            diagnostics: Vec::new(),
4725            decisions: Vec::new(),
4726            actions: vec![ParserAction::new(2, 0, 0, None)],
4727            nodes: recursive_nodes,
4728        };
4729
4730        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
4731            .expect("one outcome should be selected");
4732        assert_eq!(selected.actions[0].source_state(), 1);
4733    }
4734
4735    #[test]
4736    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
4737        let first_alt = RecognizeOutcome {
4738            index: 2,
4739            consumed_eof: true,
4740            alt_number: 0,
4741            member_values: BTreeMap::new(),
4742            return_values: BTreeMap::new(),
4743            diagnostics: vec![ParserDiagnostic {
4744                line: 1,
4745                column: 3,
4746                message: "missing 'Y' at '<EOF>'".to_owned(),
4747            }],
4748            decisions: vec![0],
4749            actions: vec![ParserAction::new(1, 0, 0, None)],
4750            nodes: vec![RecognizedNode::Token { index: 0 }],
4751        };
4752        let second_alt = RecognizeOutcome {
4753            diagnostics: Vec::new(),
4754            decisions: vec![1],
4755            actions: vec![ParserAction::new(2, 0, 0, None)],
4756            ..first_alt.clone()
4757        };
4758
4759        let selected =
4760            select_best_outcome([second_alt, first_alt].into_iter(), PredictionMode::Sll)
4761                .expect("one outcome should be selected");
4762        assert_eq!(selected.diagnostics.len(), 1);
4763        assert_eq!(selected.decisions, [0]);
4764    }
4765}