Skip to main content

antlr4_runtime/
parser.rs

1// `HashMap`/`HashSet` here are used as parser-internal caches keyed on
2// stable ATN coordinates (state numbers, token indices). They're never
3// iterated externally, so the project's `disallowed_types` lint (which
4// guards against non-deterministic iteration order leaking out) does not
5// apply to these uses.
6use std::cell::RefCell;
7#[allow(clippy::disallowed_types)]
8use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
9use std::hash::{BuildHasherDefault, Hash, Hasher};
10use std::rc::Rc;
11
12/// Rotate constant copied from rustc-hash / `FxHash`. The default
13/// `RandomState` hasher seeds itself from the OS RNG and runs `SipHash` on
14/// every key, which dominates `recognize_state_fast`'s memo lookups;
15/// `FxHasher` is a streaming integer hasher with near-zero per-call overhead
16/// and matches the access pattern of small integer keys that the parser memo
17/// uses.
18#[derive(Clone, Copy, Default)]
19struct FxHasher {
20    hash: u64,
21}
22
23const FX_ROT: u32 = 5;
24const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
25
26impl Hasher for FxHasher {
27    /// Folds bytes 8 at a time so a `write(&[u8; 8])` call hashes to the same
28    /// state as a `write_u64` of the same little-endian bits. The `Hash` impls
29    /// for `String`, `[u8; N]`, and slice-like types reach the hasher through
30    /// `write`; matching the typed-method behaviour avoids the silent
31    /// divergence flagged in PR #5 review (Greptile P2). Tail bytes that do
32    /// not form a full word are mixed one at a time with the same constants,
33    /// keeping behaviour deterministic regardless of the slice length.
34    #[inline]
35    fn write(&mut self, mut bytes: &[u8]) {
36        while bytes.len() >= 8 {
37            let (head, rest) = bytes.split_at(8);
38            let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
39            self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
40            bytes = rest;
41        }
42        for byte in bytes {
43            self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
44        }
45    }
46    #[inline]
47    fn write_u64(&mut self, value: u64) {
48        self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
49    }
50    #[inline]
51    fn write_usize(&mut self, value: usize) {
52        self.write_u64(value as u64);
53    }
54    #[inline]
55    fn write_u32(&mut self, value: u32) {
56        self.write_u64(u64::from(value));
57    }
58    #[inline]
59    fn write_i32(&mut self, value: i32) {
60        self.write_u64(u64::from(i32::cast_unsigned(value)));
61    }
62    #[inline]
63    fn finish(&self) -> u64 {
64        self.hash
65    }
66}
67
68type FxBuildHasher = BuildHasherDefault<FxHasher>;
69#[allow(clippy::disallowed_types)]
70type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
71#[allow(clippy::disallowed_types)]
72type FxHashSet<K> = HashSet<K, FxBuildHasher>;
73
74use crate::atn::parser::{
75    ParserAtnPrediction, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
76};
77use crate::atn::{Atn, AtnState, AtnStateKind, Transition};
78use crate::errors::AntlrError;
79use crate::int_stream::IntStream;
80use crate::prediction::{EMPTY_RETURN_STATE, PredictionContext};
81use crate::recognizer::{Recognizer, RecognizerData};
82use crate::token::{CommonToken, TOKEN_EOF, Token, TokenRef, TokenSource, TokenSourceError};
83use crate::token_stream::CommonTokenStream;
84use crate::tree::{ErrorNode, ParseTree, ParserRuleContext, RuleNode, TerminalNode};
85use crate::vocabulary::Vocabulary;
86
87/// Upper bound for the recursive metadata recognizer before it treats a path as
88/// non-viable. Long expression-regression descriptors legitimately walk tens
89/// of thousands of ATN edges.
90const RECOGNITION_DEPTH_LIMIT: usize = 32_768;
91/// Whole-rule direct adaptive execution is allowed to give up and fall back to
92/// the existing recognizer. Keep the guard at the same order of magnitude as
93/// speculative recognition so malformed cyclic ATNs cannot spin forever.
94const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
95/// Probe window for deciding whether clean-pass one-outcome memo entries are
96/// reusable enough to keep caching. Large C# parses mostly produce one-shot
97/// entries; small ambiguous Kotlin loops repeatedly hit the same keys.
98const CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT: usize = 4096;
99const CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT: usize = 8;
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102enum SingleOutcomeMemoMode {
103    Probe,
104    Promote,
105    Sparse,
106}
107
108fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
109    intervals
110        .iter()
111        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
112}
113
114fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
115    let mut symbols = BTreeSet::new();
116    for (start, stop) in intervals {
117        symbols.extend(*start..=*stop);
118    }
119    symbols
120}
121
122fn interval_complement_symbols(
123    intervals: &[(i32, i32)],
124    min_vocabulary: i32,
125    max_vocabulary: i32,
126) -> BTreeSet<i32> {
127    (min_vocabulary..=max_vocabulary)
128        .filter(|symbol| !interval_set_contains(intervals, *symbol))
129        .collect()
130}
131
132#[cfg(feature = "perf-counters")]
133mod perf_counters {
134    use std::cell::Cell;
135    thread_local! {
136        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
137        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
138        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
139        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
140        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
141        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
142        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
143    }
144    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
145        c.with(|v| v.set(v.get() + n));
146    }
147    thread_local! {
148        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
149        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
150        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
151        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
152        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
153        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
154        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
155        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
156        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
157        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
158        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
159    }
160    pub(super) fn snapshot() -> [(&'static str, u64); 18] {
161        [
162            ("rfs_calls", RFS_CALLS.with(Cell::get)),
163            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
164            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
165            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
166            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
167            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
168            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
169            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
170            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
171            (
172                "atom_range_transitions",
173                ATOM_RANGE_TRANSITIONS.with(Cell::get),
174            ),
175            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
176            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
177            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
178            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
179            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
180            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
181            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
182            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
183        ]
184    }
185    pub fn reset() {
186        RFS_CALLS.with(|c| c.set(0));
187        RFS_MEMO_HITS.with(|c| c.set(0));
188        RFS_MEMO_MISSES.with(|c| c.set(0));
189        RFS_VISITING_CYCLE.with(|c| c.set(0));
190        MEMO_INSERTED.with(|c| c.set(0));
191        OUTCOMES_PUSHED.with(|c| c.set(0));
192        OUTCOMES_CLONED.with(|c| c.set(0));
193        EPSILON_TRANSITIONS.with(|c| c.set(0));
194        RULE_TRANSITIONS.with(|c| c.set(0));
195        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
196        SINGLE_TRANS_BODY.with(|c| c.set(0));
197        MULTI_TRANS_BODY.with(|c| c.set(0));
198        SINGLE_TRANS_RULE.with(|c| c.set(0));
199        SINGLE_TRANS_ATOM.with(|c| c.set(0));
200        SINGLE_TRANS_OTHER.with(|c| c.set(0));
201        OUTCOMES_RETURN_0.with(|c| c.set(0));
202        OUTCOMES_RETURN_1.with(|c| c.set(0));
203        OUTCOMES_RETURN_N.with(|c| c.set(0));
204    }
205    pub fn dump() {
206        for (name, value) in snapshot() {
207            #[allow(clippy::print_stderr)]
208            {
209                eprintln!("perf {name}={value}");
210            }
211        }
212    }
213}
214
215#[cfg(feature = "perf-counters")]
216pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
217/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
218/// fast recognizer has probed far enough that per-token stream sync dominates.
219/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
220/// switching long inputs to the cheaper filled-stream path before large fanout.
221const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
222/// Parser semantic action reached while recognizing one ATN path.
223///
224/// Generated parsers use `source_state` to dispatch back to the grammar action
225/// rendered for that ATN action transition. The token interval is the current
226/// rule's input span at the action site, which covers common target templates
227/// such as `$text`. Rule-init actions do not have an ATN action source state,
228/// so they are marked separately and may carry an ATN state for expected-token
229/// rendering.
230#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
231pub struct ParserAction {
232    source_state: usize,
233    rule_index: usize,
234    start_index: usize,
235    stop_index: Option<usize>,
236    rule_init: bool,
237    expected_state: Option<usize>,
238}
239
240impl ParserAction {
241    /// Creates an action event for a recognized parser path.
242    pub const fn new(
243        source_state: usize,
244        rule_index: usize,
245        start_index: usize,
246        stop_index: Option<usize>,
247    ) -> Self {
248        Self {
249            source_state,
250            rule_index,
251            start_index,
252            stop_index,
253            rule_init: false,
254            expected_state: None,
255        }
256    }
257
258    /// Creates an action event for a rule-level `@init` action.
259    pub const fn new_rule_init(
260        rule_index: usize,
261        start_index: usize,
262        expected_state: Option<usize>,
263    ) -> Self {
264        Self {
265            source_state: usize::MAX,
266            rule_index,
267            start_index,
268            stop_index: None,
269            rule_init: true,
270            expected_state,
271        }
272    }
273
274    /// ATN state that owns the semantic-action transition.
275    pub const fn source_state(&self) -> usize {
276        self.source_state
277    }
278
279    /// Grammar rule index recorded by the serialized ATN action transition.
280    pub const fn rule_index(&self) -> usize {
281        self.rule_index
282    }
283
284    /// Token-stream index where the active rule began.
285    pub const fn start_index(&self) -> usize {
286        self.start_index
287    }
288
289    /// Last token-stream index consumed before the action was reached.
290    pub const fn stop_index(&self) -> Option<usize> {
291        self.stop_index
292    }
293
294    /// Reports whether this event represents a rule-level `@init` action.
295    pub const fn is_rule_init(&self) -> bool {
296        self.rule_init
297    }
298
299    /// ATN state used to compute expected-token display for this action.
300    pub const fn expected_state(&self) -> Option<usize> {
301        self.expected_state
302    }
303}
304
305/// Parser semantic predicate rendered from a supported target template.
306///
307/// The metadata recognizer evaluates these at the token-stream index where the
308/// predicate transition is reached. Unsupported or absent predicate templates
309/// remain unconditional so existing generated parsers keep their previous
310/// behavior unless the generator opts into this table.
311#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
312pub enum ParserPredicate {
313    True,
314    False,
315    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
316    FalseWithMessage {
317        message: &'static str,
318    },
319    /// Target-template test helper that reports predicate evaluation before
320    /// returning the wrapped boolean value.
321    Invoke {
322        value: bool,
323    },
324    LookaheadTextEquals {
325        offset: isize,
326        text: &'static str,
327    },
328    LookaheadNotEquals {
329        offset: isize,
330        token_type: i32,
331    },
332    /// Checks that the last two consumed visible tokens were adjacent in the
333    /// token stream. Used by C# parser predicates for split operator tokens.
334    TokenPairAdjacent,
335    /// Checks a generated parser context child by rule index and text.
336    ///
337    /// If the child is absent the predicate succeeds, matching target helpers
338    /// that treat incomplete or non-matching contexts as non-restrictive.
339    ContextChildRuleTextNotEquals {
340        rule_index: usize,
341        text: &'static str,
342    },
343    /// Compares the current rule invocation's integer argument with a literal
344    /// value from a supported `ValEquals("$i", "...")` target template.
345    LocalIntEquals {
346        value: i64,
347    },
348    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
349    /// rule invocation's integer argument.
350    LocalIntLessOrEqual {
351        value: i64,
352    },
353    /// Compares a generated parser integer member modulo a literal value.
354    MemberModuloEquals {
355        member: usize,
356        modulus: i64,
357        value: i64,
358        equals: bool,
359    },
360    /// Compares a generated parser integer member with a literal value.
361    MemberEquals {
362        member: usize,
363        value: i64,
364        equals: bool,
365    },
366}
367
368/// Prediction strategy requested by generated parser harnesses.
369#[derive(Clone, Copy, Debug, Eq, PartialEq)]
370pub enum PredictionMode {
371    /// Prefer the clean full-context outcome when alternatives reach the same
372    /// input position.
373    Ll,
374    /// Preserve SLL's first-viable alternative bias at a decision, even when a
375    /// later full-context alternative could avoid recovery.
376    Sll,
377    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
378    LlExactAmbigDetection,
379}
380
381/// Integer argument metadata for a generated parser rule invocation.
382///
383/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
384/// the generator records the rule-transition source state and the value that
385/// should be visible to semantic predicates inside the callee.
386#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
387pub struct ParserRuleArg {
388    /// ATN state containing the rule transition that receives this argument.
389    pub source_state: usize,
390    /// Callee rule index for the transition.
391    pub rule_index: usize,
392    /// Literal fallback value to expose in the callee.
393    pub value: i64,
394    /// Whether the callee should inherit the caller's current integer argument.
395    pub inherit_local: bool,
396}
397
398/// Integer member mutation attached to an ATN action transition.
399#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
400pub struct ParserMemberAction {
401    /// ATN state containing the action transition.
402    pub source_state: usize,
403    /// Generator-assigned integer member id.
404    pub member: usize,
405    /// Delta applied when the action is reached on one speculative path.
406    pub delta: i64,
407}
408
409/// Integer return-value assignment attached to an ATN action transition.
410///
411/// Generated parsers use this metadata when target actions assign a simple
412/// return field such as `$y=1000;`. The interpreter applies it while selecting
413/// the recognized path so the finished parse tree can answer later
414/// `$label.y` action templates.
415#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
416pub struct ParserReturnAction {
417    /// ATN state containing the action transition.
418    pub source_state: usize,
419    /// Rule index recorded by the serialized action transition.
420    pub rule_index: usize,
421    /// Return-field name as it appears in the grammar.
422    pub name: &'static str,
423    /// Literal integer value assigned by the action.
424    pub value: i64,
425}
426
427/// Optional generated-runtime metadata for metadata-driven parser execution.
428#[derive(Clone, Copy, Debug, Default)]
429pub struct ParserRuntimeOptions<'a> {
430    /// Rule indexes whose `@init` actions should be replayed.
431    pub init_action_rules: &'a [usize],
432    /// Whether generated parse-tree contexts should retain alternative numbers.
433    pub track_alt_numbers: bool,
434    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
435    pub predicates: &'a [(usize, usize, ParserPredicate)],
436    /// Rule-call integer argument table keyed by ATN source state.
437    pub rule_args: &'a [ParserRuleArg],
438    /// Integer member mutations keyed by ATN action source state.
439    pub member_actions: &'a [ParserMemberAction],
440    /// Integer return assignments keyed by ATN action source state.
441    pub return_actions: &'a [ParserReturnAction],
442}
443
444pub trait Parser: Recognizer {
445    /// Reports whether generated parser rules should build parse-tree nodes
446    /// while recognizing input.
447    fn build_parse_trees(&self) -> bool;
448
449    /// Enables or disables parse-tree construction for subsequent rule calls.
450    fn set_build_parse_trees(&mut self, build: bool);
451
452    /// Returns the number of parser syntax errors recorded by committed parse
453    /// paths so far.
454    fn number_of_syntax_errors(&self) -> usize {
455        0
456    }
457
458    /// Reports whether prediction diagnostic-listener messages are emitted
459    /// during parser ATN recognition.
460    fn report_diagnostic_errors(&self) -> bool {
461        false
462    }
463
464    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
465    /// rule calls.
466    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
467
468    /// Reports the prediction strategy used when selecting among alternatives.
469    fn prediction_mode(&self) -> PredictionMode {
470        PredictionMode::Ll
471    }
472
473    /// Sets the prediction strategy for subsequent rule calls.
474    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
475}
476
477#[derive(Debug)]
478struct CachedPredictionContext {
479    version: usize,
480    atn_key: usize,
481    context: Rc<PredictionContext>,
482}
483
484#[derive(Debug)]
485pub struct BaseParser<S> {
486    input: CommonTokenStream<S>,
487    data: RecognizerData,
488    build_parse_trees: bool,
489    syntax_errors: usize,
490    report_diagnostic_errors: bool,
491    prediction_mode: PredictionMode,
492    prediction_diagnostics: Vec<ParserDiagnostic>,
493    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
494    generated_parser_diagnostics: Vec<ParserDiagnostic>,
495    generated_sync_expected: Option<TokenBitSet>,
496    int_members: BTreeMap<usize, i64>,
497    rule_context_stack: Vec<RuleContextFrame>,
498    rule_context_version: usize,
499    prediction_context_cache: Option<CachedPredictionContext>,
500    pending_invoking_states: Vec<isize>,
501    precedence_stack: Vec<i32>,
502    /// Predicate side effects are observable in a few target-template tests;
503    /// speculative recognition may revisit the same coordinate, so replay it
504    /// once per parser instance.
505    invoked_predicates: Vec<(usize, usize)>,
506    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
507    /// hot rule-transition checks to a vector lookup after the first visit
508    /// while the thread-local shared ATN cache still owns the cross-parse
509    /// computed value.
510    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
511    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
512    /// epsilon-reachable consuming transition and shows up as a hot loop in
513    /// `next_recovery_context` and recovery diagnostics on long inputs.
514    /// Keying on `state_number` and sharing the result through `Rc` removes
515    /// repeated DFS plus per-call `BTreeSet` allocations.
516    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
517    /// Same expected-symbol cache as a bitset for generated parser sync.
518    /// Successful parses only need `contains` and union; keeping that path out
519    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
520    /// check and defers deterministic formatting to diagnostics.
521    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
522    /// Per-state cache for whether a return state can finish its owning rule
523    /// without consuming more input. Generated-parser sync uses this to walk
524    /// parent prediction contexts for nullable exits without paying repeated
525    /// epsilon-closure searches on every loop or optional decision.
526    rule_stop_reach_cache: Vec<Option<bool>>,
527    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
528    /// threads the same epsilon-recovery context through hundreds of follow
529    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
530    /// reference bump and lets the memo key hash by pointer.
531    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
532    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
533    /// touch a given decision state still pay no upfront cost; once cached,
534    /// the recognizer prunes alternatives whose look-1 cannot accept the
535    /// current lookahead, letting common SLL decisions reduce to a single
536    /// transition walk instead of a full speculative fan-out.
537    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
538    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
539    /// Each multi-trans visit asks "given this decision state and this
540    /// lookahead token, which alt do I commit to?" Hitting this cache
541    /// turns the question into a hashmap probe instead of re-scanning
542    /// the decision's per-transition FIRST sets every visit.
543    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
544    /// Per-parse cache for whether an ATN state can reach itself without
545    /// consuming input. Only those states need the recursive recognizer's
546    /// `(state, token-index)` cycle guard.
547    empty_cycle_cache: Vec<Option<bool>>,
548    /// Probe state for deciding whether clean-pass one-outcome memo entries
549    /// are worth storing for the current parse.
550    single_outcome_memo_mode: SingleOutcomeMemoMode,
551    single_outcome_probe_seen: FxHashSet<FastRecognizeKey>,
552    single_outcome_probe_samples: usize,
553    single_outcome_probe_repeats: usize,
554    /// Empty recovery-symbols singleton used as the default at rule entry and
555    /// after token consumption.
556    empty_recovery_symbols: Rc<BTreeSet<i32>>,
557    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
558    /// prefilter trims speculative rule calls whose called rule cannot
559    /// match the current lookahead, but it also bypasses single-token
560    /// insertion / deletion recovery that ANTLR runs at the rule's first
561    /// consuming transition. `parse_atn_rule` flips this off and retries
562    /// when the first pass produces no clean outcome so the runtime can
563    /// repair inputs the reference parser would have repaired.
564    fast_first_set_prefilter: bool,
565    /// Whether the fast recognizer should explore parser error-recovery paths.
566    /// Public rule parsing starts with this disabled for the common valid-input
567    /// path and enables it only for the retry that needs ANTLR-style repairs.
568    fast_recovery_enabled: bool,
569    /// Whether the fast recognizer should record terminal-token nodes while
570    /// speculating. Clean valid-input parsing can reconstruct terminals from
571    /// selected rule spans after recognition, avoiding many speculative `Rc`
572    /// nodes that are thrown away with losing paths.
573    fast_token_nodes_enabled: bool,
574}
575
576/// Rollback marker for speculative generated parser paths.
577#[derive(Clone, Copy, Debug, Eq, PartialEq)]
578pub struct GeneratedDiagnosticsCheckpoint {
579    diagnostics_len: usize,
580    syntax_errors: usize,
581}
582
583#[derive(Clone, Copy, Debug, Eq, PartialEq)]
584struct RuleContextFrame {
585    rule_index: usize,
586    invoking_state: isize,
587}
588
589#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
590struct RecognizeOutcome {
591    index: usize,
592    consumed_eof: bool,
593    alt_number: usize,
594    member_values: BTreeMap<usize, i64>,
595    return_values: BTreeMap<String, i64>,
596    diagnostics: Vec<ParserDiagnostic>,
597    decisions: Vec<usize>,
598    actions: Vec<ParserAction>,
599    nodes: Vec<RecognizedNode>,
600}
601
602#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
603enum RecognizedNode {
604    Token {
605        index: usize,
606    },
607    ErrorToken {
608        index: usize,
609    },
610    MissingToken {
611        token_type: i32,
612        at_index: usize,
613        text: String,
614    },
615    Rule {
616        rule_index: usize,
617        invoking_state: isize,
618        alt_number: usize,
619        start_index: usize,
620        stop_index: Option<usize>,
621        return_values: BTreeMap<String, i64>,
622        children: Vec<Self>,
623    },
624    LeftRecursiveBoundary {
625        rule_index: usize,
626    },
627}
628
629#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
630struct FastRecognizeOutcome {
631    index: usize,
632    consumed_eof: bool,
633    diagnostics: FastDiagnostics,
634    /// Speculative parse-tree fragment built up as the recognizer climbs.
635    /// The list is held as a persistent cons-list of `Rc`-wrapped nodes so
636    /// prepending while chaining recognition outcomes is `O(1)` and cloning
637    /// an outcome (memo lookup, dedup, or when fanning a child's tree out
638    /// to every follow outcome) only bumps a reference count rather than
639    /// deep-copying. On left-recursive grammars the unfolded list can carry
640    /// thousands of nodes per speculative path; without the persistent-list
641    /// shape recognition becomes super-linear in path length.
642    nodes: NodeList,
643}
644
645#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
646#[allow(clippy::box_collection)]
647struct FastDiagnostics(Option<Box<Vec<ParserDiagnostic>>>);
648
649impl FastDiagnostics {
650    const fn new() -> Self {
651        Self(None)
652    }
653
654    #[cfg(test)]
655    fn from_vec(diagnostics: Vec<ParserDiagnostic>) -> Self {
656        if diagnostics.is_empty() {
657            Self::new()
658        } else {
659            Self(Some(Box::new(diagnostics)))
660        }
661    }
662
663    fn is_empty(&self) -> bool {
664        self.0
665            .as_ref()
666            .is_none_or(|diagnostics| diagnostics.is_empty())
667    }
668
669    fn as_slice(&self) -> &[ParserDiagnostic] {
670        self.0.as_deref().map_or(&[], Vec::as_slice)
671    }
672
673    fn insert(&mut self, index: usize, diagnostic: ParserDiagnostic) {
674        self.0
675            .get_or_insert_with(Box::default)
676            .insert(index, diagnostic);
677    }
678
679    fn append(&mut self, other: &mut Self) {
680        if other.is_empty() {
681            return;
682        }
683        self.0
684            .get_or_insert_with(Box::default)
685            .append(other.0.get_or_insert_with(Box::default));
686        if other.is_empty() {
687            other.0 = None;
688        }
689    }
690}
691
692impl std::ops::Deref for FastDiagnostics {
693    type Target = [ParserDiagnostic];
694
695    fn deref(&self) -> &Self::Target {
696        self.as_slice()
697    }
698}
699
700/// Persistent cons-list of fast-recognizer nodes. The list keeps nodes in the
701/// same head-first order as the original `Vec<FastRecognizedNode>` they
702/// replaced. Shared tails across speculative outcomes amortize the cost of
703/// chaining a child rule's nodes onto every follow outcome.
704///
705/// `One` is an inline single-element variant: most outcomes carry only one
706/// node (a single token or a single rule wrapper), so storing that node
707/// directly avoids allocating an `Rc<NodeList>` tail wrapper.
708#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
709enum NodeList {
710    #[default]
711    Empty,
712    One(Rc<FastRecognizedNode>),
713    Cons {
714        head: Rc<FastRecognizedNode>,
715        tail: Rc<Self>,
716    },
717}
718
719impl NodeList {
720    /// Creates an empty list.
721    const fn new() -> Self {
722        Self::Empty
723    }
724
725    /// Prepends `node` and returns the new list. Both shared tails and the
726    /// new head are reference-counted so this is `O(1)`.
727    fn cons(self, node: Rc<FastRecognizedNode>) -> Self {
728        match self {
729            Self::Empty => Self::One(node),
730            existing @ (Self::One(_) | Self::Cons { .. }) => Self::Cons {
731                head: node,
732                tail: Rc::new(existing),
733            },
734        }
735    }
736
737    /// In-place prepend that takes ownership of `self` via [`std::mem::take`]
738    /// so existing call sites can keep using `&mut` access.
739    fn prepend(&mut self, node: Rc<FastRecognizedNode>) {
740        let owned = std::mem::take(self);
741        *self = owned.cons(node);
742    }
743
744    /// Materializes the list into a `Vec` in head-first order. Used at the
745    /// boundaries that need random-access traversal (the public rule entry
746    /// when building the final parse tree, and
747    /// `fold_fast_left_recursive_boundaries`).
748    fn to_vec(&self) -> Vec<Rc<FastRecognizedNode>> {
749        let mut out = Vec::new();
750        let mut cursor = self;
751        loop {
752            match cursor {
753                Self::Empty => break,
754                Self::One(node) => {
755                    out.push(Rc::clone(node));
756                    break;
757                }
758                Self::Cons { head, tail } => {
759                    out.push(Rc::clone(head));
760                    cursor = tail.as_ref();
761                }
762            }
763        }
764        out
765    }
766
767    const fn iter(&self) -> NodeListIter<'_> {
768        NodeListIter { cursor: self }
769    }
770
771    fn len(&self) -> usize {
772        self.iter().count()
773    }
774
775    fn has_left_recursive_boundary(&self) -> bool {
776        self.iter()
777            .any(|node| fast_node_has_left_recursive_boundary(node.as_ref()))
778    }
779
780    fn has_explicit_token_node(&self) -> bool {
781        self.iter().any(|node| {
782            matches!(
783                node.as_ref(),
784                FastRecognizedNode::Token { .. }
785                    | FastRecognizedNode::ErrorToken { .. }
786                    | FastRecognizedNode::MissingToken { .. }
787            )
788        })
789    }
790
791    /// Builds a list from an already ordered vector.
792    fn from_vec(nodes: Vec<Rc<FastRecognizedNode>>) -> Self {
793        let mut list = Self::new();
794        for node in nodes.into_iter().rev() {
795            list.prepend(node);
796        }
797        list
798    }
799}
800
801struct NodeListIter<'a> {
802    cursor: &'a NodeList,
803}
804
805impl<'a> Iterator for NodeListIter<'a> {
806    type Item = &'a Rc<FastRecognizedNode>;
807
808    fn next(&mut self) -> Option<Self::Item> {
809        match self.cursor {
810            NodeList::Empty => None,
811            NodeList::One(node) => {
812                self.cursor = &NodeList::Empty;
813                Some(node)
814            }
815            NodeList::Cons { head, tail } => {
816                self.cursor = tail.as_ref();
817                Some(head)
818            }
819        }
820    }
821}
822
823/// Minimal parse-tree fragment retained by the fast recognizer so the public
824/// rule entry can build nested rule contexts without paying for
825/// action/decision bookkeeping.
826#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
827enum FastRecognizedNode {
828    Token {
829        index: usize,
830    },
831    ErrorToken {
832        index: usize,
833    },
834    MissingToken {
835        token_type: i32,
836        at_index: usize,
837        text: String,
838    },
839    Rule {
840        rule_index: usize,
841        invoking_state: isize,
842        start_index: usize,
843        stop_index: Option<usize>,
844        children: NodeList,
845    },
846    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
847    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
848    /// public rule entry hands the tree to the caller.
849    LeftRecursiveBoundary {
850        rule_index: usize,
851    },
852}
853
854#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
855struct ParserDiagnostic {
856    line: usize,
857    column: usize,
858    message: String,
859}
860
861#[derive(Clone, Debug, Default, Eq, PartialEq)]
862struct ExpectedTokens {
863    index: Option<usize>,
864    symbols: BTreeSet<i32>,
865    no_viable: Option<NoViableAlternative>,
866}
867
868#[derive(Clone, Copy, Debug, Eq, PartialEq)]
869struct NoViableAlternative {
870    start_index: usize,
871    error_index: usize,
872}
873
874impl ExpectedTokens {
875    /// Records the expected symbols for the farthest token index reached by any
876    /// failed ATN path.
877    fn record_transition(&mut self, index: usize, transition: &Transition, max_token_type: i32) {
878        let symbols = transition_expected_symbols(transition, max_token_type);
879        match self.index {
880            Some(current) if index < current => {}
881            Some(current) if index == current => self.symbols.extend(symbols),
882            _ => {
883                self.index = Some(index);
884                self.symbols = symbols;
885            }
886        }
887    }
888
889    /// Records an ambiguous decision that failed after consuming a shared
890    /// prefix, which ANTLR reports as `no viable alternative`.
891    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
892        match self.no_viable {
893            Some(current) if error_index < current.error_index => {}
894            _ => {
895                self.no_viable = Some(NoViableAlternative {
896                    start_index,
897                    error_index,
898                });
899            }
900        }
901    }
902}
903
904/// Compact token-type set for parser-internal FIRST/lookahead caches.
905///
906/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
907/// but the hot recognizer path mostly needs `contains` and set union over
908/// small token ids. A bitset avoids tree traversal and per-symbol allocation
909/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
910#[derive(Clone, Debug, Default, Eq, PartialEq)]
911struct TokenBitSet {
912    words: Vec<u64>,
913}
914
915impl TokenBitSet {
916    fn insert(&mut self, symbol: i32) {
917        let Some(slot) = token_bit_slot(symbol) else {
918            return;
919        };
920        let word = slot / u64::BITS as usize;
921        if word >= self.words.len() {
922            self.words.resize(word + 1, 0);
923        }
924        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
925    }
926
927    fn extend_range(&mut self, start: i32, stop: i32) {
928        let (start, stop) = if start <= stop {
929            (start, stop)
930        } else {
931            (stop, start)
932        };
933        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
934            self.insert(TOKEN_EOF);
935        }
936        let positive_start = start.max(1);
937        if positive_start > stop {
938            return;
939        }
940        let Some(start_slot) = token_bit_slot(positive_start) else {
941            return;
942        };
943        let Some(stop_slot) = token_bit_slot(stop) else {
944            return;
945        };
946        self.extend_slot_range(start_slot, stop_slot);
947    }
948
949    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
950        if start_slot > stop_slot {
951            return;
952        }
953        let start_word = start_slot / u64::BITS as usize;
954        let stop_word = stop_slot / u64::BITS as usize;
955        if stop_word >= self.words.len() {
956            self.words.resize(stop_word + 1, 0);
957        }
958        let start_offset = start_slot % u64::BITS as usize;
959        let stop_offset = stop_slot % u64::BITS as usize;
960        if start_word == stop_word {
961            self.words[start_word] |=
962                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
963            return;
964        }
965        self.words[start_word] |= !0_u64 << start_offset;
966        for word in &mut self.words[(start_word + 1)..stop_word] {
967            *word = !0_u64;
968        }
969        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
970    }
971
972    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
973        for symbol in symbols {
974            self.insert(symbol);
975        }
976    }
977
978    fn extend_from(&mut self, other: &Self) {
979        if other.words.len() > self.words.len() {
980            self.words.resize(other.words.len(), 0);
981        }
982        for (left, right) in self.words.iter_mut().zip(&other.words) {
983            *left |= *right;
984        }
985    }
986
987    fn contains(&self, symbol: i32) -> bool {
988        let Some(slot) = token_bit_slot(symbol) else {
989            return false;
990        };
991        let word = slot / u64::BITS as usize;
992        self.words
993            .get(word)
994            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
995    }
996
997    fn is_empty(&self) -> bool {
998        self.words.iter().all(|word| *word == 0)
999    }
1000
1001    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
1002        for (word_index, word) in self.words.iter().copied().enumerate() {
1003            let mut bits = word;
1004            while bits != 0 {
1005                let bit = bits.trailing_zeros() as usize;
1006                if let Some(symbol) = token_bit_symbol(word_index * u64::BITS as usize + bit) {
1007                    target.insert(symbol);
1008                }
1009                bits &= bits - 1;
1010            }
1011        }
1012    }
1013
1014    fn to_btree_set(&self) -> BTreeSet<i32> {
1015        let mut out = BTreeSet::new();
1016        self.extend_btree_set(&mut out);
1017        out
1018    }
1019}
1020
1021fn token_bit_slot(symbol: i32) -> Option<usize> {
1022    if symbol == TOKEN_EOF {
1023        Some(0)
1024    } else if symbol > 0 {
1025        usize::try_from(symbol).ok()
1026    } else {
1027        None
1028    }
1029}
1030
1031fn token_bit_symbol(slot: usize) -> Option<i32> {
1032    if slot == 0 {
1033        Some(TOKEN_EOF)
1034    } else {
1035        i32::try_from(slot).ok()
1036    }
1037}
1038
1039/// Converts one consuming transition into the token types that would satisfy it
1040/// for diagnostic reporting.
1041fn transition_expected_symbols(transition: &Transition, max_token_type: i32) -> BTreeSet<i32> {
1042    let mut symbols = BTreeSet::new();
1043    match transition {
1044        Transition::Atom { label, .. } => {
1045            symbols.insert(*label);
1046        }
1047        Transition::Range { start, stop, .. } => {
1048            symbols.extend(*start..=*stop);
1049        }
1050        Transition::Set { set, .. } => {
1051            for (start, stop) in set.ranges() {
1052                symbols.extend(*start..=*stop);
1053            }
1054        }
1055        Transition::NotSet { set, .. } => {
1056            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
1057        }
1058        Transition::Wildcard { .. } => {
1059            symbols.extend(1..=max_token_type);
1060        }
1061        Transition::Epsilon { .. }
1062        | Transition::Rule { .. }
1063        | Transition::Predicate { .. }
1064        | Transition::Action { .. }
1065        | Transition::Precedence { .. } => {}
1066    }
1067    symbols
1068}
1069
1070fn transition_expected_token_set(transition: &Transition, max_token_type: i32) -> TokenBitSet {
1071    let mut symbols = TokenBitSet::default();
1072    match transition {
1073        Transition::Atom { label, .. } => {
1074            symbols.insert(*label);
1075        }
1076        Transition::Range { start, stop, .. } => {
1077            symbols.extend_range(*start, *stop);
1078        }
1079        Transition::Set { set, .. } => {
1080            for (start, stop) in set.ranges() {
1081                symbols.extend_range(*start, *stop);
1082            }
1083        }
1084        Transition::NotSet { set, .. } => {
1085            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
1086        }
1087        Transition::Wildcard { .. } => {
1088            symbols.extend_range(1, max_token_type);
1089        }
1090        Transition::Epsilon { .. }
1091        | Transition::Rule { .. }
1092        | Transition::Predicate { .. }
1093        | Transition::Action { .. }
1094        | Transition::Precedence { .. } => {}
1095    }
1096    symbols
1097}
1098
1099/// Returns the consuming-token expectations reachable from an ATN state through
1100/// epsilon transitions. Recovery diagnostics need this closure so alternatives
1101/// and loop exits report the same expectation set ANTLR users see.
1102fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
1103    let mut symbols = BTreeSet::new();
1104    let mut stack = vec![state_number];
1105    let mut visited = BTreeSet::new();
1106    while let Some(current) = stack.pop() {
1107        if !visited.insert(current) {
1108            continue;
1109        }
1110        let Some(state) = atn.state(current) else {
1111            continue;
1112        };
1113        for transition in &state.transitions {
1114            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
1115            if transition_symbols.is_empty() {
1116                if transition.is_epsilon() {
1117                    stack.push(transition.target());
1118                }
1119            } else {
1120                symbols.extend(transition_symbols);
1121            }
1122        }
1123    }
1124    symbols
1125}
1126
1127fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
1128    let mut symbols = TokenBitSet::default();
1129    let mut stack = vec![state_number];
1130    let mut visited = BTreeSet::new();
1131    while let Some(current) = stack.pop() {
1132        if !visited.insert(current) {
1133            continue;
1134        }
1135        let Some(state) = atn.state(current) else {
1136            continue;
1137        };
1138        for transition in &state.transitions {
1139            let transition_symbols =
1140                transition_expected_token_set(transition, atn.max_token_type());
1141            if transition_symbols.is_empty() {
1142                if transition.is_epsilon() {
1143                    stack.push(transition.target());
1144                }
1145            } else {
1146                symbols.extend_from(&transition_symbols);
1147            }
1148        }
1149    }
1150    symbols
1151}
1152
1153fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
1154    let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
1155        return false;
1156    };
1157    let Some(&stop_state) = atn.rule_to_stop_state().get(rule_index) else {
1158        return false;
1159    };
1160    epsilon_reaches_state(atn, state_number, stop_state)
1161}
1162
1163fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
1164    let mut stack = vec![start];
1165    let mut visited = BTreeSet::new();
1166    while let Some(current) = stack.pop() {
1167        if current == target {
1168            return true;
1169        }
1170        if !visited.insert(current) {
1171            continue;
1172        }
1173        let Some(state) = atn.state(current) else {
1174            continue;
1175        };
1176        stack.extend(
1177            state
1178                .transitions
1179                .iter()
1180                .filter(|transition| transition.is_epsilon())
1181                .map(Transition::target),
1182        );
1183    }
1184    false
1185}
1186
1187/// FIRST set for a rule entry plus whether the rule is nullable.
1188///
1189/// Walks epsilon, predicate, action, and rule-call transitions until it finds
1190/// a consuming transition or reaches the rule's stop state. Used by the fast
1191/// recognizer to skip rule alternatives whose first-consumed token cannot
1192/// possibly match the current lookahead.
1193#[derive(Clone, Debug, Default, Eq, PartialEq)]
1194struct FirstSet {
1195    symbols: TokenBitSet,
1196    nullable: bool,
1197}
1198
1199/// Per-parser cache of FIRST sets computed during recognition. The fast path
1200/// consults this on every speculative `Transition::Rule` encounter, so the
1201/// computation must amortize across all of those calls — the FIRST set is a
1202/// pure function of the ATN, not of the input position. Cached entries are
1203/// shared via `Rc` so the recognizer never deep-copies the underlying
1204/// `BTreeSet<i32>`.
1205type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
1206
1207// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
1208// and decision-lookahead entries are purely functions of the grammar's
1209// ATN, so caching across parses lets repeated parsing of the same grammar
1210// (the common case for a CLI tool or language server) avoid redoing the
1211// closure work. Generated parsers hand us a `&'static Atn` whose address
1212// is stable, which is what we hash on.
1213type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
1214
1215#[derive(Default)]
1216struct SharedAtnCache {
1217    first_set: FirstSetCache,
1218    decision_lookahead: DecisionLookaheadCache,
1219}
1220
1221thread_local! {
1222    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
1223        RefCell::new(FxHashMap::default());
1224}
1225
1226/// Compound key for `SHARED_ATN_CACHES`.
1227///
1228/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
1229/// pointer identifies one grammar for the program's lifetime. For the
1230/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
1231/// the secondary fields below catch the pointer collision: a new grammar
1232/// would need to match all of `(states ptr, states len, max_token_type)` to
1233/// be mistaken for the dropped one. That combination changing under us
1234/// without a rebuild is implausible enough to treat as a bug; bundling them
1235/// into the key is otherwise a few extra bytes per lookup.
1236#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1237struct SharedAtnCacheKey {
1238    atn: usize,
1239    states: usize,
1240    state_count: usize,
1241    max_token_type: i32,
1242}
1243
1244impl SharedAtnCacheKey {
1245    fn for_atn(atn: &Atn) -> Self {
1246        Self {
1247            atn: std::ptr::from_ref::<Atn>(atn) as usize,
1248            states: atn.states().as_ptr() as usize,
1249            state_count: atn.states().len(),
1250            max_token_type: atn.max_token_type(),
1251        }
1252    }
1253}
1254
1255fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
1256    SHARED_ATN_CACHES.with(|cell| {
1257        let key = SharedAtnCacheKey::for_atn(atn);
1258        let mut map = cell.borrow_mut();
1259        let cache = map.entry(key).or_default();
1260        f(&mut cache.first_set)
1261    })
1262}
1263
1264fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
1265    SHARED_ATN_CACHES.with(|cell| {
1266        let key = SharedAtnCacheKey::for_atn(atn);
1267        let mut map = cell.borrow_mut();
1268        let cache = map.entry(key).or_default();
1269        f(cache)
1270    })
1271}
1272
1273/// Per-decision-state cached look-1 sets for each outgoing transition.
1274///
1275/// At a multi-alternative state, the recognizer would otherwise speculatively
1276/// walk every alternative even when only one can possibly accept the current
1277/// lookahead. Caching the look-1 set per transition lets us prune the
1278/// non-viable transitions before recursing — the same SLL prediction trick
1279/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
1280/// filter rather than a full DFA.
1281#[derive(Debug, Default)]
1282struct DecisionLookahead {
1283    transitions: Vec<TransitionLookSet>,
1284}
1285
1286/// Look-1 information for one outgoing transition.
1287///
1288/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
1289/// can reach the rule stop without consuming a token (e.g. an empty alt).
1290/// Nullable transitions cannot be pruned: they may still be the right path
1291/// when the lookahead consumes nothing further inside the current rule.
1292#[derive(Clone, Debug, Default)]
1293struct TransitionLookSet {
1294    symbols: TokenBitSet,
1295    nullable: bool,
1296}
1297
1298/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
1299/// rarely-touched fields keeps the recursive helpers below the function-arity
1300/// lint and lets every nested call thread the same cache and cycle guards.
1301struct FirstSetCtx<'a> {
1302    cache: &'a mut FirstSetCache,
1303    in_progress: BTreeSet<(usize, usize)>,
1304    hit_cycle: bool,
1305}
1306
1307/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
1308/// shared cache and tolerating recursive nullable rule chains. Mutually
1309/// recursive rules cannot stack-overflow because callers in flight are tracked
1310/// in `ctx.in_progress`; revisits return without recursing, and the partial
1311/// result is cached only when no cycle was detected during its computation.
1312///
1313/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
1314/// rule-call probes only pay a reference bump.
1315fn rule_first_set(
1316    atn: &Atn,
1317    target: usize,
1318    rule_stop_state: usize,
1319    cache: &mut FirstSetCache,
1320) -> Rc<FirstSet> {
1321    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
1322        return Rc::clone(cached);
1323    }
1324    let mut ctx = FirstSetCtx {
1325        cache,
1326        in_progress: BTreeSet::new(),
1327        hit_cycle: false,
1328    };
1329    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
1330}
1331
1332fn rule_first_set_cached(
1333    atn: &Atn,
1334    target: usize,
1335    rule_stop_state: usize,
1336    ctx: &mut FirstSetCtx<'_>,
1337) -> Rc<FirstSet> {
1338    let key = (target, rule_stop_state);
1339    if let Some(cached) = ctx.cache.get(&key) {
1340        return Rc::clone(cached);
1341    }
1342    if !ctx.in_progress.insert(key) {
1343        // Cycle: a caller above is already computing this entry. Return an
1344        // empty FIRST set; that caller's traversal supplies the contributions
1345        // from the rule's other alternatives.
1346        return Rc::new(FirstSet::default());
1347    }
1348    let saved_hit_cycle = ctx.hit_cycle;
1349    ctx.hit_cycle = false;
1350    let mut first = FirstSet::default();
1351    let mut visited = BTreeSet::new();
1352    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
1353    ctx.in_progress.remove(&key);
1354    let entry = Rc::new(first);
1355    if !ctx.hit_cycle {
1356        ctx.cache.insert(key, Rc::clone(&entry));
1357    }
1358    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
1359    entry
1360}
1361
1362/// Returns the look-1 set for traversing `transition` while still inside the
1363/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
1364/// prunes transitions whose look-1 cannot accept the current lookahead.
1365fn transition_first_set(
1366    atn: &Atn,
1367    transition: &Transition,
1368    rule_stop_state: usize,
1369    cache: &mut FirstSetCache,
1370) -> TransitionLookSet {
1371    match transition {
1372        Transition::Atom { label, .. } => {
1373            let mut symbols = TokenBitSet::default();
1374            symbols.insert(*label);
1375            TransitionLookSet {
1376                symbols,
1377                nullable: false,
1378            }
1379        }
1380        Transition::Range { start, stop, .. } => {
1381            let mut symbols = TokenBitSet::default();
1382            symbols.extend_range(*start, *stop);
1383            TransitionLookSet {
1384                symbols,
1385                nullable: false,
1386            }
1387        }
1388        Transition::Set { set, .. } => {
1389            let mut symbols = TokenBitSet::default();
1390            for (start, stop) in set.ranges() {
1391                symbols.extend_range(*start, *stop);
1392            }
1393            TransitionLookSet {
1394                symbols,
1395                nullable: false,
1396            }
1397        }
1398        Transition::NotSet { set, .. } => {
1399            let max = atn.max_token_type();
1400            let mut symbols = TokenBitSet::default();
1401            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
1402            TransitionLookSet {
1403                symbols,
1404                nullable: false,
1405            }
1406        }
1407        Transition::Wildcard { .. } => {
1408            let mut symbols = TokenBitSet::default();
1409            symbols.extend_range(1, atn.max_token_type());
1410            TransitionLookSet {
1411                symbols,
1412                nullable: false,
1413            }
1414        }
1415        Transition::Epsilon { target }
1416        | Transition::Action { target, .. }
1417        | Transition::Predicate { target, .. }
1418        | Transition::Precedence { target, .. } => {
1419            // Walk the closure starting at `target` until a consuming transition
1420            // is reached or the rule stop state is hit.
1421            let first = rule_first_set(atn, *target, rule_stop_state, cache);
1422            TransitionLookSet {
1423                symbols: first.symbols.clone(),
1424                nullable: first.nullable,
1425            }
1426        }
1427        Transition::Rule {
1428            target,
1429            rule_index,
1430            follow_state,
1431            ..
1432        } => {
1433            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
1434                return TransitionLookSet::default();
1435            };
1436            let child = rule_first_set(atn, *target, child_stop, cache);
1437            let mut symbols = child.symbols.clone();
1438            let nullable = if child.nullable {
1439                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
1440                symbols.extend_from(&follow.symbols);
1441                follow.nullable
1442            } else {
1443                false
1444            };
1445            TransitionLookSet { symbols, nullable }
1446        }
1447    }
1448}
1449
1450/// Reports whether `transition` can be pruned at a multi-alt state because
1451/// its cached look-1 cannot accept the current lookahead.
1452///
1453/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
1454/// Rule/Precedence) so consuming transitions still reach the
1455/// `matches`+recovery path that surfaces single-token deletion / insertion
1456/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
1457/// transition is pruned, its FIRST set is folded into `expected` so failed
1458/// parses produce the same `mismatched input ... expecting ...` diagnostic
1459/// the no-prefilter baseline would emit.
1460/// Returns the unique alt index (0-based) when `symbol` falls into exactly
1461/// one transition's FIRST set and no transition is nullable. Used as an
1462/// LL(1) commit point: when prediction is unambiguous from the lookahead
1463/// alone, the recursive recognizer can skip every other alt without paying
1464/// for the per-transition filter probe.
1465///
1466/// `None` signals the caller to fall back to per-transition lookahead
1467/// filtering. Returning `Some` for an alt whose transition cannot actually
1468/// match would prune the only viable parse path; this is why we require
1469/// strict disjointness *and* no nullable transitions in the decision.
1470fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
1471    let mut chosen: Option<usize> = None;
1472    for (index, transition) in entry.transitions.iter().enumerate() {
1473        if transition.nullable {
1474            return None;
1475        }
1476        if transition.symbols.contains(symbol) {
1477            if chosen.is_some() {
1478                return None;
1479            }
1480            chosen = Some(index);
1481        }
1482    }
1483    chosen
1484}
1485
1486/// Returns the unique greedy alt index (0-based) selected by the current
1487/// lookahead.
1488///
1489/// The shortcut is intentionally conservative around nullable exits. If the
1490/// current symbol can start a consuming alternative and an empty alternative is
1491/// also present, one-token lookahead is not enough to know whether the symbol
1492/// belongs to the current construct or to its caller's follow set. `None`
1493/// signals the caller to fall back to adaptive prediction.
1494fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
1495    let mut matching_non_nullable_alt = None;
1496    let mut nullable_alt = None;
1497    for (index, transition) in entry.transitions.iter().enumerate() {
1498        if transition.nullable {
1499            if nullable_alt.is_some() {
1500                return None;
1501            }
1502            nullable_alt = Some(index);
1503        }
1504        if transition.symbols.contains(symbol) {
1505            if transition.nullable {
1506                continue;
1507            }
1508            if matching_non_nullable_alt.is_some() {
1509                return None;
1510            }
1511            matching_non_nullable_alt = Some(index);
1512        }
1513    }
1514    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
1515        return None;
1516    }
1517    if non_greedy {
1518        nullable_alt.or(matching_non_nullable_alt)
1519    } else {
1520        matching_non_nullable_alt.or(nullable_alt)
1521    }
1522}
1523
1524fn should_skip_via_lookahead(
1525    transition: &Transition,
1526    transition_index: usize,
1527    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
1528    index: usize,
1529    record_expected: bool,
1530    expected: &mut ExpectedTokens,
1531) -> bool {
1532    let prune_non_consuming = matches!(
1533        transition,
1534        Transition::Epsilon { .. }
1535            | Transition::Action { .. }
1536            | Transition::Predicate { .. }
1537            | Transition::Rule { .. }
1538            | Transition::Precedence { .. }
1539    );
1540    if !prune_non_consuming {
1541        return false;
1542    }
1543    let Some((symbol, entry)) = lookahead_filter else {
1544        return false;
1545    };
1546    let Some(set) = entry.transitions.get(transition_index) else {
1547        return false;
1548    };
1549    if set.symbols.contains(*symbol) || set.nullable {
1550        return false;
1551    }
1552    if record_expected && !set.symbols.is_empty() {
1553        record_pruned_transition_expected(set, index, expected);
1554    }
1555    true
1556}
1557
1558fn should_skip_rule_via_first_set(
1559    first: &FirstSet,
1560    symbol: i32,
1561    record_expected: bool,
1562    index: usize,
1563    expected: &mut ExpectedTokens,
1564) -> bool {
1565    if first.nullable || first.symbols.contains(symbol) {
1566        return false;
1567    }
1568    if record_expected && !first.symbols.is_empty() {
1569        record_token_bit_expected(&first.symbols, index, expected);
1570    }
1571    true
1572}
1573
1574fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
1575    match expected.index {
1576        Some(current) if index < current => {}
1577        Some(current) if index == current => {
1578            symbols.extend_btree_set(&mut expected.symbols);
1579        }
1580        _ => {
1581            expected.index = Some(index);
1582            expected.symbols = symbols.to_btree_set();
1583        }
1584    }
1585}
1586
1587/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
1588fn record_pruned_transition_expected(
1589    set: &TransitionLookSet,
1590    index: usize,
1591    expected: &mut ExpectedTokens,
1592) {
1593    match expected.index {
1594        Some(current) if index < current => {}
1595        Some(current) if index == current => {
1596            set.symbols.extend_btree_set(&mut expected.symbols);
1597        }
1598        _ => {
1599            expected.index = Some(index);
1600            expected.symbols = set.symbols.to_btree_set();
1601        }
1602    }
1603}
1604
1605fn rule_first_set_inner(
1606    atn: &Atn,
1607    state_number: usize,
1608    rule_stop_state: usize,
1609    ctx: &mut FirstSetCtx<'_>,
1610    visited: &mut BTreeSet<usize>,
1611    first: &mut FirstSet,
1612) {
1613    if !visited.insert(state_number) {
1614        return;
1615    }
1616    if state_number == rule_stop_state {
1617        first.nullable = true;
1618        return;
1619    }
1620    let Some(state) = atn.state(state_number) else {
1621        return;
1622    };
1623    for transition in &state.transitions {
1624        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
1625        if !transition_symbols.is_empty() {
1626            first.symbols.extend_iter(transition_symbols);
1627            continue;
1628        }
1629        match transition {
1630            Transition::Epsilon { target }
1631            | Transition::Action { target, .. }
1632            | Transition::Predicate { target, .. }
1633            | Transition::Precedence { target, .. } => {
1634                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
1635            }
1636            Transition::Rule {
1637                target,
1638                rule_index,
1639                follow_state,
1640                ..
1641            } => {
1642                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
1643                    continue;
1644                };
1645                let child_key = (*target, child_stop);
1646                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
1647                    ctx.hit_cycle = true;
1648                }
1649                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
1650                first.symbols.extend_from(&child.symbols);
1651                if child.nullable {
1652                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
1653                }
1654            }
1655            Transition::Atom { .. }
1656            | Transition::Range { .. }
1657            | Transition::Set { .. }
1658            | Transition::NotSet { .. }
1659            | Transition::Wildcard { .. } => {}
1660        }
1661    }
1662}
1663
1664/// Returns token types that can resume parsing from `state_number` after a
1665/// failed child rule, following rule calls as well as epsilon transitions.
1666fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
1667    let mut symbols = BTreeSet::new();
1668    state_sync_symbols_inner(
1669        atn,
1670        state_number,
1671        stop_state,
1672        &mut BTreeSet::new(),
1673        &mut symbols,
1674    );
1675    symbols
1676}
1677
1678/// Walks epsilon-like continuations from a parent follow state until it finds
1679/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
1680fn state_sync_symbols_inner(
1681    atn: &Atn,
1682    state_number: usize,
1683    stop_state: usize,
1684    visited: &mut BTreeSet<usize>,
1685    symbols: &mut BTreeSet<i32>,
1686) {
1687    if !visited.insert(state_number) {
1688        return;
1689    }
1690    if state_number == stop_state {
1691        symbols.insert(TOKEN_EOF);
1692        return;
1693    }
1694    let Some(state) = atn.state(state_number) else {
1695        return;
1696    };
1697    for transition in &state.transitions {
1698        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
1699        if transition_symbols.is_empty() {
1700            match transition {
1701                Transition::Rule { target, .. }
1702                | Transition::Epsilon { target }
1703                | Transition::Action { target, .. }
1704                | Transition::Predicate { target, .. }
1705                | Transition::Precedence { target, .. } => {
1706                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
1707                }
1708                Transition::Atom { .. }
1709                | Transition::Range { .. }
1710                | Transition::Set { .. }
1711                | Transition::NotSet { .. }
1712                | Transition::Wildcard { .. } => {}
1713            }
1714        } else {
1715            symbols.extend(transition_symbols);
1716        }
1717    }
1718}
1719
1720fn state_can_reach_symbol_with_precedence(
1721    atn: &Atn,
1722    state_number: usize,
1723    symbol: i32,
1724    precedence: i32,
1725    visited: &mut BTreeSet<usize>,
1726) -> bool {
1727    if !visited.insert(state_number) {
1728        return false;
1729    }
1730    let Some(state) = atn.state(state_number) else {
1731        return false;
1732    };
1733    state.transitions.iter().any(|transition| {
1734        if transition.matches(symbol, 1, atn.max_token_type()) {
1735            return true;
1736        }
1737        if !transition.is_epsilon() {
1738            return false;
1739        }
1740        if matches!(
1741            transition,
1742            Transition::Precedence {
1743                precedence: transition_precedence,
1744                ..
1745            } if *transition_precedence < precedence
1746        ) {
1747            return false;
1748        }
1749        state_can_reach_symbol_with_precedence(
1750            atn,
1751            transition.target(),
1752            symbol,
1753            precedence,
1754            visited,
1755        )
1756    })
1757}
1758
1759fn context_can_match_symbol_before_state(
1760    atn: &Atn,
1761    context: &PredictionContext,
1762    stop_state_number: usize,
1763    symbol: i32,
1764) -> bool {
1765    (0..context.len()).any(|index| {
1766        context.return_state(index).is_some_and(|return_state| {
1767            let parent = context
1768                .parent(index)
1769                .unwrap_or_else(PredictionContext::empty);
1770            state_or_parent_can_match_symbol_before_state(
1771                atn,
1772                return_state,
1773                &parent,
1774                stop_state_number,
1775                symbol,
1776                &mut BTreeSet::new(),
1777            )
1778        })
1779    })
1780}
1781
1782fn state_or_parent_can_match_symbol_before_state(
1783    atn: &Atn,
1784    state_number: usize,
1785    parent: &Rc<PredictionContext>,
1786    stop_state_number: usize,
1787    symbol: i32,
1788    visited: &mut BTreeSet<usize>,
1789) -> bool {
1790    if state_number == EMPTY_RETURN_STATE {
1791        return false;
1792    }
1793    if state_number == stop_state_number {
1794        return context_can_match_symbol_before_state(atn, parent, stop_state_number, symbol);
1795    }
1796    if !visited.insert(state_number) {
1797        return false;
1798    }
1799    let Some(state) = atn.state(state_number) else {
1800        return false;
1801    };
1802    state.transitions.iter().any(|transition| {
1803        if transition.matches(symbol, 1, atn.max_token_type()) {
1804            return true;
1805        }
1806        transition.is_epsilon()
1807            && state_or_parent_can_match_symbol_before_state(
1808                atn,
1809                transition.target(),
1810                parent,
1811                stop_state_number,
1812                symbol,
1813                visited,
1814            )
1815    })
1816}
1817
1818/// Carries recovery expectations and their restart state through epsilon-only
1819/// paths. ANTLR can report and repair at the decision state even when the
1820/// failed consuming transition is nested under block or loop epsilon edges.
1821fn next_recovery_context(
1822    atn: &Atn,
1823    state: &AtnState,
1824    inherited: &BTreeSet<i32>,
1825    inherited_state: Option<usize>,
1826) -> (BTreeSet<i32>, Option<usize>) {
1827    let state_symbols = state_expected_symbols(atn, state.state_number);
1828    if state.transitions.len() > 1 && !state_symbols.is_empty() {
1829        let mut symbols = state_symbols;
1830        symbols.extend(inherited.iter().copied());
1831        return (symbols, Some(state.state_number));
1832    }
1833    (inherited.clone(), inherited_state)
1834}
1835
1836fn recovery_expected_symbols(
1837    atn: &Atn,
1838    state_number: usize,
1839    inherited: &BTreeSet<i32>,
1840) -> BTreeSet<i32> {
1841    let mut symbols = state_expected_symbols(atn, state_number);
1842    symbols.extend(inherited.iter().copied());
1843    symbols
1844}
1845
1846/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
1847/// parser's cached state-expected-symbols sets and the inherited `Rc`
1848/// without copying when the state cannot widen recovery.
1849fn fast_next_recovery_context<S>(
1850    parser: &mut BaseParser<S>,
1851    atn: &Atn,
1852    state: &AtnState,
1853    inherited: &Rc<BTreeSet<i32>>,
1854    inherited_state: Option<usize>,
1855) -> (Rc<BTreeSet<i32>>, Option<usize>)
1856where
1857    S: TokenSource,
1858{
1859    if state.transitions.len() <= 1 {
1860        return (Rc::clone(inherited), inherited_state);
1861    }
1862    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number);
1863    if state_symbols.is_empty() {
1864        return (Rc::clone(inherited), inherited_state);
1865    }
1866    if inherited.is_empty() {
1867        return (state_symbols, Some(state.state_number));
1868    }
1869    if Rc::ptr_eq(&state_symbols, inherited) {
1870        return (state_symbols, Some(state.state_number));
1871    }
1872    let mut combined = (*state_symbols).clone();
1873    combined.extend(inherited.iter().copied());
1874    (
1875        parser.intern_recovery_symbols(combined),
1876        Some(state.state_number),
1877    )
1878}
1879
1880/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
1881/// cached state-expected-symbols and avoids cloning when no widening is
1882/// needed.
1883fn fast_recovery_expected_symbols<S>(
1884    parser: &mut BaseParser<S>,
1885    atn: &Atn,
1886    state_number: usize,
1887    inherited: &Rc<BTreeSet<i32>>,
1888) -> Rc<BTreeSet<i32>>
1889where
1890    S: TokenSource,
1891{
1892    let cached = parser.cached_state_expected_symbols(atn, state_number);
1893    if inherited.is_empty() {
1894        return cached;
1895    }
1896    if cached.is_empty() {
1897        return Rc::clone(inherited);
1898    }
1899    if Rc::ptr_eq(&cached, inherited) {
1900        return cached;
1901    }
1902    let mut combined = (*cached).clone();
1903    combined.extend(inherited.iter().copied());
1904    parser.intern_recovery_symbols(combined)
1905}
1906
1907/// Applies generated integer-member side effects to one speculative path.
1908fn apply_member_actions(
1909    source_state: usize,
1910    actions: &[ParserMemberAction],
1911    values: &mut BTreeMap<usize, i64>,
1912) {
1913    for action in actions
1914        .iter()
1915        .filter(|action| action.source_state == source_state)
1916    {
1917        *values.entry(action.member).or_default() += action.delta;
1918    }
1919}
1920
1921/// Returns the speculative member state after replaying one ATN action state.
1922fn member_values_after_action(
1923    source_state: usize,
1924    actions: &[ParserMemberAction],
1925    values: &BTreeMap<usize, i64>,
1926) -> BTreeMap<usize, i64> {
1927    let mut values = values.clone();
1928    apply_member_actions(source_state, actions, &mut values);
1929    values
1930}
1931
1932/// Returns the speculative rule-return state after replaying one ATN action.
1933fn return_values_after_action(
1934    source_state: usize,
1935    rule_index: usize,
1936    actions: &[ParserReturnAction],
1937    values: &BTreeMap<String, i64>,
1938) -> BTreeMap<String, i64> {
1939    let mut values = values.clone();
1940    for action in actions
1941        .iter()
1942        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
1943    {
1944        values.insert(action.name.to_owned(), action.value);
1945    }
1946    values
1947}
1948
1949/// Resolves the integer argument visible to a child rule invocation.
1950fn rule_local_int_arg(
1951    rule_args: &[ParserRuleArg],
1952    source_state: usize,
1953    rule_index: usize,
1954    local_int_arg: Option<(usize, i64)>,
1955) -> Option<(usize, i64)> {
1956    rule_args
1957        .iter()
1958        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
1959        .map(|arg| {
1960            let value = if arg.inherit_local {
1961                local_int_arg.map_or(arg.value, |(_, value)| value)
1962            } else {
1963                arg.value
1964            };
1965            (rule_index, value)
1966        })
1967}
1968
1969/// Builds the terminal recognition outcome for a path that reached its stop
1970/// state.
1971fn stop_outcome(
1972    index: usize,
1973    consumed_eof: bool,
1974    rule_alt_number: usize,
1975    member_values: BTreeMap<usize, i64>,
1976    return_values: BTreeMap<String, i64>,
1977) -> Vec<RecognizeOutcome> {
1978    vec![RecognizeOutcome {
1979        index,
1980        consumed_eof,
1981        alt_number: rule_alt_number,
1982        member_values,
1983        return_values,
1984        diagnostics: Vec::new(),
1985        decisions: Vec::new(),
1986        actions: Vec::new(),
1987        nodes: Vec::new(),
1988    }]
1989}
1990
1991#[derive(Clone, Debug, Eq, PartialEq)]
1992struct RecognizeRequest<'a> {
1993    state_number: usize,
1994    stop_state: usize,
1995    index: usize,
1996    rule_start_index: usize,
1997    decision_start_index: Option<usize>,
1998    init_action_rules: &'a BTreeSet<usize>,
1999    predicates: &'a [(usize, usize, ParserPredicate)],
2000    rule_args: &'a [ParserRuleArg],
2001    member_actions: &'a [ParserMemberAction],
2002    return_actions: &'a [ParserReturnAction],
2003    local_int_arg: Option<(usize, i64)>,
2004    member_values: BTreeMap<usize, i64>,
2005    return_values: BTreeMap<String, i64>,
2006    rule_alt_number: usize,
2007    track_alt_numbers: bool,
2008    consumed_eof: bool,
2009    /// Current left-recursive precedence threshold, matching ANTLR's
2010    /// `precpred(_ctx, k)` check for generated precedence rules.
2011    precedence: i32,
2012    depth: usize,
2013    recovery_symbols: BTreeSet<i32>,
2014    recovery_state: Option<usize>,
2015}
2016
2017#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2018struct RecognizeKey {
2019    state_number: usize,
2020    stop_state: usize,
2021    index: usize,
2022    rule_start_index: usize,
2023    decision_start_index: Option<usize>,
2024    local_int_arg: Option<(usize, i64)>,
2025    member_values: BTreeMap<usize, i64>,
2026    return_values: BTreeMap<String, i64>,
2027    rule_alt_number: usize,
2028    track_alt_numbers: bool,
2029    consumed_eof: bool,
2030    precedence: i32,
2031    recovery_symbols: BTreeSet<i32>,
2032    recovery_state: Option<usize>,
2033}
2034
2035#[derive(Clone, Debug, Eq, PartialEq)]
2036struct EpsilonActionStep {
2037    source_state: usize,
2038    target: usize,
2039    action_rule_index: Option<usize>,
2040    left_recursive_boundary: Option<usize>,
2041    decision: Option<usize>,
2042    decision_start_index: Option<usize>,
2043    alt_number: usize,
2044    recovery_symbols: BTreeSet<i32>,
2045    recovery_state: Option<usize>,
2046}
2047
2048struct RecognizeScratch<'a> {
2049    visiting: &'a mut BTreeSet<RecognizeKey>,
2050    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2051    expected: &'a mut ExpectedTokens,
2052}
2053
2054#[derive(Clone, Debug, Eq, PartialEq)]
2055struct FastRecognizeRequest {
2056    state_number: usize,
2057    stop_state: usize,
2058    index: usize,
2059    rule_start_index: usize,
2060    decision_start_index: Option<usize>,
2061    precedence: i32,
2062    depth: usize,
2063    recovery_symbols: Rc<BTreeSet<i32>>,
2064    recovery_state: Option<usize>,
2065}
2066
2067#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2068struct FastRecognizeTopRequest {
2069    start_state: usize,
2070    stop_state: usize,
2071    start_index: usize,
2072    precedence: i32,
2073    caller_follow_state: Option<usize>,
2074}
2075
2076/// Memo key for the fast recognizer. `recovery_symbols` must come from
2077/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
2078/// key, so equal sets share one allocation and the key can store that
2079/// allocation's address instead of cloning an `Rc` and walking the full
2080/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
2081/// into distinct cache coordinates.
2082#[derive(Clone, Debug)]
2083struct FastRecognizeKey {
2084    state_number: usize,
2085    stop_state: usize,
2086    index: usize,
2087    rule_start_index: usize,
2088    decision_start_index: Option<usize>,
2089    precedence: i32,
2090    recovery_symbols_id: usize,
2091    recovery_state: Option<usize>,
2092}
2093
2094impl PartialEq for FastRecognizeKey {
2095    fn eq(&self, other: &Self) -> bool {
2096        if self.state_number != other.state_number
2097            || self.stop_state != other.stop_state
2098            || self.index != other.index
2099            || self.rule_start_index != other.rule_start_index
2100            || self.decision_start_index != other.decision_start_index
2101            || self.precedence != other.precedence
2102            || self.recovery_state != other.recovery_state
2103            || self.recovery_symbols_id != other.recovery_symbols_id
2104        {
2105            return false;
2106        }
2107        true
2108    }
2109}
2110
2111impl Eq for FastRecognizeKey {}
2112
2113impl Hash for FastRecognizeKey {
2114    fn hash<H: Hasher>(&self, hasher: &mut H) {
2115        self.state_number.hash(hasher);
2116        self.stop_state.hash(hasher);
2117        self.index.hash(hasher);
2118        self.rule_start_index.hash(hasher);
2119        self.decision_start_index.hash(hasher);
2120        self.precedence.hash(hasher);
2121        self.recovery_state.hash(hasher);
2122        self.recovery_symbols_id.hash(hasher);
2123    }
2124}
2125
2126struct FastRecoveryRequest<'a, 'b> {
2127    atn: &'a Atn,
2128    transition: &'a Transition,
2129    expected_symbols: Rc<BTreeSet<i32>>,
2130    target: usize,
2131    request: FastRecognizeRequest,
2132    visiting: &'b mut FxHashSet<(usize, usize)>,
2133    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
2134    expected: &'b mut ExpectedTokens,
2135}
2136
2137struct FastCurrentTokenDeletionRequest<'a, 'b> {
2138    atn: &'a Atn,
2139    expected_symbols: Rc<BTreeSet<i32>>,
2140    request: FastRecognizeRequest,
2141    visiting: &'b mut FxHashSet<(usize, usize)>,
2142    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
2143    expected: &'b mut ExpectedTokens,
2144}
2145
2146#[derive(Clone, Copy)]
2147struct FastChildRuleFailureRecoveryRequest<'a> {
2148    atn: &'a Atn,
2149    rule_index: usize,
2150    start_index: usize,
2151    follow_state: usize,
2152    stop_state: usize,
2153    expected: &'a ExpectedTokens,
2154}
2155
2156struct RecoveryRequest<'a, 'b> {
2157    atn: &'a Atn,
2158    transition: &'a Transition,
2159    expected_symbols: BTreeSet<i32>,
2160    target: usize,
2161    request: RecognizeRequest<'a>,
2162    visiting: &'b mut BTreeSet<RecognizeKey>,
2163    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2164    expected: &'b mut ExpectedTokens,
2165}
2166
2167struct CurrentTokenDeletionRequest<'a, 'b> {
2168    atn: &'a Atn,
2169    expected_symbols: BTreeSet<i32>,
2170    request: RecognizeRequest<'a>,
2171    visiting: &'b mut BTreeSet<RecognizeKey>,
2172    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
2173    expected: &'b mut ExpectedTokens,
2174}
2175
2176/// Carries the state needed after the normal token-recovery strategies fail
2177/// for a consuming transition.
2178struct ConsumingFailureFallback<'a> {
2179    atn: &'a Atn,
2180    target: usize,
2181    request: RecognizeRequest<'a>,
2182    symbol: i32,
2183    expected_symbols: BTreeSet<i32>,
2184    decision_start_index: Option<usize>,
2185    decision: Option<usize>,
2186}
2187
2188/// Captures the parent-rule context needed when a called rule fails before it
2189/// can produce a normal outcome.
2190struct ChildRuleFailureRecovery<'a> {
2191    atn: &'a Atn,
2192    rule_index: usize,
2193    start_index: usize,
2194    follow_state: usize,
2195    stop_state: usize,
2196    member_values: BTreeMap<usize, i64>,
2197    expected: &'a ExpectedTokens,
2198}
2199
2200/// Bundles the context needed to evaluate one semantic predicate transition.
2201#[derive(Clone, Copy, Debug)]
2202struct PredicateEval<'a> {
2203    index: usize,
2204    rule_index: usize,
2205    pred_index: usize,
2206    predicates: &'a [(usize, usize, ParserPredicate)],
2207    context: Option<&'a ParserRuleContext>,
2208    local_int_arg: Option<(usize, i64)>,
2209    member_values: &'a BTreeMap<usize, i64>,
2210}
2211
2212/// Captures predicate-failure recovery metadata for fail-option predicates.
2213struct PredicateFailureRecovery<'a> {
2214    rule_index: usize,
2215    index: usize,
2216    message: &'a str,
2217    member_values: BTreeMap<usize, i64>,
2218    return_values: BTreeMap<String, i64>,
2219    rule_alt_number: usize,
2220}
2221
2222#[derive(Debug)]
2223enum DirectAdaptiveParseControl {
2224    Fallback(DirectAdaptiveFallback),
2225}
2226
2227#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2228enum DirectAdaptiveFallback {
2229    Action,
2230    InvalidAlt,
2231    LeftRecursiveBoundary,
2232    MissingAtn,
2233    NoTransition,
2234    Predicate,
2235    Prediction,
2236    Precedence,
2237    RuleStop,
2238    SemanticContext,
2239    StepLimit,
2240    TokenMismatch,
2241    UnknownDecision,
2242}
2243
2244type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
2245
2246struct DirectAdaptiveParser<'atn, 'sim, S>
2247where
2248    S: TokenSource,
2249{
2250    parser: &'sim mut BaseParser<S>,
2251    atn: &'atn Atn,
2252    simulator: &'sim mut ParserAtnSimulator<'atn>,
2253    decision_by_state: Vec<Option<usize>>,
2254    steps: usize,
2255}
2256
2257/// Outcome of a generated token / set / not-set match that may recover.
2258///
2259/// Generated parsers append `children` to the current rule context. `consumed_eof`
2260/// reports whether the match actually consumed a real EOF terminal — it is true
2261/// only on a successful match (or single-token deletion that lands on EOF), and
2262/// always false on single-token insertion, which synthesizes a missing token and
2263/// consumes nothing. Generated code feeds this into `finish_rule`'s
2264/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
2265/// truly matched, matching ANTLR's `matchedEOF` semantics.
2266#[derive(Clone, Debug, Eq, PartialEq)]
2267pub struct GeneratedMatch {
2268    children: Vec<ParseTree>,
2269    consumed_eof: bool,
2270}
2271
2272impl GeneratedMatch {
2273    /// Parse-tree children produced by the match (the matched terminal, an
2274    /// error node plus deleted-then-matched terminal, or a single missing-token
2275    /// error node).
2276    #[must_use]
2277    pub fn children(&self) -> &[ParseTree] {
2278        &self.children
2279    }
2280
2281    /// Consumes the result, returning the children for appending to the rule
2282    /// context.
2283    #[must_use]
2284    pub fn into_children(self) -> Vec<ParseTree> {
2285        self.children
2286    }
2287
2288    /// Whether a real EOF terminal was consumed by this match.
2289    #[must_use]
2290    pub const fn consumed_eof(&self) -> bool {
2291        self.consumed_eof
2292    }
2293}
2294
2295impl<S> BaseParser<S>
2296where
2297    S: TokenSource,
2298{
2299    /// Creates a parser base over a buffered token stream and recognizer
2300    /// metadata.
2301    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
2302        Self {
2303            input,
2304            data,
2305            build_parse_trees: true,
2306            syntax_errors: 0,
2307            report_diagnostic_errors: false,
2308            prediction_mode: PredictionMode::Ll,
2309            prediction_diagnostics: Vec::new(),
2310            reported_prediction_diagnostics: BTreeSet::new(),
2311            generated_parser_diagnostics: Vec::new(),
2312            generated_sync_expected: None,
2313            int_members: BTreeMap::new(),
2314            rule_context_stack: Vec::new(),
2315            rule_context_version: 0,
2316            prediction_context_cache: None,
2317            pending_invoking_states: Vec::new(),
2318            precedence_stack: vec![0],
2319            invoked_predicates: Vec::new(),
2320            rule_first_set_cache: Vec::new(),
2321            state_expected_cache: FxHashMap::default(),
2322            state_expected_token_cache: FxHashMap::default(),
2323            rule_stop_reach_cache: Vec::new(),
2324            recovery_symbols_intern: FxHashMap::default(),
2325            decision_lookahead_cache: FxHashMap::default(),
2326            ll1_decision_cache: FxHashMap::default(),
2327            empty_cycle_cache: Vec::new(),
2328            single_outcome_memo_mode: SingleOutcomeMemoMode::Probe,
2329            single_outcome_probe_seen: FxHashSet::default(),
2330            single_outcome_probe_samples: 0,
2331            single_outcome_probe_repeats: 0,
2332            empty_recovery_symbols: Rc::new(BTreeSet::new()),
2333            fast_first_set_prefilter: true,
2334            fast_recovery_enabled: true,
2335            fast_token_nodes_enabled: true,
2336        }
2337    }
2338
2339    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
2340        &mut self.input
2341    }
2342
2343    /// Returns the token stream owned by this parser.
2344    #[must_use]
2345    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
2346        &self.input
2347    }
2348
2349    /// Consumes this parser and returns its token stream.
2350    #[must_use]
2351    pub fn into_token_stream(self) -> CommonTokenStream<S> {
2352        self.input
2353    }
2354
2355    /// Returns the number of parser syntax errors recorded by committed parse
2356    /// paths so far.
2357    pub const fn number_of_syntax_errors(&self) -> usize {
2358        self.syntax_errors
2359    }
2360
2361    /// Records a syntax error that generated parser code returns as fatal before
2362    /// it can recover into the current rule context.
2363    pub const fn record_generated_syntax_error(&mut self) {
2364        self.record_syntax_errors(1);
2365    }
2366
2367    const fn record_syntax_errors(&mut self, count: usize) {
2368        self.syntax_errors = self.syntax_errors.saturating_add(count);
2369    }
2370
2371    /// Emits diagnostics buffered by the token stream while generated parser
2372    /// code was fetching lexer tokens directly.
2373    pub fn report_token_source_errors(&mut self) {
2374        report_token_source_errors(&self.input.drain_source_errors());
2375    }
2376
2377    /// Captures generated-parser diagnostics and syntax-error count before a
2378    /// speculative generated rule path.
2379    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
2380        GeneratedDiagnosticsCheckpoint {
2381            diagnostics_len: self.generated_parser_diagnostics.len(),
2382            syntax_errors: self.syntax_errors,
2383        }
2384    }
2385
2386    /// Restores generated-parser diagnostics after a speculative rule path failed.
2387    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
2388        self.generated_parser_diagnostics
2389            .truncate(marker.diagnostics_len);
2390        self.syntax_errors = marker.syntax_errors;
2391        self.generated_sync_expected = None;
2392    }
2393
2394    /// Emits diagnostics recorded by committed generated parser recovery.
2395    pub fn report_generated_parser_diagnostics(&mut self) {
2396        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
2397        let token_errors = self.input.drain_source_errors();
2398        report_generated_diagnostics(&parser_diagnostics, &token_errors);
2399    }
2400
2401    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
2402    /// decision code.
2403    pub fn record_generated_ambiguity_diagnostic(
2404        &mut self,
2405        atn: &Atn,
2406        state_number: usize,
2407        start_index: usize,
2408        stop_index: usize,
2409        alts: &[usize],
2410    ) {
2411        if !self.report_diagnostic_errors || alts.len() < 2 {
2412            return;
2413        }
2414        let Some(decision) = atn
2415            .decision_to_state()
2416            .iter()
2417            .position(|candidate| *candidate == state_number)
2418        else {
2419            return;
2420        };
2421        let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
2422            return;
2423        };
2424        let rule_name = self
2425            .rule_names()
2426            .get(rule_index)
2427            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
2428        let input = display_input_text(&self.input.text(start_index, stop_index));
2429        let alts = alts
2430            .iter()
2431            .map(usize::to_string)
2432            .collect::<Vec<_>>()
2433            .join(", ");
2434        let key = (decision, start_index, format!("{alts}:{input}"));
2435        if !self.reported_prediction_diagnostics.insert(key) {
2436            return;
2437        }
2438        let start_token = self.token_at(start_index);
2439        let stop_token = self.token_at(stop_index);
2440        self.generated_parser_diagnostics.push(diagnostic_for_token(
2441            start_token.as_ref(),
2442            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
2443        ));
2444        self.generated_parser_diagnostics.push(diagnostic_for_token(
2445            stop_token.as_ref(),
2446            format!(
2447                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
2448            ),
2449        ));
2450    }
2451
2452    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
2453    /// parser calls to the adaptive simulator.
2454    pub fn record_generated_prediction_diagnostic(
2455        &mut self,
2456        atn: &Atn,
2457        state_number: usize,
2458        prediction: &ParserAtnPrediction,
2459    ) {
2460        let Some(diagnostic) = &prediction.diagnostic else {
2461            return;
2462        };
2463        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
2464            return;
2465        }
2466        let Some(decision) = atn
2467            .decision_to_state()
2468            .iter()
2469            .position(|candidate| *candidate == state_number)
2470        else {
2471            return;
2472        };
2473        let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
2474            return;
2475        };
2476        let rule_name = self
2477            .rule_names()
2478            .get(rule_index)
2479            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
2480        let attempt_input = display_input_text(
2481            &self
2482                .input
2483                .text(diagnostic.start_index, diagnostic.sll_stop_index),
2484        );
2485        let result_input = display_input_text(
2486            &self
2487                .input
2488                .text(diagnostic.start_index, diagnostic.ll_stop_index),
2489        );
2490        let alts = diagnostic
2491            .conflicting_alts
2492            .iter()
2493            .map(usize::to_string)
2494            .collect::<Vec<_>>()
2495            .join(", ");
2496        let key = (
2497            decision,
2498            diagnostic.start_index,
2499            format!(
2500                "{:?}:{alts}:{attempt_input}:{result_input}",
2501                diagnostic.kind
2502            ),
2503        );
2504        if !self.reported_prediction_diagnostics.insert(key) {
2505            return;
2506        }
2507        let attempt_token = self.token_at(diagnostic.sll_stop_index);
2508        self.generated_parser_diagnostics.push(diagnostic_for_token(
2509            attempt_token.as_ref(),
2510            format!(
2511                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
2512            ),
2513        ));
2514        let result_token = self.token_at(diagnostic.ll_stop_index);
2515        let message = match diagnostic.kind {
2516            ParserAtnPredictionDiagnosticKind::Ambiguity => {
2517                format!(
2518                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
2519                )
2520            }
2521            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
2522                format!(
2523                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
2524                )
2525            }
2526        };
2527        self.generated_parser_diagnostics
2528            .push(diagnostic_for_token(result_token.as_ref(), message));
2529    }
2530
2531    pub fn la(&mut self, offset: isize) -> i32 {
2532        self.input.la_token(offset)
2533    }
2534
2535    pub fn consume(&mut self) {
2536        IntStream::consume(&mut self.input);
2537    }
2538
2539    /// Sets a generated integer member value used by target-template tests.
2540    pub fn set_int_member(&mut self, member: usize, value: i64) {
2541        self.int_members.insert(member, value);
2542    }
2543
2544    /// Reads a generated integer member value.
2545    pub fn int_member(&self, member: usize) -> Option<i64> {
2546        self.int_members.get(&member).copied()
2547    }
2548
2549    /// Captures generated integer members before speculative generated parser
2550    /// execution.
2551    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
2552        self.int_members.clone()
2553    }
2554
2555    /// Restores generated integer members after generated parser fallback.
2556    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
2557        self.int_members = members;
2558    }
2559
2560    /// Adds `delta` to a generated integer member and returns the new value.
2561    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
2562        let value = self.int_members.entry(member).or_default();
2563        *value += delta;
2564        *value
2565    }
2566
2567    /// Matches and consumes the current token when it has the expected token
2568    /// type.
2569    ///
2570    /// On success the consumed token is wrapped as a terminal parse-tree node.
2571    /// On mismatch the error carries vocabulary display names so diagnostics are
2572    /// stable across literal and symbolic token naming.
2573    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
2574        let current = self
2575            .input
2576            .lt_ref(1)
2577            .ok_or_else(|| AntlrError::ParserError {
2578                line: 0,
2579                column: 0,
2580                message: "missing current token".to_owned(),
2581            })?;
2582        if current.token_type() == token_type {
2583            self.consume();
2584            Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
2585        } else {
2586            Err(AntlrError::MismatchedInput {
2587                expected: self.vocabulary().display_name(token_type),
2588                found: self.vocabulary().display_name(current.token_type()),
2589            })
2590        }
2591    }
2592
2593    /// Matches a token from generated recursive-descent code, including ANTLR's
2594    /// single-token insertion recovery when the active rule context can legally
2595    /// continue at the current input symbol.
2596    pub fn match_token_recovering(
2597        &mut self,
2598        token_type: i32,
2599        follow_state: usize,
2600        atn: &Atn,
2601    ) -> Result<GeneratedMatch, AntlrError> {
2602        let current = self
2603            .input
2604            .lt_ref(1)
2605            .ok_or_else(|| AntlrError::ParserError {
2606                line: 0,
2607                column: 0,
2608                message: "missing current token".to_owned(),
2609            })?;
2610        if current.token_type() == token_type {
2611            self.generated_sync_expected = None;
2612            let consumed_eof = current.token_type() == TOKEN_EOF;
2613            self.consume();
2614            return Ok(GeneratedMatch {
2615                children: vec![ParseTree::Terminal(TerminalNode::from_ref(current))],
2616                consumed_eof,
2617            });
2618        }
2619        let mut expected_symbols = BTreeSet::new();
2620        expected_symbols.insert(token_type);
2621        self.recover_generated_match(
2622            current.as_ref().clone(),
2623            &expected_symbols,
2624            follow_state,
2625            atn,
2626            |symbol| symbol == token_type,
2627        )
2628    }
2629
2630    pub fn match_set_recovering(
2631        &mut self,
2632        intervals: &[(i32, i32)],
2633        follow_state: usize,
2634        atn: &Atn,
2635    ) -> Result<GeneratedMatch, AntlrError> {
2636        let current = self
2637            .input
2638            .lt_ref(1)
2639            .ok_or_else(|| AntlrError::ParserError {
2640                line: 0,
2641                column: 0,
2642                message: "missing current token".to_owned(),
2643            })?;
2644        if interval_set_contains(intervals, current.token_type()) {
2645            self.generated_sync_expected = None;
2646            let consumed_eof = current.token_type() == TOKEN_EOF;
2647            self.consume();
2648            return Ok(GeneratedMatch {
2649                children: vec![ParseTree::Terminal(TerminalNode::from_ref(current))],
2650                consumed_eof,
2651            });
2652        }
2653        let expected_symbols = interval_symbols(intervals);
2654        self.recover_generated_match(
2655            current.as_ref().clone(),
2656            &expected_symbols,
2657            follow_state,
2658            atn,
2659            |symbol| interval_set_contains(intervals, symbol),
2660        )
2661    }
2662
2663    pub fn match_not_set_recovering(
2664        &mut self,
2665        intervals: &[(i32, i32)],
2666        min_vocabulary: i32,
2667        max_vocabulary: i32,
2668        follow_state: usize,
2669        atn: &Atn,
2670    ) -> Result<GeneratedMatch, AntlrError> {
2671        let current = self
2672            .input
2673            .lt_ref(1)
2674            .ok_or_else(|| AntlrError::ParserError {
2675                line: 0,
2676                column: 0,
2677                message: "missing current token".to_owned(),
2678            })?;
2679        if (min_vocabulary..=max_vocabulary).contains(&current.token_type())
2680            && !interval_set_contains(intervals, current.token_type())
2681        {
2682            self.generated_sync_expected = None;
2683            let consumed_eof = current.token_type() == TOKEN_EOF;
2684            self.consume();
2685            return Ok(GeneratedMatch {
2686                children: vec![ParseTree::Terminal(TerminalNode::from_ref(current))],
2687                consumed_eof,
2688            });
2689        }
2690        let expected_symbols =
2691            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
2692        self.recover_generated_match(
2693            current.as_ref().clone(),
2694            &expected_symbols,
2695            follow_state,
2696            atn,
2697            |symbol| {
2698                (min_vocabulary..=max_vocabulary).contains(&symbol)
2699                    && !interval_set_contains(intervals, symbol)
2700            },
2701        )
2702    }
2703
2704    fn recover_generated_match(
2705        &mut self,
2706        current: CommonToken,
2707        expected_symbols: &BTreeSet<i32>,
2708        follow_state: usize,
2709        atn: &Atn,
2710        matches: impl Fn(i32) -> bool,
2711    ) -> Result<GeneratedMatch, AntlrError> {
2712        let expected_display = self.expected_symbols_display(expected_symbols);
2713        if current.token_type() != TOKEN_EOF
2714            && let Some(next) = self.input.lt(2).cloned()
2715            && matches(next.token_type())
2716        {
2717            let message = format!(
2718                "extraneous input {} expecting {expected_display}",
2719                token_input_display(&current)
2720            );
2721            self.push_generated_parser_diagnostic(diagnostic_for_token(Some(&current), message));
2722            self.record_syntax_errors(1);
2723            self.generated_sync_expected = None;
2724            // Single-token deletion: skip `current`, then accept `next`. The
2725            // accepted token can be EOF only if it is a real EOF terminal.
2726            let consumed_eof = next.token_type() == TOKEN_EOF;
2727            self.consume();
2728            self.consume();
2729            return Ok(GeneratedMatch {
2730                children: vec![
2731                    ParseTree::Error(ErrorNode::new(current)),
2732                    ParseTree::Terminal(TerminalNode::new(next)),
2733                ],
2734                consumed_eof,
2735            });
2736        }
2737        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
2738        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
2739        // *after* the current element can consume the current symbol. At EOF that
2740        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
2741        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
2742        // when EOF merely leaks in from the empty enclosing context (as in
2743        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
2744        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
2745        // so consult the follow state's OWN expected set for the explicit case.
2746        let follow_explicitly_expects_eof = current.token_type() == TOKEN_EOF
2747            && self
2748                .cached_state_expected_symbols(atn, follow_state)
2749                .contains(&TOKEN_EOF);
2750        if follow_symbols.contains(&current.token_type())
2751            && (current.token_type() != TOKEN_EOF
2752                || self.rule_context_stack.len() > 1
2753                || expected_symbols.is_empty()
2754                || follow_explicitly_expects_eof)
2755        {
2756            let message = format!(
2757                "missing {expected_display} at {}",
2758                token_input_display(&current)
2759            );
2760            self.push_generated_parser_diagnostic(diagnostic_for_token(Some(&current), message));
2761            self.record_syntax_errors(1);
2762            self.generated_sync_expected = None;
2763            let token_type = expected_symbols.iter().next().copied().unwrap_or(TOKEN_EOF);
2764            let mut missing_symbol = BTreeSet::new();
2765            missing_symbol.insert(token_type);
2766            let missing_display = self.expected_symbols_display(&missing_symbol);
2767            let token = CommonToken::new(token_type)
2768                .with_text(format!("<missing {missing_display}>"))
2769                .with_span(usize::MAX, usize::MAX)
2770                .with_position(current.line(), current.column());
2771            // Single-token insertion synthesizes a missing token and consumes
2772            // nothing, so no EOF terminal is consumed even when the lookahead is
2773            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
2774            // from recording EOF as the rule stop on this recovery path.
2775            return Ok(GeneratedMatch {
2776                children: vec![ParseTree::Error(ErrorNode::new(token))],
2777                consumed_eof: false,
2778            });
2779        }
2780        let mismatch_expected = self.generated_sync_expected.take().map_or_else(
2781            || expected_symbols.clone(),
2782            |symbols| symbols.to_btree_set(),
2783        );
2784        let mismatch_expected_display = self.expected_symbols_display(&mismatch_expected);
2785        Err(AntlrError::ParserError {
2786            line: current.line(),
2787            column: current.column(),
2788            message: format!(
2789                "mismatched input {} expecting {mismatch_expected_display}",
2790                token_input_display(&current)
2791            ),
2792        })
2793    }
2794
2795    fn generated_recovery_follow_symbols(
2796        &mut self,
2797        atn: &Atn,
2798        follow_state: usize,
2799    ) -> BTreeSet<i32> {
2800        let mut follow = self
2801            .cached_state_expected_symbols(atn, follow_state)
2802            .as_ref()
2803            .clone();
2804        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
2805            follow.extend(self.context_expected_symbols(atn));
2806        }
2807        follow
2808    }
2809
2810    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
2811        self.match_token(TOKEN_EOF)
2812    }
2813
2814    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
2815        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
2816    }
2817
2818    pub fn match_not_set(
2819        &mut self,
2820        intervals: &[(i32, i32)],
2821        min_vocabulary: i32,
2822        max_vocabulary: i32,
2823    ) -> Result<ParseTree, AntlrError> {
2824        self.match_interval_condition(intervals, |symbol| {
2825            (min_vocabulary..=max_vocabulary).contains(&symbol)
2826                && !interval_set_contains(intervals, symbol)
2827        })
2828    }
2829
2830    fn match_interval_condition(
2831        &mut self,
2832        intervals: &[(i32, i32)],
2833        matches: impl FnOnce(i32) -> bool,
2834    ) -> Result<ParseTree, AntlrError> {
2835        let current = self
2836            .input
2837            .lt_ref(1)
2838            .ok_or_else(|| AntlrError::ParserError {
2839                line: 0,
2840                column: 0,
2841                message: "missing current token".to_owned(),
2842            })?;
2843        if matches(current.token_type()) {
2844            self.consume();
2845            Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
2846        } else {
2847            Err(AntlrError::MismatchedInput {
2848                expected: self.interval_display(intervals),
2849                found: self.vocabulary().display_name(current.token_type()),
2850            })
2851        }
2852    }
2853
2854    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
2855        let values = intervals
2856            .iter()
2857            .map(|(start, stop)| {
2858                if start == stop {
2859                    self.vocabulary().display_name(*start)
2860                } else {
2861                    format!(
2862                        "{}..{}",
2863                        self.vocabulary().display_name(*start),
2864                        self.vocabulary().display_name(*stop)
2865                    )
2866                }
2867            })
2868            .collect::<Vec<_>>()
2869            .join(", ");
2870        format!("{{{values}}}")
2871    }
2872
2873    pub const fn rule_node(&self, context: ParserRuleContext) -> ParseTree {
2874        ParseTree::Rule(RuleNode::new(context))
2875    }
2876
2877    /// Enters a generated parser rule and returns the context object the
2878    /// generated method should populate.
2879    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
2880        self.set_state(state);
2881        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
2882        self.rule_context_stack.push(RuleContextFrame {
2883            rule_index,
2884            invoking_state,
2885        });
2886        self.invalidate_prediction_context_cache();
2887        let start_index = self.current_visible_index();
2888        let mut context = ParserRuleContext::new(rule_index, invoking_state);
2889        if let Some(token) = self.token_ref_at(start_index) {
2890            context.set_start_ref(token);
2891        }
2892        context
2893    }
2894
2895    /// Records the ATN source state for the next generated rule invocation.
2896    ///
2897    /// ANTLR's full-context prediction reconstructs caller follow states from
2898    /// each active rule context's invoking state. Generated Rust rule methods are
2899    /// plain functions, so the caller supplies that ATN state just before making a
2900    /// rule call; `enter_rule` consumes it when the callee starts.
2901    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
2902        let marker = self.pending_invoking_states.len();
2903        self.pending_invoking_states.push(invoking_state);
2904        marker
2905    }
2906
2907    /// Discards an invoking-state marker if the callee did not consume it.
2908    pub fn discard_invoking_state(&mut self, marker: usize) {
2909        self.pending_invoking_states.truncate(marker);
2910    }
2911
2912    /// Exits the current generated parser rule.
2913    pub fn exit_rule(&mut self) {
2914        self.rule_context_stack.pop();
2915        self.invalidate_prediction_context_cache();
2916    }
2917
2918    /// Converts the active generated-parser rule stack into an ANTLR prediction
2919    /// context for full-context adaptive prediction.
2920    pub fn prediction_context(&mut self, atn: &Atn) -> Rc<PredictionContext> {
2921        let atn_ptr: *const Atn = atn;
2922        let atn_key = atn_ptr as usize;
2923        if let Some(cached) = &self.prediction_context_cache
2924            && cached.version == self.rule_context_version
2925            && cached.atn_key == atn_key
2926        {
2927            return Rc::clone(&cached.context);
2928        }
2929        let mut context = PredictionContext::empty();
2930        for frame in self.rule_context_stack.iter().skip(1) {
2931            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
2932                continue;
2933            };
2934            let Some(Transition::Rule { follow_state, .. }) = atn
2935                .state(state_number)
2936                .and_then(|state| state.transitions.first())
2937            else {
2938                continue;
2939            };
2940            context = PredictionContext::singleton(context, *follow_state);
2941        }
2942        self.prediction_context_cache = Some(CachedPredictionContext {
2943            version: self.rule_context_version,
2944            atn_key,
2945            context: Rc::clone(&context),
2946        });
2947        context
2948    }
2949
2950    fn invalidate_prediction_context_cache(&mut self) {
2951        self.rule_context_version = self.rule_context_version.wrapping_add(1);
2952        self.prediction_context_cache = None;
2953    }
2954
2955    /// Adds a generated parser child only when parse-tree construction is
2956    /// enabled. The match is recorded on the context either way (via `add_child`,
2957    /// or `note_matched_child` when trees are off) so generated recovery can tell
2958    /// whether the rule has matched anything yet without depending on `children`.
2959    pub fn add_parse_child(&self, context: &mut ParserRuleContext, child: ParseTree) {
2960        if self.build_parse_trees {
2961            context.add_child(child);
2962        } else {
2963            context.note_matched_child();
2964        }
2965    }
2966
2967    /// Finishes a generated parser rule and returns its parse-tree node.
2968    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
2969        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
2970        if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
2971            context.set_stop_ref(token);
2972        }
2973        self.exit_rule();
2974        self.rule_node(context)
2975    }
2976
2977    /// Recovers a generated rule catch block after a committed mismatch.
2978    ///
2979    /// ANTLR's generated parsers catch recognition errors inside each rule,
2980    /// report the original error, then consume unexpected tokens until the
2981    /// caller's recovery set can resume. Tokens consumed during recovery become
2982    /// error nodes in the current rule context.
2983    pub fn recover_generated_rule(
2984        &mut self,
2985        context: &mut ParserRuleContext,
2986        atn: &Atn,
2987        error: AntlrError,
2988    ) {
2989        let diagnostic = self.generated_rule_error_diagnostic(error);
2990        self.push_generated_parser_diagnostic(diagnostic);
2991        self.generated_sync_expected = None;
2992        let recovery_symbols = self.context_expected_symbols(atn);
2993        loop {
2994            let symbol = self.la(1);
2995            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
2996                break;
2997            }
2998            let Some(token) = self.input.lt(1).cloned() else {
2999                break;
3000            };
3001            self.consume();
3002            self.add_parse_child(context, ParseTree::Error(ErrorNode::new(token)));
3003        }
3004        self.record_syntax_errors(1);
3005    }
3006
3007    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
3008        if self
3009            .generated_parser_diagnostics
3010            .iter()
3011            .any(|existing| existing == &diagnostic)
3012        {
3013            return;
3014        }
3015        self.generated_parser_diagnostics.push(diagnostic);
3016    }
3017
3018    fn generated_rule_error_diagnostic(&mut self, error: AntlrError) -> ParserDiagnostic {
3019        match error {
3020            AntlrError::ParserError {
3021                line,
3022                column,
3023                message,
3024            } => ParserDiagnostic {
3025                line,
3026                column,
3027                message,
3028            },
3029            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
3030                self.input.lt(1),
3031                format!("mismatched input {found} expecting {expected}"),
3032            ),
3033            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
3034                self.input.lt(1),
3035                format!("no viable alternative at input {input}"),
3036            ),
3037            AntlrError::LexerError {
3038                line,
3039                column,
3040                message,
3041            } => ParserDiagnostic {
3042                line,
3043                column,
3044                message,
3045            },
3046            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
3047        }
3048    }
3049
3050    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
3051    pub fn finish_recursion_rule(
3052        &mut self,
3053        mut context: ParserRuleContext,
3054        consumed_eof: bool,
3055    ) -> ParseTree {
3056        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
3057        if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
3058            context.set_stop_ref(token);
3059        }
3060        self.unroll_recursion_context();
3061        self.rule_node(context)
3062    }
3063
3064    /// Enters a generated left-recursive rule at `precedence`.
3065    pub fn enter_recursion_rule(
3066        &mut self,
3067        state: isize,
3068        rule_index: usize,
3069        precedence: i32,
3070    ) -> ParserRuleContext {
3071        self.precedence_stack.push(precedence);
3072        self.enter_rule(state, rule_index)
3073    }
3074
3075    /// Replaces the current context while expanding a left-recursive rule.
3076    pub fn push_new_recursion_context(
3077        &mut self,
3078        state: isize,
3079        rule_index: usize,
3080    ) -> ParserRuleContext {
3081        self.set_state(state);
3082        ParserRuleContext::new(rule_index, state)
3083    }
3084
3085    /// Wraps the previous left-recursive context before parsing the next
3086    /// recursive operator alternative.
3087    pub fn push_new_recursion_context_with_previous(
3088        &mut self,
3089        state: isize,
3090        rule_index: usize,
3091        current: &mut ParserRuleContext,
3092    ) {
3093        self.set_state(state);
3094        if let Some(stop) = self
3095            .rule_stop_token_index(self.input.index(), false)
3096            .and_then(|index| self.token_ref_at(index))
3097        {
3098            current.set_stop_ref(stop);
3099        }
3100        let invoking_state = current.invoking_state();
3101        let start = current.start_ref();
3102        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
3103        if let Some(start) = start {
3104            replacement.set_start_ref(start);
3105        }
3106        let previous = std::mem::replace(current, replacement);
3107        if self.build_parse_trees {
3108            current.add_child(self.rule_node(previous));
3109        }
3110    }
3111
3112    /// Leaves a generated left-recursive rule.
3113    pub fn unroll_recursion_context(&mut self) {
3114        if self.precedence_stack.len() > 1 {
3115            self.precedence_stack.pop();
3116        }
3117        self.exit_rule();
3118    }
3119
3120    /// Checks whether a generated left-recursive loop has an operator
3121    /// alternative that can start at the current token under the active
3122    /// precedence. The operator block still performs adaptive prediction; this
3123    /// guard only decides whether the loop should enter or exit.
3124    pub fn left_recursive_loop_enter_matches(
3125        &mut self,
3126        atn: &Atn,
3127        state_number: usize,
3128        precedence: i32,
3129    ) -> bool {
3130        let symbol = self.la(1);
3131        if symbol == TOKEN_EOF {
3132            return false;
3133        }
3134        let Some(state) = atn.state(state_number) else {
3135            return false;
3136        };
3137        let context = self.prediction_context(atn);
3138        if context_can_match_symbol_before_state(atn, &context, state_number, symbol) {
3139            return false;
3140        }
3141        state.transitions.iter().any(|transition| {
3142            let target = transition.target();
3143            if atn
3144                .state(target)
3145                .is_some_and(|state| state.kind == AtnStateKind::LoopEnd)
3146            {
3147                return false;
3148            }
3149            state_can_reach_symbol_with_precedence(
3150                atn,
3151                target,
3152                symbol,
3153                precedence,
3154                &mut BTreeSet::new(),
3155            )
3156        })
3157    }
3158
3159    /// Implements generated `precpred(_ctx, k)` checks.
3160    pub fn precpred(&self, precedence: i32) -> bool {
3161        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
3162    }
3163
3164    /// Evaluates a generated parser semantic predicate at the current input
3165    /// position.
3166    pub fn parser_semantic_predicate_matches(
3167        &mut self,
3168        predicates: &[(usize, usize, ParserPredicate)],
3169        rule_index: usize,
3170        pred_index: usize,
3171    ) -> bool {
3172        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
3173    }
3174
3175    /// Evaluates a generated parser semantic predicate with the current integer
3176    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
3177    pub fn parser_semantic_predicate_matches_with_local(
3178        &mut self,
3179        predicates: &[(usize, usize, ParserPredicate)],
3180        rule_index: usize,
3181        pred_index: usize,
3182        local_int_arg: i32,
3183    ) -> bool {
3184        self.parser_semantic_predicate_matches_inner(
3185            predicates,
3186            rule_index,
3187            pred_index,
3188            Some((rule_index, i64::from(local_int_arg))),
3189        )
3190    }
3191
3192    fn parser_semantic_predicate_matches_inner(
3193        &mut self,
3194        predicates: &[(usize, usize, ParserPredicate)],
3195        rule_index: usize,
3196        pred_index: usize,
3197        local_int_arg: Option<(usize, i64)>,
3198    ) -> bool {
3199        let index = self.input.index();
3200        let member_values = self.int_members.clone();
3201        self.parser_predicate_matches(PredicateEval {
3202            index,
3203            rule_index,
3204            pred_index,
3205            predicates,
3206            context: None,
3207            local_int_arg,
3208            member_values: &member_values,
3209        })
3210    }
3211
3212    /// Evaluates a generated parser semantic predicate with access to the
3213    /// current generated rule context.
3214    pub fn parser_semantic_predicate_matches_with_context_and_local(
3215        &mut self,
3216        predicates: &[(usize, usize, ParserPredicate)],
3217        rule_index: usize,
3218        pred_index: usize,
3219        context: &ParserRuleContext,
3220        local_int_arg: i32,
3221    ) -> bool {
3222        let index = self.input.index();
3223        let member_values = self.int_members.clone();
3224        self.parser_predicate_matches(PredicateEval {
3225            index,
3226            rule_index,
3227            pred_index,
3228            predicates,
3229            context: Some(context),
3230            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
3231            member_values: &member_values,
3232        })
3233    }
3234
3235    /// Returns a generated fail-option message for a parser semantic
3236    /// predicate coordinate.
3237    pub fn parser_semantic_predicate_failure_message(
3238        &self,
3239        rule_index: usize,
3240        pred_index: usize,
3241        predicates: &[(usize, usize, ParserPredicate)],
3242    ) -> Option<&'static str> {
3243        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
3244    }
3245
3246    /// Matches any non-EOF token.
3247    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
3248        let current = self
3249            .input
3250            .lt_ref(1)
3251            .ok_or_else(|| AntlrError::ParserError {
3252                line: 0,
3253                column: 0,
3254                message: "missing current token".to_owned(),
3255            })?;
3256        if current.token_type() == TOKEN_EOF {
3257            return Err(AntlrError::MismatchedInput {
3258                expected: "wildcard".to_owned(),
3259                found: self.vocabulary().display_name(TOKEN_EOF),
3260            });
3261        }
3262        self.consume();
3263        Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
3264    }
3265
3266    /// Generated parser synchronization hook. The current interpreter owns
3267    /// recovery; direct generated methods can call this as a no-op until the
3268    /// generated recovery strategy is expanded.
3269    #[allow(clippy::unnecessary_wraps)]
3270    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
3271        self.set_state(state);
3272        Ok(())
3273    }
3274
3275    /// Synchronizes a generated parser decision against the ATN lookahead set.
3276    ///
3277    /// ANTLR generated parsers call the error strategy before optional and loop
3278    /// decisions. When the current token cannot start any alternative, follow a
3279    /// nullable exit, or be deleted before a later synchronization token, the
3280    /// generated Rust method reports that decision-level mismatch instead of
3281    /// descending into a child rule that cannot start at the current token.
3282    pub fn sync_decision(
3283        &mut self,
3284        atn: &Atn,
3285        state_number: usize,
3286        current_context_empty: bool,
3287        loop_back: bool,
3288    ) -> Result<Vec<ParseTree>, AntlrError> {
3289        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
3290        self.generated_sync_expected = None;
3291        let Some(state) = atn.state(state_number) else {
3292            return Ok(Vec::new());
3293        };
3294        let Some(rule_index) = state.rule_index else {
3295            return Ok(Vec::new());
3296        };
3297        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index).copied() else {
3298            return Ok(Vec::new());
3299        };
3300        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
3301        let symbol = self.la(1);
3302        let mut has_expected_symbols = false;
3303        let mut nullable = false;
3304        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
3305        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
3306        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
3307        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
3308        // and worth deleting; an implicit-follow EOF means the loop should simply
3309        // exit and leave the token for the (absent) caller — matching ANTLR, which
3310        // exits the loop via prediction rather than consuming up to a synthetic EOF.
3311        let mut explicit_eof_expected = false;
3312        for transition in &entry.transitions {
3313            if transition.symbols.contains(symbol) {
3314                return Ok(Vec::new());
3315            }
3316            has_expected_symbols |= !transition.symbols.is_empty();
3317            nullable |= transition.nullable;
3318            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
3319        }
3320        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
3321        if nullable {
3322            if context_expected
3323                .as_ref()
3324                .is_some_and(|expected| expected.contains(symbol))
3325            {
3326                return Ok(Vec::new());
3327            }
3328        }
3329        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
3330            return Ok(Vec::new());
3331        }
3332        let mut expected = TokenBitSet::default();
3333        for transition in &entry.transitions {
3334            expected.extend_from(&transition.symbols);
3335        }
3336        if let Some(context_expected) = context_expected {
3337            expected.extend_from(&context_expected);
3338        }
3339        let can_delete_in_place =
3340            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
3341        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
3342        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
3343        // least one iteration) does `consumeUntil` the follow set — multi-token
3344        // deletion, one error per skipped token across iterations; a loop ENTRY
3345        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
3346        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
3347        // unexpected token only when LA(2) is expected, otherwise report a mismatch
3348        // and leave recovery to the rule.
3349        //
3350        // The generated loop always presents the loop-ENTRY state to this method on
3351        // every pass, so `state.kind` cannot distinguish entry from back; the caller
3352        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
3353        // an iteration has been taken, and true on a `+` loop's first sync since its
3354        // mandatory first element is iteration 1). Treating a loop entry as a
3355        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
3356        // `c`s, which ANTLR rejects with `mismatched input`).
3357        let loop_sync = loop_back;
3358        if symbol != TOKEN_EOF && can_delete_in_place {
3359            let mut cursor = self.input.index();
3360            let mut skipped = Vec::new();
3361            loop {
3362                let current = self.token_type_at(cursor);
3363                if current == TOKEN_EOF {
3364                    break;
3365                }
3366                skipped.push(cursor);
3367                let next = self.consume_index(cursor, current);
3368                if next == cursor {
3369                    break;
3370                }
3371                let next_symbol = self.token_type_at(next);
3372                // Stop (and delete the skipped tokens as error nodes) when the next
3373                // token is a real expected continuation. EOF counts only when it is
3374                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
3375                // genuinely extraneous and the generated EOF match consumes the real
3376                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
3377                // rule-follow) does NOT count — the loop must exit and leave the
3378                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
3379                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
3380                    explicit_eof_expected
3381                } else {
3382                    expected.contains(next_symbol)
3383                };
3384                if next_is_expected_stop {
3385                    let current_token = self.input.lt(1).cloned();
3386                    let expected_symbols = expected.to_btree_set();
3387                    let message = format!(
3388                        "extraneous input {} expecting {}",
3389                        current_token
3390                            .as_ref()
3391                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
3392                        self.expected_symbols_display(&expected_symbols)
3393                    );
3394                    self.push_generated_parser_diagnostic(diagnostic_for_token(
3395                        current_token.as_ref(),
3396                        message,
3397                    ));
3398                    self.record_syntax_errors(1);
3399                    let mut children = Vec::with_capacity(skipped.len());
3400                    for index in skipped {
3401                        if let Some(token) = self.token_at(index) {
3402                            self.consume();
3403                            children.push(ParseTree::Error(ErrorNode::new(token)));
3404                        }
3405                    }
3406                    return Ok(children);
3407                }
3408                // A non-loop block entry deletes at most one token (single-token
3409                // deletion): if LA(2) is not expected, stop scanning so the mismatch
3410                // is reported at the first token instead of skipping ahead.
3411                if !loop_sync {
3412                    break;
3413                }
3414                cursor = next;
3415            }
3416        }
3417        if nullable {
3418            self.generated_sync_expected = Some(expected);
3419            return Ok(Vec::new());
3420        }
3421        let current = self.input.lt(1).cloned();
3422        let expected_symbols = expected.to_btree_set();
3423        Err(AntlrError::ParserError {
3424            line: current.as_ref().map(Token::line).unwrap_or_default(),
3425            column: current.as_ref().map(Token::column).unwrap_or_default(),
3426            message: format!(
3427                "mismatched input {} expecting {}",
3428                current
3429                    .as_ref()
3430                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
3431                self.expected_symbols_display(&expected_symbols)
3432            ),
3433        })
3434    }
3435
3436    /// Returns a generated-parser prediction when one token of lookahead
3437    /// uniquely selects an alternative for `state_number`.
3438    ///
3439    /// This mirrors the interpreter's LL(1) commit point and lets generated
3440    /// recursive-descent methods avoid invoking the adaptive simulator for
3441    /// simple optional/block/loop decisions.
3442    pub fn ll1_decision_prediction(
3443        &mut self,
3444        atn: &Atn,
3445        state_number: usize,
3446    ) -> Option<ParserAtnPrediction> {
3447        let state = atn.state(state_number)?;
3448        if state.precedence_rule_decision {
3449            return None;
3450        }
3451        let rule_stop = state
3452            .rule_index
3453            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index).copied())?;
3454        let symbol = self.la(1);
3455        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
3456        ll1_greedy_alt(&entry, symbol, state.non_greedy).map(|alt| ParserAtnPrediction {
3457            alt: alt + 1,
3458            requires_full_context: false,
3459            has_semantic_context: false,
3460            diagnostic: None,
3461        })
3462    }
3463
3464    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
3465        let context = self.prediction_context(atn);
3466        let mut expected = BTreeSet::new();
3467        self.collect_context_expected_symbols(atn, &context, &mut expected);
3468        expected
3469    }
3470
3471    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
3472        let context = self.prediction_context(atn);
3473        let mut expected = TokenBitSet::default();
3474        self.collect_context_expected_token_set(atn, &context, &mut expected);
3475        expected
3476    }
3477
3478    fn collect_context_expected_symbols(
3479        &mut self,
3480        atn: &Atn,
3481        context: &Rc<PredictionContext>,
3482        expected: &mut BTreeSet<i32>,
3483    ) {
3484        if context.is_empty() {
3485            expected.insert(TOKEN_EOF);
3486            return;
3487        }
3488        for index in 0..context.len() {
3489            let Some(return_state) = context.return_state(index) else {
3490                continue;
3491            };
3492            if return_state == EMPTY_RETURN_STATE {
3493                expected.insert(TOKEN_EOF);
3494                continue;
3495            }
3496            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
3497            if self.cached_state_can_reach_rule_stop(atn, return_state)
3498                && let Some(parent) = context.parent(index)
3499            {
3500                self.collect_context_expected_symbols(atn, &parent, expected);
3501            }
3502        }
3503    }
3504
3505    fn collect_context_expected_token_set(
3506        &mut self,
3507        atn: &Atn,
3508        context: &Rc<PredictionContext>,
3509        expected: &mut TokenBitSet,
3510    ) {
3511        if context.is_empty() {
3512            expected.insert(TOKEN_EOF);
3513            return;
3514        }
3515        for index in 0..context.len() {
3516            let Some(return_state) = context.return_state(index) else {
3517                continue;
3518            };
3519            if return_state == EMPTY_RETURN_STATE {
3520                expected.insert(TOKEN_EOF);
3521                continue;
3522            }
3523            let state_expected = self.cached_state_expected_token_set(atn, return_state);
3524            expected.extend_from(&state_expected);
3525            if self.cached_state_can_reach_rule_stop(atn, return_state)
3526                && let Some(parent) = context.parent(index)
3527            {
3528                self.collect_context_expected_token_set(atn, &parent, expected);
3529            }
3530        }
3531    }
3532
3533    /// Builds a generated no-viable-alternative parser error.
3534    pub fn no_viable_alternative_error(&mut self, start_index: usize) -> AntlrError {
3535        let error_index = self.input.index();
3536        self.no_viable_alternative_error_at(start_index, error_index)
3537    }
3538
3539    /// Builds a generated no-viable-alternative parser error at the simulator's
3540    /// failing lookahead index. `adaptive_predict` restores the input cursor
3541    /// before returning, so generated parsers have to pass the recorded index
3542    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
3543    pub fn no_viable_alternative_error_at(
3544        &mut self,
3545        start_index: usize,
3546        error_index: usize,
3547    ) -> AntlrError {
3548        let diagnostic = self.no_viable_alternative(start_index, error_index);
3549        AntlrError::ParserError {
3550            line: diagnostic.line,
3551            column: diagnostic.column,
3552            message: diagnostic.message,
3553        }
3554    }
3555
3556    /// Builds a generated failed-predicate parser error.
3557    pub fn failed_predicate_error(&mut self, message: impl Into<String>) -> AntlrError {
3558        let current = self.input.lt(1).cloned();
3559        AntlrError::ParserError {
3560            line: current.as_ref().map(Token::line).unwrap_or_default(),
3561            column: current.as_ref().map(Token::column).unwrap_or_default(),
3562            message: format!("rule failed predicate: {}", message.into()),
3563        }
3564    }
3565
3566    /// Builds a generated parser error for a semantic predicate with ANTLR's
3567    /// `<fail='...'>` option.
3568    pub fn failed_predicate_option_error(
3569        &mut self,
3570        rule_index: usize,
3571        message: impl Into<String>,
3572    ) -> AntlrError {
3573        let current = self.input.lt(1).cloned();
3574        let rule_name = self
3575            .rule_names()
3576            .get(rule_index)
3577            .map_or_else(|| rule_index.to_string(), Clone::clone);
3578        AntlrError::ParserError {
3579            line: current.as_ref().map(Token::line).unwrap_or_default(),
3580            column: current.as_ref().map(Token::column).unwrap_or_default(),
3581            message: format!("rule {rule_name} {}", message.into()),
3582        }
3583    }
3584
3585    /// Builds a generated parser-action event at the current input position.
3586    pub fn parser_action_at_current(
3587        &mut self,
3588        source_state: usize,
3589        rule_index: usize,
3590        start_index: usize,
3591        consumed_eof: bool,
3592    ) -> ParserAction {
3593        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
3594        ParserAction::new(source_state, rule_index, start_index, stop_index)
3595    }
3596
3597    /// Attempts to execute a whole generated rule by committing simulator
3598    /// decisions directly. Unsupported constructs or decisions that need
3599    /// full-context / predicate evaluation restore the input cursor and fall
3600    /// back to [`Self::parse_atn_rule`].
3601    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
3602        &mut self,
3603        atn: &'atn Atn,
3604        simulator: &mut ParserAtnSimulator<'atn>,
3605        rule_index: usize,
3606    ) -> Result<ParseTree, AntlrError> {
3607        let start_index = self.current_visible_index();
3608        self.clear_prediction_diagnostics();
3609        self.reset_per_parse_caches();
3610        let mut decision_by_state = vec![None; atn.states().len()];
3611        for (decision, &state_number) in atn.decision_to_state().iter().enumerate() {
3612            if let Some(slot) = decision_by_state.get_mut(state_number) {
3613                *slot = Some(decision);
3614            }
3615        }
3616
3617        let result = DirectAdaptiveParser {
3618            parser: self,
3619            atn,
3620            simulator,
3621            decision_by_state,
3622            steps: 0,
3623        }
3624        .parse_rule(rule_index, -1, 0);
3625
3626        match result {
3627            Ok(tree) => {
3628                report_token_source_errors(&self.input.drain_source_errors());
3629                Ok(tree)
3630            }
3631            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
3632                let _ = reason;
3633                self.input.seek(start_index);
3634                self.parse_atn_rule(atn, rule_index)
3635            }
3636        }
3637    }
3638
3639    /// Parses a generated rule by interpreting the parser ATN from the rule's
3640    /// start state to its stop state.
3641    ///
3642    /// The recognizer backtracks across alternatives and loop exits using token
3643    /// stream indices instead of committing to input consumption immediately.
3644    /// Once a viable ATN path is found, the parser commits the accepted token
3645    /// interval and returns a rule node whose children mirror every grammar
3646    /// rule invocation reached on that path, matching ANTLR's parse-tree
3647    /// shape.
3648    pub fn parse_atn_rule(
3649        &mut self,
3650        atn: &Atn,
3651        rule_index: usize,
3652    ) -> Result<ParseTree, AntlrError> {
3653        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
3654    }
3655
3656    /// Parses a generated rule by interpreting the parser ATN with an initial
3657    /// left-recursive precedence threshold.
3658    pub fn parse_atn_rule_with_precedence(
3659        &mut self,
3660        atn: &Atn,
3661        rule_index: usize,
3662        precedence: i32,
3663    ) -> Result<ParseTree, AntlrError> {
3664        let start_state = atn
3665            .rule_to_start_state()
3666            .get(rule_index)
3667            .copied()
3668            .ok_or_else(|| {
3669                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
3670            })?;
3671        let stop_state = atn
3672            .rule_to_stop_state()
3673            .get(rule_index)
3674            .copied()
3675            .filter(|state| *state != usize::MAX)
3676            .ok_or_else(|| {
3677                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
3678            })?;
3679
3680        let start_index = self.current_visible_index();
3681        self.clear_prediction_diagnostics();
3682        self.reset_per_parse_caches();
3683        let caller_follow_state = self.pending_invoking_follow_state(atn);
3684        self.fast_recovery_enabled = false;
3685        self.fast_token_nodes_enabled = false;
3686        let top_request = FastRecognizeTopRequest {
3687            start_state,
3688            stop_state,
3689            start_index,
3690            precedence,
3691            caller_follow_state,
3692        };
3693        let first_pass = self.fast_recognize_top(atn, top_request);
3694        self.fast_token_nodes_enabled = true;
3695        self.fast_recovery_enabled = true;
3696        let needs_tree_retry = matches!(
3697            &first_pass,
3698            Ok((outcome, _)) if self.build_parse_trees && outcome.nodes.has_left_recursive_boundary()
3699        );
3700        let needs_retry = match &first_pass {
3701            // The FIRST-set prefilter trims speculative rule calls that can't
3702            // match the current lookahead — useful for perf on grammars with
3703            // many epsilon-reachable rules, but the trim also bypasses
3704            // single-token insertion / deletion recovery that ANTLR's
3705            // reference parser runs at the child rule's first consuming
3706            // transition. Retry without the prefilter whenever the first pass
3707            // either produced no outcome at all or produced a recovered
3708            // outcome (diagnostics non-empty), since the second pass might
3709            // surface a child-level recovery with cleaner diagnostics or
3710            // closer parity to ANTLR's tree shape. Left-recursive tree
3711            // boundaries also need the token-node pass; otherwise the fold has
3712            // no concrete left operand to wrap into ANTLR's recursive context.
3713            Err(_) => true,
3714            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
3715        };
3716        let (outcome, _expected) = if needs_retry {
3717            self.fast_first_set_prefilter = false;
3718            let retry = self.fast_recognize_top(atn, top_request);
3719            self.fast_first_set_prefilter = true;
3720            let selected = if needs_tree_retry {
3721                match retry {
3722                    ok @ Ok(_) => ok,
3723                    Err(_) => first_pass,
3724                }
3725            } else {
3726                select_better_top_outcome(first_pass, retry)
3727            };
3728            selected.map_err(|expected| {
3729                let error = self.recognition_error(rule_index, start_index, &expected);
3730                self.record_syntax_errors(1);
3731                report_token_source_errors(&self.input.drain_source_errors());
3732                error
3733            })?
3734        } else {
3735            first_pass.expect("first_pass is Ok in the no-retry branch")
3736        };
3737        self.record_syntax_errors(outcome.diagnostics.len());
3738        report_parser_diagnostics(&self.prediction_diagnostics);
3739        report_parser_diagnostics(&outcome.diagnostics);
3740        report_token_source_errors(&self.input.drain_source_errors());
3741        let mut context = ParserRuleContext::with_child_capacity(
3742            rule_index,
3743            self.state(),
3744            if self.build_parse_trees {
3745                outcome.nodes.len()
3746            } else {
3747                0
3748            },
3749        );
3750        if let Some(token) = self.token_ref_at(start_index) {
3751            context.set_start_ref(token);
3752        }
3753        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
3754        if let Some(token) = stop_index.and_then(|token_index| self.token_ref_at(token_index)) {
3755            context.set_stop_ref(token);
3756        }
3757        if self.build_parse_trees {
3758            if outcome.nodes.has_left_recursive_boundary() {
3759                let folded = fold_fast_left_recursive_boundaries(outcome.nodes.to_vec());
3760                if folded.iter().any(|node| {
3761                    matches!(
3762                        node.as_ref(),
3763                        FastRecognizedNode::Token { .. }
3764                            | FastRecognizedNode::ErrorToken { .. }
3765                            | FastRecognizedNode::MissingToken { .. }
3766                    )
3767                }) {
3768                    for node in &folded {
3769                        context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
3770                    }
3771                } else {
3772                    self.add_fast_implicit_token_children(
3773                        &mut context,
3774                        start_index,
3775                        stop_index,
3776                        &folded,
3777                    )?;
3778                }
3779            } else if outcome.nodes.has_explicit_token_node() {
3780                for node in outcome.nodes.iter() {
3781                    context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
3782                }
3783            } else {
3784                self.add_fast_implicit_token_children_iter(
3785                    &mut context,
3786                    start_index,
3787                    stop_index,
3788                    outcome.nodes.iter(),
3789                )?;
3790            }
3791        }
3792        self.input.seek(outcome.index);
3793
3794        Ok(self.rule_node(context))
3795    }
3796
3797    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
3798        let invoking_state = self.pending_invoking_states.last().copied()?;
3799        let state_number = usize::try_from(invoking_state).ok()?;
3800        match atn.state(state_number)?.transitions.first()? {
3801            Transition::Rule { follow_state, .. } => Some(*follow_state),
3802            _ => None,
3803        }
3804    }
3805
3806    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
3807        // Generated callers own statement separators; leave them available when
3808        // an interpreted child rule can either stop before or consume one.
3809        let token_type = self.token_type_at(index);
3810        let visible_channel = self.input.channel();
3811        let token = self.token_at(index);
3812        let is_boundary = token
3813            .as_ref()
3814            .and_then(Token::text)
3815            .is_some_and(is_caller_follow_boundary_text);
3816        let is_boundary_gap = token.as_ref().is_some_and(|token| {
3817            token.channel() != visible_channel
3818                || token.text().is_some_and(is_caller_follow_boundary_gap_text)
3819        });
3820        (token_type, is_boundary, is_boundary_gap)
3821    }
3822
3823    /// Runs the fast recognizer once from the rule's start state and returns
3824    /// the best outcome or the per-attempt expected-token accumulator. The
3825    /// caller flips `fast_first_set_prefilter` between calls when a retry is
3826    /// needed, so the FIRST-set cache is left intact across both passes.
3827    fn fast_recognize_top(
3828        &mut self,
3829        atn: &Atn,
3830        request: FastRecognizeTopRequest,
3831    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
3832        let FastRecognizeTopRequest {
3833            start_state,
3834            stop_state,
3835            start_index,
3836            precedence,
3837            caller_follow_state,
3838        } = request;
3839        // `input.size()` is intentionally only the currently buffered token
3840        // count here. Do not restore an up-front fill just to size this map:
3841        // the fixed floor avoids small-input churn, and large inputs grow the
3842        // cache after the deferred-fill threshold without forcing startup
3843        // tokenization. The 8x multiplier matches the empirical
3844        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
3845        // Kotlin ~12× memo entries per token), so the table avoids one
3846        // rehash on the typical hot path.
3847        let memo_capacity = self.input.size().saturating_mul(8).clamp(65_536, 524_288);
3848        let mut visiting = FxHashSet::with_capacity_and_hasher(256, FxBuildHasher::default());
3849        let mut memo = FxHashMap::with_capacity_and_hasher(memo_capacity, FxBuildHasher::default());
3850        let mut expected = ExpectedTokens::default();
3851        let empty_recovery = self.empty_recovery_symbols();
3852        let outcomes = self.recognize_state_fast(
3853            atn,
3854            FastRecognizeRequest {
3855                state_number: start_state,
3856                stop_state,
3857                index: start_index,
3858                rule_start_index: start_index,
3859                decision_start_index: None,
3860                precedence,
3861                depth: 0,
3862                recovery_symbols: empty_recovery,
3863                recovery_state: None,
3864            },
3865            &mut visiting,
3866            &mut memo,
3867            &mut expected,
3868        );
3869        #[cfg(feature = "perf-counters")]
3870        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
3871            perf_counters::dump();
3872            perf_counters::reset();
3873        }
3874        let caller_follow =
3875            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
3876        match select_best_fast_outcome(
3877            outcomes.into_iter(),
3878            self.prediction_mode,
3879            caller_follow.as_deref(),
3880            |index| self.caller_follow_token_info(index),
3881        ) {
3882            Some(outcome) => Ok((outcome, expected)),
3883            None => Err(expected),
3884        }
3885    }
3886
3887    /// Converts a recognized fast-recognizer node into a public parse-tree
3888    /// node, mirroring [`Self::recognized_node_tree`] for the slow path.
3889    fn fast_recognized_node_tree(
3890        &mut self,
3891        node: &FastRecognizedNode,
3892    ) -> Result<ParseTree, AntlrError> {
3893        match node {
3894            FastRecognizedNode::Token { index } => {
3895                let token = self
3896                    .input
3897                    .get_ref(*index)
3898                    .ok_or_else(|| AntlrError::ParserError {
3899                        line: 0,
3900                        column: 0,
3901                        message: format!("missing token at index {index}"),
3902                    })?;
3903                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
3904            }
3905            FastRecognizedNode::ErrorToken { index } => {
3906                let token = self
3907                    .input
3908                    .get_ref(*index)
3909                    .ok_or_else(|| AntlrError::ParserError {
3910                        line: 0,
3911                        column: 0,
3912                        message: format!("missing error token at index {index}"),
3913                    })?;
3914                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
3915            }
3916            FastRecognizedNode::MissingToken {
3917                token_type,
3918                at_index,
3919                text,
3920            } => {
3921                let current = self.token_at(*at_index);
3922                let token = CommonToken::new(*token_type)
3923                    .with_text(text.as_str())
3924                    .with_span(usize::MAX, usize::MAX)
3925                    .with_position(
3926                        current.as_ref().map(Token::line).unwrap_or_default(),
3927                        current.as_ref().map(Token::column).unwrap_or_default(),
3928                    );
3929                Ok(ParseTree::Error(ErrorNode::new(token)))
3930            }
3931            FastRecognizedNode::Rule {
3932                rule_index,
3933                invoking_state,
3934                start_index,
3935                stop_index,
3936                children,
3937            } => {
3938                let mut context = ParserRuleContext::with_child_capacity(
3939                    *rule_index,
3940                    *invoking_state,
3941                    children.len(),
3942                );
3943                if let Some(token) = self.token_ref_at(*start_index) {
3944                    context.set_start_ref(token);
3945                }
3946                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
3947                    context.set_stop_ref(token);
3948                }
3949                if children.has_left_recursive_boundary() {
3950                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
3951                    for child in &folded {
3952                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
3953                    }
3954                } else {
3955                    for child in children.iter() {
3956                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
3957                    }
3958                }
3959                Ok(self.rule_node(context))
3960            }
3961            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
3962                Err(AntlrError::Unsupported(format!(
3963                    "unfolded left-recursive boundary for rule {rule_index}"
3964                )))
3965            }
3966        }
3967    }
3968
3969    fn fast_recognized_node_tree_with_implicit_tokens(
3970        &mut self,
3971        node: &FastRecognizedNode,
3972    ) -> Result<ParseTree, AntlrError> {
3973        match node {
3974            FastRecognizedNode::Rule {
3975                rule_index,
3976                invoking_state,
3977                start_index,
3978                stop_index,
3979                children,
3980            } => {
3981                let mut context = ParserRuleContext::with_child_capacity(
3982                    *rule_index,
3983                    *invoking_state,
3984                    children.len(),
3985                );
3986                if let Some(token) = self.token_ref_at(*start_index) {
3987                    context.set_start_ref(token);
3988                }
3989                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
3990                    context.set_stop_ref(token);
3991                }
3992                if children.has_left_recursive_boundary() {
3993                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
3994                    self.add_fast_implicit_token_children(
3995                        &mut context,
3996                        *start_index,
3997                        *stop_index,
3998                        &folded,
3999                    )?;
4000                } else {
4001                    self.add_fast_implicit_token_children_iter(
4002                        &mut context,
4003                        *start_index,
4004                        *stop_index,
4005                        children.iter(),
4006                    )?;
4007                }
4008                Ok(self.rule_node(context))
4009            }
4010            _ => self.fast_recognized_node_tree(node),
4011        }
4012    }
4013
4014    fn add_fast_implicit_token_children(
4015        &mut self,
4016        context: &mut ParserRuleContext,
4017        start_index: usize,
4018        stop_index: Option<usize>,
4019        children: &[Rc<FastRecognizedNode>],
4020    ) -> Result<(), AntlrError> {
4021        self.add_fast_implicit_token_children_iter(
4022            context,
4023            start_index,
4024            stop_index,
4025            children.iter(),
4026        )
4027    }
4028
4029    fn add_fast_implicit_token_children_iter<'a>(
4030        &mut self,
4031        context: &mut ParserRuleContext,
4032        start_index: usize,
4033        stop_index: Option<usize>,
4034        children: impl IntoIterator<Item = &'a Rc<FastRecognizedNode>>,
4035    ) -> Result<(), AntlrError> {
4036        let mut cursor = Some(start_index);
4037        for child in children {
4038            if let Some((child_start, child_stop)) = fast_recognized_node_span(child.as_ref()) {
4039                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
4040                context.add_child(
4041                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
4042                );
4043                if let Some(child_stop) = child_stop {
4044                    cursor = self.next_visible_after_token(child_stop);
4045                }
4046            } else {
4047                context.add_child(
4048                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
4049                );
4050            }
4051        }
4052        if let Some(stop) = stop_index {
4053            self.add_visible_terminals_through(context, cursor, stop)?;
4054        }
4055        Ok(())
4056    }
4057
4058    fn add_visible_terminals_before(
4059        &mut self,
4060        context: &mut ParserRuleContext,
4061        cursor: &mut Option<usize>,
4062        before: usize,
4063    ) -> Result<(), AntlrError> {
4064        let Some(stop) = before.checked_sub(1) else {
4065            return Ok(());
4066        };
4067        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
4068        *cursor = next;
4069        Ok(())
4070    }
4071
4072    fn add_visible_terminals_through(
4073        &mut self,
4074        context: &mut ParserRuleContext,
4075        mut cursor: Option<usize>,
4076        stop: usize,
4077    ) -> Result<Option<usize>, AntlrError> {
4078        while let Some(index) = cursor {
4079            if index > stop {
4080                return Ok(Some(index));
4081            }
4082            let token = self
4083                .input
4084                .get_ref(index)
4085                .ok_or_else(|| AntlrError::ParserError {
4086                    line: 0,
4087                    column: 0,
4088                    message: format!("missing token at index {index}"),
4089                })?;
4090            let is_eof = token.token_type() == TOKEN_EOF;
4091            context.add_child(ParseTree::Terminal(TerminalNode::from_ref(token)));
4092            if is_eof {
4093                return Ok(None);
4094            }
4095            cursor = self.next_visible_after_token(index);
4096        }
4097        Ok(None)
4098    }
4099
4100    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
4101        let next = self.input.next_visible_after(index);
4102        (next != index).then_some(next)
4103    }
4104
4105    /// Parses a generated rule and returns semantic actions reached on the
4106    /// selected ATN path.
4107    ///
4108    /// This slower path preserves action ordering and token intervals for
4109    /// generated code that replays target-specific action templates after the
4110    /// recognizer has chosen one viable parse path.
4111    pub fn parse_atn_rule_with_actions(
4112        &mut self,
4113        atn: &Atn,
4114        rule_index: usize,
4115    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
4116        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
4117    }
4118
4119    /// Parses a generated rule and emits ATN actions plus selected rule-init
4120    /// actions reached on the chosen path.
4121    ///
4122    /// Generated parsers use this when a grammar contains rule-level `@init`
4123    /// templates that must run for nested rule invocations. The runtime keeps
4124    /// the action list path-sensitive, so init templates are replayed only for
4125    /// rules that were actually entered by the selected parse.
4126    pub fn parse_atn_rule_with_action_inits(
4127        &mut self,
4128        atn: &Atn,
4129        rule_index: usize,
4130        init_action_rules: &[usize],
4131    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
4132        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
4133    }
4134
4135    /// Parses a generated rule with optional semantic-action replay features.
4136    ///
4137    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
4138    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
4139    /// unchanged for grammars that do not request that target template.
4140    pub fn parse_atn_rule_with_action_options(
4141        &mut self,
4142        atn: &Atn,
4143        rule_index: usize,
4144        init_action_rules: &[usize],
4145        track_alt_numbers: bool,
4146    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
4147        self.parse_atn_rule_with_runtime_options(
4148            atn,
4149            rule_index,
4150            ParserRuntimeOptions {
4151                init_action_rules,
4152                track_alt_numbers,
4153                ..ParserRuntimeOptions::default()
4154            },
4155        )
4156    }
4157
4158    /// Parses a generated rule with action replay and parser predicate support.
4159    ///
4160    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
4161    /// target-template predicate semantics emitted by the generator. Missing
4162    /// entries are treated as true so unsupported predicate-free grammars keep
4163    /// the previous unconditional transition behavior.
4164    pub fn parse_atn_rule_with_runtime_options(
4165        &mut self,
4166        atn: &Atn,
4167        rule_index: usize,
4168        options: ParserRuntimeOptions<'_>,
4169    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
4170        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
4171    }
4172
4173    /// Parses a generated rule with action replay, parser predicate support,
4174    /// and an initial left-recursive precedence threshold.
4175    pub fn parse_atn_rule_with_runtime_options_and_precedence(
4176        &mut self,
4177        atn: &Atn,
4178        rule_index: usize,
4179        precedence: i32,
4180        options: ParserRuntimeOptions<'_>,
4181    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
4182        let ParserRuntimeOptions {
4183            init_action_rules,
4184            track_alt_numbers,
4185            predicates,
4186            rule_args,
4187            member_actions,
4188            return_actions,
4189        } = options;
4190        let start_state = atn
4191            .rule_to_start_state()
4192            .get(rule_index)
4193            .copied()
4194            .ok_or_else(|| {
4195                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
4196            })?;
4197        let stop_state = atn
4198            .rule_to_stop_state()
4199            .get(rule_index)
4200            .copied()
4201            .filter(|state| *state != usize::MAX)
4202            .ok_or_else(|| {
4203                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
4204            })?;
4205
4206        let start_index = self.current_visible_index();
4207        self.clear_prediction_diagnostics();
4208        self.reset_per_parse_caches();
4209        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
4210        let invoking_state = self.pending_invoking_states.pop();
4211        let local_int_arg = invoking_state
4212            .and_then(|state| usize::try_from(state).ok())
4213            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
4214        let mut visiting = BTreeSet::new();
4215        let mut memo = BTreeMap::new();
4216        let mut expected = ExpectedTokens::default();
4217        let member_values = self.int_members.clone();
4218        let return_values = BTreeMap::new();
4219        let outcomes = self.recognize_state(
4220            atn,
4221            RecognizeRequest {
4222                state_number: start_state,
4223                stop_state,
4224                index: start_index,
4225                rule_start_index: start_index,
4226                decision_start_index: None,
4227                init_action_rules: &init_action_rules,
4228                predicates,
4229                rule_args,
4230                member_actions,
4231                return_actions,
4232                local_int_arg,
4233                member_values,
4234                return_values,
4235                rule_alt_number: 0,
4236                track_alt_numbers,
4237                consumed_eof: false,
4238                precedence,
4239                depth: 0,
4240                recovery_symbols: BTreeSet::new(),
4241                recovery_state: None,
4242            },
4243            &mut visiting,
4244            &mut memo,
4245            &mut expected,
4246        );
4247        let Some(outcome) = select_best_outcome(outcomes.into_iter(), self.prediction_mode) else {
4248            let error = self.recognition_error(rule_index, start_index, &expected);
4249            self.record_syntax_errors(1);
4250            report_token_source_errors(&self.input.drain_source_errors());
4251            return Err(error);
4252        };
4253
4254        self.record_syntax_errors(outcome.diagnostics.len());
4255        report_parser_diagnostics(&self.prediction_diagnostics);
4256        report_parser_diagnostics(&outcome.diagnostics);
4257        report_token_source_errors(&self.input.drain_source_errors());
4258        let mut actions = outcome.actions;
4259        if init_action_rules.contains(&rule_index) {
4260            actions.insert(
4261                0,
4262                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
4263            );
4264        }
4265        let mut context =
4266            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
4267        if track_alt_numbers {
4268            context.set_alt_number(outcome.alt_number);
4269        }
4270        for (name, value) in outcome.return_values {
4271            context.set_int_return(name, value);
4272        }
4273        if let Some(token) = self.token_ref_at(start_index) {
4274            context.set_start_ref(token);
4275        }
4276        if let Some(token) = self.rule_stop_token_ref(outcome.index, outcome.consumed_eof) {
4277            context.set_stop_ref(token);
4278        }
4279        if self.build_parse_trees {
4280            let nodes = fold_left_recursive_boundaries(outcome.nodes);
4281            for node in &nodes {
4282                context.add_child(self.recognized_node_tree(node, track_alt_numbers)?);
4283            }
4284        }
4285        self.input.seek(outcome.index);
4286
4287        Ok((self.rule_node(context), actions))
4288    }
4289
4290    /// Temporary parser entry used by generated parser methods while the parser
4291    /// ATN simulator is being implemented.
4292    ///
4293    /// This keeps generated parser crates buildable and gives us a stable method
4294    /// surface for every grammar rule. It intentionally accepts all remaining
4295    /// tokens into one rule context; it is not the final parser semantics.
4296    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
4297        let mut context = ParserRuleContext::new(rule_index, self.state());
4298        while self.la(1) != TOKEN_EOF {
4299            let token_type = self.la(1);
4300            let child = self.match_token(token_type)?;
4301            if self.build_parse_trees {
4302                context.add_child(child);
4303            }
4304        }
4305        if self.build_parse_trees {
4306            context.add_child(self.match_eof()?);
4307        }
4308        Ok(self.rule_node(context))
4309    }
4310
4311    /// Builds the parser error reported when no ATN path can reach the active
4312    /// rule stop state.
4313    fn recognition_error(
4314        &mut self,
4315        rule_index: usize,
4316        start_index: usize,
4317        expected: &ExpectedTokens,
4318    ) -> AntlrError {
4319        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
4320        self.input.seek(index);
4321        let current = self.input.lt(1).cloned();
4322        let line = current.as_ref().map(Token::line).unwrap_or_default();
4323        let column = current.as_ref().map(Token::column).unwrap_or_default();
4324        AntlrError::ParserError {
4325            line,
4326            column,
4327            message,
4328        }
4329    }
4330
4331    /// Builds the token index and ANTLR-compatible message for a failed rule.
4332    fn expected_error_message(
4333        &mut self,
4334        rule_index: usize,
4335        start_index: usize,
4336        expected: &ExpectedTokens,
4337    ) -> (usize, String) {
4338        let index = expected
4339            .index
4340            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
4341            .unwrap_or_else(|| self.input.index());
4342        self.input.seek(index);
4343        let current = self.input.lt(1).cloned();
4344        let message = if expected
4345            .no_viable
4346            .as_ref()
4347            .is_some_and(|no_viable| no_viable.error_index == index)
4348        {
4349            let start = expected
4350                .no_viable
4351                .as_ref()
4352                .map_or(start_index, |no_viable| no_viable.start_index);
4353            let text = display_input_text(&self.input.text(start, index));
4354            format!("no viable alternative at input '{text}'")
4355        } else if expected.symbols.is_empty() {
4356            if expected.index.is_some() {
4357                let found = current
4358                    .as_ref()
4359                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
4360                if current
4361                    .as_ref()
4362                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
4363                {
4364                    format!(
4365                        "missing {} at {found}",
4366                        self.expected_symbols_display(&expected.symbols)
4367                    )
4368                } else {
4369                    format!("mismatched input {found}")
4370                }
4371            } else {
4372                format!("no viable alternative while parsing rule {rule_index}")
4373            }
4374        } else {
4375            format!(
4376                "mismatched input {} expecting {}",
4377                current
4378                    .as_ref()
4379                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
4380                self.expected_symbols_display(&expected.symbols)
4381            )
4382        };
4383        (index, message)
4384    }
4385
4386    /// Converts a failed child rule into a recovered outcome so the parent can
4387    /// continue after reporting the child diagnostic.
4388    fn child_rule_failure_recovery(
4389        &mut self,
4390        rule_index: usize,
4391        start_index: usize,
4392        sync_symbols: &BTreeSet<i32>,
4393        member_values: BTreeMap<usize, i64>,
4394        expected: &ExpectedTokens,
4395    ) -> Option<RecognizeOutcome> {
4396        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
4397        let token = self.token_at(error_index);
4398        let mut next_index = error_index;
4399        loop {
4400            let symbol = self.token_type_at(next_index);
4401            if sync_symbols.contains(&symbol) {
4402                if next_index == error_index {
4403                    return None;
4404                }
4405                break;
4406            }
4407            if symbol == TOKEN_EOF {
4408                break;
4409            }
4410            let after = self.consume_index(next_index, symbol);
4411            if after == next_index {
4412                break;
4413            }
4414            next_index = after;
4415        }
4416        Some(RecognizeOutcome {
4417            index: next_index,
4418            consumed_eof: false,
4419            alt_number: 0,
4420            member_values,
4421            return_values: BTreeMap::new(),
4422            diagnostics: vec![diagnostic_for_token(token.as_ref(), message)],
4423            decisions: Vec::new(),
4424            actions: Vec::new(),
4425            nodes: vec![RecognizedNode::ErrorToken { index: error_index }],
4426        })
4427    }
4428
4429    /// Adapts the optional recovery result to the normal outcome list used by
4430    /// rule-call transitions.
4431    fn child_rule_failure_recovery_outcomes(
4432        &mut self,
4433        request: ChildRuleFailureRecovery<'_>,
4434    ) -> Vec<RecognizeOutcome> {
4435        let sync_symbols =
4436            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
4437        self.child_rule_failure_recovery(
4438            request.rule_index,
4439            request.start_index,
4440            &sync_symbols,
4441            request.member_values,
4442            request.expected,
4443        )
4444        .into_iter()
4445        .collect()
4446    }
4447
4448    /// Formats expected token types using ANTLR's single-token or set syntax.
4449    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
4450        expected_symbols_display(symbols, self.vocabulary())
4451    }
4452
4453    /// Returns the single-token deletion repair if the token after `index`
4454    /// satisfies the failed consuming transition.
4455    fn single_token_deletion(
4456        &mut self,
4457        transition: &Transition,
4458        index: usize,
4459        max_token_type: i32,
4460        expected_symbols: &BTreeSet<i32>,
4461    ) -> Option<(ParserDiagnostic, usize, i32)> {
4462        let current_symbol = self.token_type_at(index);
4463        if current_symbol == TOKEN_EOF {
4464            return None;
4465        }
4466        let next_index = self.consume_index(index, current_symbol);
4467        if next_index == index {
4468            return None;
4469        }
4470        let next_symbol = self.token_type_at(next_index);
4471        if !transition.matches(next_symbol, 1, max_token_type) {
4472            return None;
4473        }
4474        let transition_expected = transition_expected_symbols(transition, max_token_type);
4475        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
4476            &transition_expected
4477        } else {
4478            expected_symbols
4479        });
4480        let current = self.token_at(index);
4481        let message = format!(
4482            "extraneous input {} expecting {expected_display}",
4483            current
4484                .as_ref()
4485                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
4486        );
4487        Some((
4488            diagnostic_for_token(current.as_ref(), message),
4489            next_index,
4490            next_symbol,
4491        ))
4492    }
4493
4494    /// Returns the repair used when deleting the current token lets a recovery
4495    /// state continue with the following token.
4496    fn current_token_deletion(
4497        &mut self,
4498        index: usize,
4499        expected_symbols: &BTreeSet<i32>,
4500    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
4501        if expected_symbols.is_empty() {
4502            return None;
4503        }
4504        let current_symbol = self.token_type_at(index);
4505        if current_symbol == TOKEN_EOF {
4506            return None;
4507        }
4508        let current = self.token_at(index);
4509        let message = format!(
4510            "extraneous input {} expecting {}",
4511            current
4512                .as_ref()
4513                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
4514            self.expected_symbols_display(expected_symbols)
4515        );
4516        let diagnostic = diagnostic_for_token(current.as_ref(), message);
4517        let mut skipped = Vec::new();
4518        let mut cursor = index;
4519        loop {
4520            let symbol = self.token_type_at(cursor);
4521            if symbol == TOKEN_EOF {
4522                return None;
4523            }
4524            skipped.push(cursor);
4525            let next_index = self.consume_index(cursor, symbol);
4526            if next_index == cursor {
4527                return None;
4528            }
4529            let next_symbol = self.token_type_at(next_index);
4530            if expected_symbols.contains(&next_symbol) {
4531                return Some((diagnostic, next_index, skipped));
4532            }
4533            cursor = next_index;
4534        }
4535    }
4536
4537    /// Returns the single-token insertion repair for a failed consuming
4538    /// transition. The caller validates the repair by continuing from the
4539    /// transition target at the same input index.
4540    fn single_token_insertion(
4541        &mut self,
4542        transition: &Transition,
4543        index: usize,
4544        max_token_type: i32,
4545        expected_symbols: &BTreeSet<i32>,
4546        follow_symbols: &BTreeSet<i32>,
4547    ) -> Option<(ParserDiagnostic, i32, String)> {
4548        let current_symbol = self.token_type_at(index);
4549        if !follow_symbols.contains(&current_symbol) {
4550            return None;
4551        }
4552        let transition_expected = transition_expected_symbols(transition, max_token_type);
4553        let token_type = transition_expected.iter().next().copied()?;
4554        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
4555            &transition_expected
4556        } else {
4557            expected_symbols
4558        });
4559        let mut token_symbols = BTreeSet::new();
4560        token_symbols.insert(token_type);
4561        let missing_token_display = self.expected_symbols_display(&token_symbols);
4562        let current = self.token_at(index);
4563        let message = format!(
4564            "missing {expected_display} at {}",
4565            current
4566                .as_ref()
4567                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
4568        );
4569        let text = format!("<missing {missing_token_display}>");
4570        Some((
4571            diagnostic_for_token(current.as_ref(), message),
4572            token_type,
4573            text,
4574        ))
4575    }
4576
4577    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
4578    /// skip the unexpected current token when the following token satisfies the
4579    /// transition that failed.
4580    fn fast_single_token_deletion_recovery(
4581        &mut self,
4582        recovery: FastRecoveryRequest<'_, '_>,
4583    ) -> Vec<FastRecognizeOutcome> {
4584        let FastRecoveryRequest {
4585            atn,
4586            transition,
4587            expected_symbols,
4588            target,
4589            request,
4590            visiting,
4591            memo,
4592            expected,
4593        } = recovery;
4594        let FastRecognizeRequest {
4595            stop_state,
4596            index,
4597            rule_start_index,
4598            decision_start_index,
4599            precedence,
4600            depth,
4601            ..
4602        } = request;
4603        let Some((diagnostic, next_index, next_symbol)) =
4604            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
4605        else {
4606            return Vec::new();
4607        };
4608        let after_next = self.consume_index(next_index, next_symbol);
4609        let empty_recovery = self.empty_recovery_symbols();
4610        self.recognize_state_fast(
4611            atn,
4612            FastRecognizeRequest {
4613                state_number: target,
4614                stop_state,
4615                index: after_next,
4616                rule_start_index,
4617                decision_start_index,
4618                precedence,
4619                depth: depth + 1,
4620                recovery_symbols: empty_recovery,
4621                recovery_state: None,
4622            },
4623            visiting,
4624            memo,
4625            expected,
4626        )
4627        .into_iter()
4628        .map(|mut outcome| {
4629            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
4630            outcome.diagnostics.insert(0, diagnostic.clone());
4631            if self.fast_token_nodes_enabled {
4632                outcome
4633                    .nodes
4634                    .prepend(Rc::new(FastRecognizedNode::Token { index: next_index }));
4635                outcome
4636                    .nodes
4637                    .prepend(Rc::new(FastRecognizedNode::ErrorToken { index }));
4638            }
4639            outcome
4640        })
4641        .collect()
4642    }
4643
4644    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
4645    /// pretend the expected transition token was present and continue without
4646    /// consuming the current token.
4647    fn fast_single_token_insertion_recovery(
4648        &mut self,
4649        recovery: FastRecoveryRequest<'_, '_>,
4650    ) -> Vec<FastRecognizeOutcome> {
4651        let FastRecoveryRequest {
4652            atn,
4653            transition,
4654            expected_symbols,
4655            target,
4656            request,
4657            visiting,
4658            memo,
4659            expected,
4660        } = recovery;
4661        let FastRecognizeRequest {
4662            stop_state,
4663            index,
4664            rule_start_index,
4665            decision_start_index,
4666            precedence,
4667            depth,
4668            ..
4669        } = request;
4670        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
4671        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
4672            transition,
4673            index,
4674            atn.max_token_type(),
4675            &expected_symbols,
4676            &follow_symbols,
4677        ) else {
4678            return Vec::new();
4679        };
4680        let empty_recovery = self.empty_recovery_symbols();
4681        self.recognize_state_fast(
4682            atn,
4683            FastRecognizeRequest {
4684                state_number: target,
4685                stop_state,
4686                index,
4687                rule_start_index,
4688                decision_start_index,
4689                precedence,
4690                depth: depth + 1,
4691                recovery_symbols: empty_recovery,
4692                recovery_state: None,
4693            },
4694            visiting,
4695            memo,
4696            expected,
4697        )
4698        .into_iter()
4699        .map(|mut outcome| {
4700            outcome.diagnostics.insert(0, diagnostic.clone());
4701            outcome
4702                .nodes
4703                .prepend(Rc::new(FastRecognizedNode::MissingToken {
4704                    token_type,
4705                    at_index: index,
4706                    text: text.clone(),
4707                }));
4708            outcome
4709        })
4710        .collect()
4711    }
4712
4713    /// Retries the current fast-recognition state after deleting one
4714    /// unexpected token that precedes a valid loop or block continuation.
4715    fn fast_current_token_deletion_recovery(
4716        &mut self,
4717        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
4718    ) -> Vec<FastRecognizeOutcome> {
4719        let FastCurrentTokenDeletionRequest {
4720            atn,
4721            expected_symbols,
4722            mut request,
4723            visiting,
4724            memo,
4725            expected,
4726        } = recovery;
4727        if request.index == request.rule_start_index {
4728            return Vec::new();
4729        }
4730        let Some((diagnostic, next_index, skipped)) =
4731            self.current_token_deletion(request.index, &expected_symbols)
4732        else {
4733            return Vec::new();
4734        };
4735        request.state_number = request.recovery_state.unwrap_or(request.state_number);
4736        request.index = next_index;
4737        request.depth += 1;
4738        request.recovery_state = None;
4739        self.recognize_state_fast(atn, request, visiting, memo, expected)
4740            .into_iter()
4741            .map(|mut outcome| {
4742                outcome.diagnostics.insert(0, diagnostic.clone());
4743                for index in skipped.iter().rev() {
4744                    outcome
4745                        .nodes
4746                        .prepend(Rc::new(FastRecognizedNode::ErrorToken { index: *index }));
4747                }
4748                outcome
4749            })
4750            .collect()
4751    }
4752
4753    /// Converts a failed child rule into a recovered fast-recognizer outcome so
4754    /// the parent can keep its child rule context and continue at a sync token.
4755    fn fast_child_rule_failure_recovery(
4756        &mut self,
4757        rule_index: usize,
4758        start_index: usize,
4759        sync_symbols: &BTreeSet<i32>,
4760        expected: &ExpectedTokens,
4761    ) -> Option<FastRecognizeOutcome> {
4762        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
4763        let token = self.token_at(error_index);
4764        let mut next_index = error_index;
4765        loop {
4766            let symbol = self.token_type_at(next_index);
4767            if sync_symbols.contains(&symbol) {
4768                if next_index == error_index {
4769                    return None;
4770                }
4771                break;
4772            }
4773            if symbol == TOKEN_EOF {
4774                break;
4775            }
4776            let after = self.consume_index(next_index, symbol);
4777            if after == next_index {
4778                break;
4779            }
4780            next_index = after;
4781        }
4782        let mut diagnostics = FastDiagnostics::new();
4783        diagnostics.insert(0, diagnostic_for_token(token.as_ref(), message));
4784        let mut nodes = NodeList::new();
4785        if self.fast_token_nodes_enabled {
4786            nodes.prepend(Rc::new(FastRecognizedNode::ErrorToken {
4787                index: error_index,
4788            }));
4789        }
4790        Some(FastRecognizeOutcome {
4791            index: next_index,
4792            consumed_eof: false,
4793            diagnostics,
4794            nodes,
4795        })
4796    }
4797
4798    /// Adapts the optional child-rule recovery result to the fast-recognizer
4799    /// outcome list used by rule-call transitions.
4800    fn fast_child_rule_failure_recovery_outcomes(
4801        &mut self,
4802        request: FastChildRuleFailureRecoveryRequest<'_>,
4803    ) -> Vec<FastRecognizeOutcome> {
4804        let FastChildRuleFailureRecoveryRequest {
4805            atn,
4806            rule_index,
4807            start_index,
4808            follow_state,
4809            stop_state,
4810            expected,
4811        } = request;
4812        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
4813        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
4814            .into_iter()
4815            .collect()
4816    }
4817
4818    /// Attempts to reach `stop_state` from `state_number` without committing
4819    /// token consumption to the parser's public stream position.
4820    #[allow(clippy::too_many_lines)]
4821    fn recognize_state_fast(
4822        &mut self,
4823        atn: &Atn,
4824        request: FastRecognizeRequest,
4825        visiting: &mut FxHashSet<(usize, usize)>,
4826        memo: &mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4827        expected: &mut ExpectedTokens,
4828    ) -> Vec<FastRecognizeOutcome> {
4829        #[cfg(feature = "perf-counters")]
4830        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
4831        let FastRecognizeRequest {
4832            mut state_number,
4833            stop_state,
4834            mut index,
4835            rule_start_index,
4836            decision_start_index,
4837            precedence,
4838            mut depth,
4839            recovery_symbols,
4840            recovery_state,
4841        } = request;
4842        // Walk straight-line epsilon chains in a loop instead of recursing
4843        // into `recognize_state_fast` for each intermediate state. ATN
4844        // serialization places long sequences of `BasicBlock` epsilon
4845        // transitions between decisions: turning that chain into a loop
4846        // collapses many recursive calls (and their memo lookups, vec
4847        // allocations, and visit-set churn) into a single function frame.
4848        // The loop exits as soon as we hit the original state's logic
4849        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
4850        // precedence) so existing fanout, recovery, and memoization still
4851        // apply unchanged.
4852        //
4853        // The inline case also handles single-atom-match states on the
4854        // happy-pass path: when the lone consuming transition matches the
4855        // current lookahead, advance the index and continue without paying
4856        // for a full `recognize_state_fast` recursion. We track tokens we
4857        // consumed inline in `inline_consumed_tokens` so they can be
4858        // prepended onto the eventual outcome list once we hit a state
4859        // whose handling falls outside this fast loop.
4860        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
4861        let mut inline_consumed_eof = false;
4862        loop {
4863            if depth > RECOGNITION_DEPTH_LIMIT {
4864                return Vec::new();
4865            }
4866            if state_number == stop_state {
4867                let mut nodes = NodeList::new();
4868                if self.fast_token_nodes_enabled {
4869                    for token_index in inline_consumed_tokens.iter().rev() {
4870                        nodes.prepend(Rc::new(FastRecognizedNode::Token {
4871                            index: *token_index,
4872                        }));
4873                    }
4874                }
4875                return vec![FastRecognizeOutcome {
4876                    index,
4877                    consumed_eof: inline_consumed_eof,
4878                    diagnostics: FastDiagnostics::new(),
4879                    nodes,
4880                }];
4881            }
4882            let Some(state) = atn.state(state_number) else {
4883                return Vec::new();
4884            };
4885            if state.transitions.len() == 1
4886                && !starts_prediction_decision(state)
4887                && !state.precedence_rule_decision
4888            {
4889                match &state.transitions[0] {
4890                    Transition::Epsilon { target }
4891                    | Transition::Predicate { target, .. }
4892                    | Transition::Action { target, .. }
4893                        if left_recursive_boundary(atn, state, *target).is_none() =>
4894                    {
4895                        #[cfg(feature = "perf-counters")]
4896                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
4897                        state_number = *target;
4898                        depth += 1;
4899                        continue;
4900                    }
4901                    Transition::Precedence {
4902                        target,
4903                        precedence: transition_precedence,
4904                    } if *transition_precedence >= precedence
4905                        && left_recursive_boundary(atn, state, *target).is_none() =>
4906                    {
4907                        #[cfg(feature = "perf-counters")]
4908                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
4909                        state_number = *target;
4910                        depth += 1;
4911                        continue;
4912                    }
4913                    // Single-atom / range / set / wildcard / not-set states
4914                    // are common (~17K of ~125K calls on C#) and almost
4915                    // always succeed in pass 1: no fanout, no recovery, no
4916                    // diagnostics. Inline the token match and continue
4917                    // walking instead of recursing — the recursive path
4918                    // would just allocate a Vec, build one outcome, prepend
4919                    // a Token node, and return. Skip pass 2 (recovery
4920                    // enabled): there the failure branch matters and the
4921                    // existing recursive code records expected symbols.
4922                    Transition::Atom { target, .. }
4923                    | Transition::Range { target, .. }
4924                    | Transition::Set { target, .. }
4925                    | Transition::NotSet { target, .. }
4926                    | Transition::Wildcard { target, .. }
4927                        if !self.fast_recovery_enabled =>
4928                    {
4929                        let symbol = self.token_type_at(index);
4930                        let transition = &state.transitions[0];
4931                        if transition.matches(symbol, 1, atn.max_token_type()) {
4932                            #[cfg(feature = "perf-counters")]
4933                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
4934                            if self.fast_token_nodes_enabled {
4935                                inline_consumed_tokens.push(index);
4936                            }
4937                            inline_consumed_eof |= symbol == TOKEN_EOF;
4938                            index = self.consume_index(index, symbol);
4939                            state_number = *target;
4940                            depth += 1;
4941                            continue;
4942                        }
4943                        // Fall through to break and let the regular
4944                        // body handle the no-match case (returns empty).
4945                    }
4946                    _ => {}
4947                }
4948            }
4949            break;
4950        }
4951        // If we collected token nodes inline but bail to the recursive
4952        // body (decision state, rule call, etc.), the outcomes returned
4953        // below will need those token nodes prepended.
4954        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
4955        let Some(state) = atn.state(state_number) else {
4956            return Vec::new();
4957        };
4958        let transition_count = state.transitions.len();
4959        let memo_lookup_enabled = self.fast_recovery_enabled || transition_count > 1;
4960        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
4961        // fields and the rule/decision boundary indices are pure plumbing —
4962        // they only affect the recovery branch and the no-viable diagnostic
4963        // recording, neither of which fires when recovery is off. Zeroing
4964        // them in the memo key collapses calls that visit the same
4965        // `(state, index)` from different rule-call sites onto one cache
4966        // entry, which is the dominant cost on large grammars (e.g. C#) where
4967        // many rules eventually delegate into the same `expression` /
4968        // `primary_expression` / `type` branches.
4969        let key = if self.fast_recovery_enabled {
4970            FastRecognizeKey {
4971                state_number,
4972                stop_state,
4973                index,
4974                rule_start_index,
4975                decision_start_index,
4976                precedence,
4977                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
4978                recovery_state,
4979            }
4980        } else {
4981            FastRecognizeKey {
4982                state_number,
4983                stop_state,
4984                index,
4985                rule_start_index: 0,
4986                decision_start_index: None,
4987                precedence,
4988                recovery_symbols_id: 0,
4989                recovery_state: None,
4990            }
4991        };
4992        if memo_lookup_enabled {
4993            if let Some(outcomes) = memo.get(&key) {
4994                #[cfg(feature = "perf-counters")]
4995                {
4996                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
4997                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
4998                }
4999                // Materialize a fresh `Vec` from the cached slice; the caller
5000                // mutates per-outcome state (eof flags, prepended nodes) so we
5001                // can't hand them the shared backing.
5002                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
5003                    let inline_eof = inline_consumed_eof;
5004                    let inline_tokens = &inline_consumed_tokens;
5005                    return outcomes
5006                        .iter()
5007                        .cloned()
5008                        .map(|mut outcome| {
5009                            if inline_eof {
5010                                outcome.consumed_eof = true;
5011                            }
5012                            if self.fast_token_nodes_enabled {
5013                                for token_index in inline_tokens.iter().rev() {
5014                                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
5015                                        index: *token_index,
5016                                    }));
5017                                }
5018                            }
5019                            outcome
5020                        })
5021                        .collect();
5022                }
5023                return outcomes.to_vec();
5024            }
5025            #[cfg(feature = "perf-counters")]
5026            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
5027        }
5028
5029        // Cycle detection: only insert into the visiting set for states
5030        // that *could* re-enter without consuming — multi-alternative
5031        // states. Single-transition states are walked in the loop above and
5032        // never form cycles (the loop only advances toward the rule stop).
5033        // Multi-alt states might contain epsilon-only edges that loop back
5034        // to the same `(state, index)` (e.g. left-recursive precedence
5035        // loops); we still need the guard there.
5036        let needs_cycle_guard =
5037            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number);
5038        #[cfg(feature = "perf-counters")]
5039        if needs_cycle_guard {
5040            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
5041        } else {
5042            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
5043            match &state.transitions[0] {
5044                Transition::Rule { .. } => {
5045                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
5046                }
5047                Transition::Atom { .. }
5048                | Transition::Range { .. }
5049                | Transition::Set { .. }
5050                | Transition::NotSet { .. }
5051                | Transition::Wildcard { .. } => {
5052                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
5053                }
5054                _ => {
5055                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
5056                }
5057            }
5058        }
5059        let visit_id = (state_number, index);
5060        if needs_cycle_guard && !visiting.insert(visit_id) {
5061            #[cfg(feature = "perf-counters")]
5062            perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
5063            return Vec::new();
5064        }
5065        let next_decision_start_index = if starts_prediction_decision(state) {
5066            Some(index)
5067        } else {
5068            decision_start_index
5069        };
5070        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
5071            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
5072        } else {
5073            (Rc::clone(&recovery_symbols), recovery_state)
5074        };
5075
5076        // Lookahead-based pruning. At a multi-alternative state we cache the
5077        // look-1 set of every outgoing transition; on visit we keep only the
5078        // transitions whose look-1 can accept the current lookahead (or that
5079        // can be reached without consuming and so could legitimately match a
5080        // shorter input). This is the main speedup vs. blind speculative
5081        // recursion: it lets each visit fan out only to the alternatives that
5082        // could possibly contribute a clean parse, mirroring the SLL phase of
5083        // ANTLR's adaptive prediction.
5084        //
5085        // Pruning is skipped at:
5086        //   * rule-start states (a child rule call may need every internal
5087        //     transition to surface single-token recovery diagnostics that
5088        //     ANTLR's reference parser emits at the rule's first consuming
5089        //     transition; the FIRST-set retry path turns the prefilter off
5090        //     entirely so let's keep this lightweight too),
5091        //   * left-recursive precedence loops (the precedence transition's
5092        //     gating is dynamic),
5093        //   * states with too few alternatives to benefit.
5094        let transition_count = state.transitions.len();
5095        let lookahead_filter = if transition_count > 1
5096            && self.fast_first_set_prefilter
5097            && !state.precedence_rule_decision
5098            && (!self.fast_recovery_enabled || state.kind != AtnStateKind::RuleStart)
5099        {
5100            state
5101                .rule_index
5102                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index).copied())
5103                .map(|rule_stop| {
5104                    let symbol = self.token_type_at(index);
5105                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
5106                    (symbol, entry)
5107                })
5108        } else {
5109            None
5110        };
5111        // LL(1) fast path: when the FIRST sets for the decision are disjoint
5112        // and none is nullable, the lookahead deterministically selects one
5113        // alternative. The recursive recognizer can then commit to that single
5114        // alt without iterating every transition through `should_skip_via_lookahead`
5115        // — saving (transition_count - 1) filter probes per visit.
5116        //
5117        // Result is cached per `(state, lookahead_token)` on the parser
5118        // instance, so subsequent visits skip the FIRST-set scan entirely.
5119        let ll1_only_alt: Option<usize> = if transition_count > 1
5120            && let Some((symbol, entry)) = lookahead_filter.as_ref()
5121        {
5122            let key = (state.state_number, *symbol);
5123            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
5124                cached
5125            } else {
5126                let result = ll1_unique_alt(entry, *symbol);
5127                self.ll1_decision_cache.insert(key, result);
5128                result
5129            }
5130        } else {
5131            None
5132        };
5133        let lookahead_filter = lookahead_filter.as_ref();
5134        // Pre-size only when we expect at least one outcome to land — most
5135        // single-transition fall-throughs (the loop above didn't catch
5136        // because they're atom/rule/predicate) push at most one entry, so
5137        // reserving one slot avoids a reallocation while keeping the
5138        // unused-slot waste at one element.
5139        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
5140        for (transition_index, transition) in state.transitions.iter().enumerate() {
5141            if let Some(alt) = ll1_only_alt {
5142                // LL(1) determinism: skip every alt except the chosen one.
5143                if alt != transition_index {
5144                    continue;
5145                }
5146            } else if should_skip_via_lookahead(
5147                transition,
5148                transition_index,
5149                lookahead_filter,
5150                index,
5151                self.fast_recovery_enabled,
5152                expected,
5153            ) {
5154                continue;
5155            }
5156            match transition {
5157                Transition::Epsilon { target }
5158                | Transition::Predicate { target, .. }
5159                | Transition::Action { target, .. } => {
5160                    #[cfg(feature = "perf-counters")]
5161                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
5162                    let boundary = left_recursive_boundary(atn, state, *target);
5163                    outcomes.extend(
5164                        self.recognize_state_fast(
5165                            atn,
5166                            FastRecognizeRequest {
5167                                state_number: *target,
5168                                stop_state,
5169                                index,
5170                                rule_start_index,
5171                                decision_start_index: next_decision_start_index,
5172                                precedence,
5173                                depth: depth + 1,
5174                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
5175                                recovery_state: epsilon_recovery_state,
5176                            },
5177                            visiting,
5178                            memo,
5179                            expected,
5180                        )
5181                        .into_iter()
5182                        .map(|mut outcome| {
5183                            if let Some(rule_index) = boundary {
5184                                outcome.nodes.prepend(Rc::new(
5185                                    FastRecognizedNode::LeftRecursiveBoundary { rule_index },
5186                                ));
5187                            }
5188                            outcome
5189                        }),
5190                    );
5191                }
5192                Transition::Precedence {
5193                    target,
5194                    precedence: transition_precedence,
5195                } => {
5196                    if *transition_precedence >= precedence {
5197                        let boundary = left_recursive_boundary(atn, state, *target);
5198                        outcomes.extend(
5199                            self.recognize_state_fast(
5200                                atn,
5201                                FastRecognizeRequest {
5202                                    state_number: *target,
5203                                    stop_state,
5204                                    index,
5205                                    rule_start_index,
5206                                    decision_start_index: next_decision_start_index,
5207                                    precedence,
5208                                    depth: depth + 1,
5209                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
5210                                    recovery_state: epsilon_recovery_state,
5211                                },
5212                                visiting,
5213                                memo,
5214                                expected,
5215                            )
5216                            .into_iter()
5217                            .map(|mut outcome| {
5218                                if let Some(rule_index) = boundary {
5219                                    outcome.nodes.prepend(Rc::new(
5220                                        FastRecognizedNode::LeftRecursiveBoundary { rule_index },
5221                                    ));
5222                                }
5223                                outcome
5224                            }),
5225                        );
5226                    }
5227                }
5228                Transition::Rule {
5229                    target,
5230                    rule_index,
5231                    follow_state,
5232                    precedence: rule_precedence,
5233                    ..
5234                } => {
5235                    #[cfg(feature = "perf-counters")]
5236                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
5237                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
5238                    else {
5239                        continue;
5240                    };
5241                    // Lookahead-based pruning. The recognizer would otherwise
5242                    // explore every speculative rule call, producing exponential
5243                    // work on grammars with many epsilon-reachable rules. When
5244                    // the rule is non-nullable and its FIRST set excludes the
5245                    // current lookahead, recursion can't find a clean path
5246                    // *through this rule*. Skipping is only safe if some sibling
5247                    // transition can still consume the lookahead — otherwise the
5248                    // rule call is the sole continuation and must run so the
5249                    // single-token insertion / deletion recovery inside the
5250                    // called rule can fire (mirroring ANTLR's reference behavior
5251                    // of conjuring a missing token at child-rule entry).
5252                    let symbol = self.token_type_at(index);
5253                    if self.fast_first_set_prefilter {
5254                        // Probe the shared cross-parse cache first; build
5255                        // the entry on miss and intern it there. The
5256                        // computation is purely a function of the ATN, so
5257                        // the cached entry is reused across parses (and
5258                        // freshly-instantiated parser values that share
5259                        // the same `&'static Atn`).
5260                        //
5261                        // `rule_first_set` returns the computed entry
5262                        // directly — it intentionally skips inserting into
5263                        // the cache when the FIRST-set walk hit a cycle, so
5264                        // we cannot assume the entry is in the cache after
5265                        // computing it.
5266                        let first = self.cached_rule_first_set(atn, *target, child_stop);
5267                        if should_skip_rule_via_first_set(
5268                            &first,
5269                            symbol,
5270                            self.fast_recovery_enabled,
5271                            index,
5272                            expected,
5273                        ) {
5274                            continue;
5275                        }
5276                    }
5277                    let expected_before_child =
5278                        self.fast_recovery_enabled.then(|| expected.clone());
5279                    let mut children = self.recognize_state_fast(
5280                        atn,
5281                        FastRecognizeRequest {
5282                            state_number: *target,
5283                            stop_state: child_stop,
5284                            index,
5285                            rule_start_index: index,
5286                            decision_start_index: None,
5287                            precedence: *rule_precedence,
5288                            depth: depth + 1,
5289                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
5290                            recovery_state: epsilon_recovery_state,
5291                        },
5292                        visiting,
5293                        memo,
5294                        expected,
5295                    );
5296                    if children.is_empty() && self.fast_recovery_enabled {
5297                        children = self.fast_child_rule_failure_recovery_outcomes(
5298                            FastChildRuleFailureRecoveryRequest {
5299                                atn,
5300                                rule_index: *rule_index,
5301                                start_index: index,
5302                                follow_state: *follow_state,
5303                                stop_state,
5304                                expected,
5305                            },
5306                        );
5307                    }
5308                    if let Some(expected_before_child) = expected_before_child {
5309                        if children
5310                            .iter()
5311                            .any(|child| child.diagnostics.is_empty() && child.index > index)
5312                        {
5313                            *expected = expected_before_child;
5314                        }
5315                    }
5316                    for child in children {
5317                        let child_index = child.index;
5318                        let child_consumed_eof = child.consumed_eof;
5319                        let child_diagnostics = child.diagnostics;
5320                        let empty_recovery = self.empty_recovery_symbols();
5321                        let follow_outcomes = self.recognize_state_fast(
5322                            atn,
5323                            FastRecognizeRequest {
5324                                state_number: *follow_state,
5325                                stop_state,
5326                                index: child_index,
5327                                rule_start_index,
5328                                decision_start_index: next_decision_start_index,
5329                                precedence,
5330                                depth: depth + 1,
5331                                recovery_symbols: empty_recovery,
5332                                recovery_state: None,
5333                            },
5334                            visiting,
5335                            memo,
5336                            expected,
5337                        );
5338                        if follow_outcomes.is_empty() {
5339                            continue;
5340                        }
5341                        let child_node = Rc::new(FastRecognizedNode::Rule {
5342                            rule_index: *rule_index,
5343                            invoking_state: invoking_state_number(state_number),
5344                            start_index: index,
5345                            stop_index: self.rule_stop_token_index(child_index, child_consumed_eof),
5346                            children: child.nodes,
5347                        });
5348                        let child_diags_empty = child_diagnostics.is_empty();
5349                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
5350                            outcome.consumed_eof |= child_consumed_eof;
5351                            // Skip the prepend dance when there's nothing to
5352                            // merge from the child — common case in pass 1.
5353                            if !child_diags_empty {
5354                                let mut diagnostics = child_diagnostics.clone();
5355                                diagnostics.append(&mut outcome.diagnostics);
5356                                outcome.diagnostics = diagnostics;
5357                            }
5358                            outcome.nodes.prepend(Rc::clone(&child_node));
5359                            outcome
5360                        }));
5361                    }
5362                }
5363                Transition::Atom { target, .. }
5364                | Transition::Range { target, .. }
5365                | Transition::Set { target, .. }
5366                | Transition::NotSet { target, .. }
5367                | Transition::Wildcard { target, .. } => {
5368                    #[cfg(feature = "perf-counters")]
5369                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
5370                    let symbol = self.token_type_at(index);
5371                    if transition.matches(symbol, 1, atn.max_token_type()) {
5372                        let next_index = self.consume_index(index, symbol);
5373                        let empty_recovery = self.empty_recovery_symbols();
5374                        outcomes.extend(
5375                            self.recognize_state_fast(
5376                                atn,
5377                                FastRecognizeRequest {
5378                                    state_number: *target,
5379                                    stop_state,
5380                                    index: next_index,
5381                                    rule_start_index,
5382                                    decision_start_index: next_decision_start_index,
5383                                    precedence,
5384                                    depth: depth + 1,
5385                                    recovery_symbols: empty_recovery,
5386                                    recovery_state: None,
5387                                },
5388                                visiting,
5389                                memo,
5390                                expected,
5391                            )
5392                            .into_iter()
5393                            .map(|mut outcome| {
5394                                outcome.consumed_eof |= symbol == TOKEN_EOF;
5395                                if self.fast_token_nodes_enabled {
5396                                    outcome
5397                                        .nodes
5398                                        .prepend(Rc::new(FastRecognizedNode::Token { index }));
5399                                }
5400                                outcome
5401                            }),
5402                        );
5403                    } else {
5404                        if !self.fast_recovery_enabled {
5405                            // In pass 1 there is no recovery to attempt; the
5406                            // recovery branch below would never run, and the
5407                            // `expected_symbols` computation is just there
5408                            // to gate that branch. Skipping it eliminates
5409                            // ~1× `state_expected_symbols` lookup per failed
5410                            // atom transition (≈82K on mono-statement.cs)
5411                            // for zero observable behavior change.
5412                            continue;
5413                        }
5414                        let expected_symbols = fast_recovery_expected_symbols(
5415                            self,
5416                            atn,
5417                            state.state_number,
5418                            &recovery_symbols,
5419                        );
5420                        if expected_symbols.contains(&symbol) {
5421                            continue;
5422                        }
5423                        {
5424                            expected.record_transition(index, transition, atn.max_token_type());
5425                            record_no_viable_if_ambiguous(
5426                                expected,
5427                                next_decision_start_index,
5428                                index,
5429                            );
5430                            outcomes.extend(self.fast_single_token_deletion_recovery(
5431                                FastRecoveryRequest {
5432                                    atn,
5433                                    transition,
5434                                    expected_symbols: Rc::clone(&expected_symbols),
5435                                    target: *target,
5436                                    request: FastRecognizeRequest {
5437                                        state_number,
5438                                        stop_state,
5439                                        index,
5440                                        rule_start_index,
5441                                        decision_start_index,
5442                                        precedence,
5443                                        depth,
5444                                        recovery_symbols: Rc::clone(&recovery_symbols),
5445                                        recovery_state,
5446                                    },
5447                                    visiting,
5448                                    memo,
5449                                    expected,
5450                                },
5451                            ));
5452                            if !state_is_left_recursive_rule(atn, state) {
5453                                outcomes.extend(self.fast_single_token_insertion_recovery(
5454                                    FastRecoveryRequest {
5455                                        atn,
5456                                        transition,
5457                                        expected_symbols: Rc::clone(&expected_symbols),
5458                                        target: *target,
5459                                        request: FastRecognizeRequest {
5460                                            state_number,
5461                                            stop_state,
5462                                            index,
5463                                            rule_start_index,
5464                                            decision_start_index,
5465                                            precedence,
5466                                            depth,
5467                                            recovery_symbols: Rc::clone(&recovery_symbols),
5468                                            recovery_state,
5469                                        },
5470                                        visiting,
5471                                        memo,
5472                                        expected,
5473                                    },
5474                                ));
5475                            }
5476                            outcomes.extend(self.fast_current_token_deletion_recovery(
5477                                FastCurrentTokenDeletionRequest {
5478                                    atn,
5479                                    expected_symbols,
5480                                    request: FastRecognizeRequest {
5481                                        state_number,
5482                                        stop_state,
5483                                        index,
5484                                        rule_start_index,
5485                                        decision_start_index,
5486                                        precedence,
5487                                        depth,
5488                                        recovery_symbols: Rc::clone(&recovery_symbols),
5489                                        recovery_state,
5490                                    },
5491                                    visiting,
5492                                    memo,
5493                                    expected,
5494                                },
5495                            ));
5496                        }
5497                    }
5498                }
5499            }
5500        }
5501
5502        if needs_cycle_guard {
5503            visiting.remove(&visit_id);
5504        }
5505        if matches!(
5506            self.prediction_mode,
5507            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
5508        ) && self.fast_recovery_enabled
5509        {
5510            // Without recovery enabled every outcome already has empty
5511            // diagnostics, so the discard pass is a no-op — skipping it
5512            // saves an iter+retain on each of the ~1M visits.
5513            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
5514        }
5515        if self.fast_recovery_enabled {
5516            dedupe_fast_outcomes(&mut outcomes);
5517        } else {
5518            dedupe_clean_fast_outcomes(&mut outcomes);
5519        }
5520        // Skip memoization for single-transition states whose outcome is
5521        // unambiguous: they only get re-entered if the caller revisits the
5522        // exact same call site, which is rare since the loop above already
5523        // collapsed straight-line epsilon walks. Multi-alternative states
5524        // are where backtracking actually revisits the same coordinate, so
5525        // we still memoize there. With recovery on we keep the existing
5526        // memoization unconditionally because the recovery branch may
5527        // record diagnostics that the cache must surface to repeated
5528        // failed visits.
5529        let should_memoize = self.fast_recovery_enabled
5530            || (transition_count > 1
5531                && (outcomes.is_empty()
5532                    || outcomes.len() > 1
5533                    || (outcomes.len() == 1 && self.should_memoize_single_outcome(&key))));
5534        // Apply inline pending state to each outcome before returning.
5535        // Tokens consumed inline by the loop-collapse don't appear in the
5536        // recursive recognizer's output, so we need to prepend them here.
5537        let apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
5538            if inline_consumed_eof {
5539                outcome.consumed_eof = true;
5540            }
5541            if !inline_consumed_tokens.is_empty() {
5542                for token_index in inline_consumed_tokens.iter().rev() {
5543                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
5544                        index: *token_index,
5545                    }));
5546                }
5547            }
5548            outcome
5549        };
5550        if should_memoize {
5551            #[cfg(feature = "perf-counters")]
5552            {
5553                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
5554                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
5555                match outcomes.len() {
5556                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
5557                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
5558                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
5559                }
5560            }
5561            // The memo is keyed by the loop-exit `(state_number, index)` so
5562            // the inline-consumed tokens belong to *this* call's output, not
5563            // the cached result. Memoize the bare outcomes (without the
5564            // inline-pending data), then prepend the inline data on return.
5565            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
5566            memo.insert(key, Rc::clone(&stored));
5567            if inline_pending {
5568                return stored.iter().cloned().map(apply_inline_pending).collect();
5569            }
5570            return stored.to_vec();
5571        }
5572        #[cfg(feature = "perf-counters")]
5573        match outcomes.len() {
5574            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
5575            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
5576            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
5577        }
5578        if inline_pending {
5579            return outcomes.into_iter().map(apply_inline_pending).collect();
5580        }
5581        outcomes
5582    }
5583
5584    /// Explores single-token deletion recovery while preserving the matched
5585    /// token and skipped error token in the selected parse tree path.
5586    fn single_token_deletion_recovery(
5587        &mut self,
5588        recovery: RecoveryRequest<'_, '_>,
5589    ) -> Vec<RecognizeOutcome> {
5590        let RecoveryRequest {
5591            atn,
5592            transition,
5593            expected_symbols,
5594            target,
5595            request,
5596            visiting,
5597            memo,
5598            expected,
5599        } = recovery;
5600        let RecognizeRequest {
5601            stop_state,
5602            index,
5603            rule_start_index,
5604            decision_start_index,
5605            init_action_rules,
5606            predicates,
5607            rule_args,
5608            member_actions,
5609            return_actions,
5610            local_int_arg,
5611            member_values,
5612            return_values,
5613            rule_alt_number,
5614            track_alt_numbers,
5615            consumed_eof,
5616            precedence,
5617            depth,
5618            ..
5619        } = request;
5620        let Some((diagnostic, next_index, next_symbol)) =
5621            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
5622        else {
5623            return Vec::new();
5624        };
5625        let after_next = self.consume_index(next_index, next_symbol);
5626        self.recognize_state(
5627            atn,
5628            RecognizeRequest {
5629                state_number: target,
5630                stop_state,
5631                index: after_next,
5632                rule_start_index,
5633                decision_start_index,
5634                init_action_rules,
5635                predicates,
5636                rule_args,
5637                member_actions,
5638                return_actions,
5639                local_int_arg,
5640                member_values,
5641                return_values,
5642                rule_alt_number,
5643                track_alt_numbers,
5644                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
5645                precedence,
5646                depth: depth + 1,
5647                recovery_symbols: BTreeSet::new(),
5648                recovery_state: None,
5649            },
5650            visiting,
5651            memo,
5652            expected,
5653        )
5654        .into_iter()
5655        .map(|mut outcome| {
5656            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
5657            outcome.diagnostics.insert(0, diagnostic.clone());
5658            outcome
5659                .nodes
5660                .insert(0, RecognizedNode::Token { index: next_index });
5661            outcome
5662                .nodes
5663                .insert(0, RecognizedNode::ErrorToken { index });
5664            outcome
5665        })
5666        .collect()
5667    }
5668
5669    /// Retries the current recognition state after deleting one unexpected
5670    /// token, preserving the deleted token as an error node in the parse tree.
5671    fn current_token_deletion_recovery(
5672        &mut self,
5673        recovery: CurrentTokenDeletionRequest<'_, '_>,
5674    ) -> Vec<RecognizeOutcome> {
5675        let CurrentTokenDeletionRequest {
5676            atn,
5677            expected_symbols,
5678            mut request,
5679            visiting,
5680            memo,
5681            expected,
5682        } = recovery;
5683        let error_index = request.index;
5684        if error_index == request.rule_start_index {
5685            return Vec::new();
5686        }
5687        let Some((diagnostic, next_index, skipped)) =
5688            self.current_token_deletion(error_index, &expected_symbols)
5689        else {
5690            return Vec::new();
5691        };
5692        request.state_number = request.recovery_state.unwrap_or(request.state_number);
5693        request.index = next_index;
5694        request.depth += 1;
5695        request.recovery_state = None;
5696        self.recognize_state(atn, request, visiting, memo, expected)
5697            .into_iter()
5698            .map(|mut outcome| {
5699                outcome.diagnostics.insert(0, diagnostic.clone());
5700                for index in skipped.iter().rev() {
5701                    outcome
5702                        .nodes
5703                        .insert(0, RecognizedNode::ErrorToken { index: *index });
5704                }
5705                outcome
5706            })
5707            .collect()
5708    }
5709
5710    /// Falls back after deletion/insertion repairs cannot continue from a
5711    /// failed consuming transition.
5712    fn consuming_failure_fallback(
5713        &mut self,
5714        fallback: ConsumingFailureFallback<'_>,
5715        visiting: &mut BTreeSet<RecognizeKey>,
5716        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
5717        expected: &mut ExpectedTokens,
5718    ) -> Vec<RecognizeOutcome> {
5719        if fallback.expected_symbols.is_empty() {
5720            return Vec::new();
5721        }
5722        if fallback.symbol == TOKEN_EOF {
5723            return self.eof_consuming_failure_fallback(fallback, expected);
5724        }
5725        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
5726    }
5727
5728    /// Keeps unexpected non-EOF input visible as an error node when no repair
5729    /// path can otherwise reach the transition target.
5730    fn non_eof_consuming_failure_fallback(
5731        &mut self,
5732        fallback: ConsumingFailureFallback<'_>,
5733        visiting: &mut BTreeSet<RecognizeKey>,
5734        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
5735        expected: &mut ExpectedTokens,
5736    ) -> Vec<RecognizeOutcome> {
5737        let ConsumingFailureFallback {
5738            atn,
5739            target,
5740            request,
5741            symbol,
5742            expected_symbols,
5743            decision_start_index,
5744            decision,
5745        } = fallback;
5746        let error_index = request.index;
5747        let diagnostic =
5748            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
5749        let next_index = self.consume_index(error_index, symbol);
5750        self.recognize_state(
5751            atn,
5752            RecognizeRequest {
5753                state_number: target,
5754                stop_state: request.stop_state,
5755                index: next_index,
5756                rule_start_index: request.rule_start_index,
5757                decision_start_index,
5758                init_action_rules: request.init_action_rules,
5759                predicates: request.predicates,
5760                rule_args: request.rule_args,
5761                member_actions: request.member_actions,
5762                return_actions: request.return_actions,
5763                local_int_arg: request.local_int_arg,
5764                member_values: request.member_values,
5765                return_values: request.return_values,
5766                rule_alt_number: request.rule_alt_number,
5767                track_alt_numbers: request.track_alt_numbers,
5768                consumed_eof: request.consumed_eof,
5769                precedence: request.precedence,
5770                depth: request.depth + 1,
5771                recovery_symbols: BTreeSet::new(),
5772                recovery_state: None,
5773            },
5774            visiting,
5775            memo,
5776            expected,
5777        )
5778        .into_iter()
5779        .map(|mut outcome| {
5780            prepend_decision(&mut outcome, decision);
5781            outcome.diagnostics.insert(0, diagnostic.clone());
5782            outcome
5783                .nodes
5784                .insert(0, RecognizedNode::ErrorToken { index: error_index });
5785            outcome
5786        })
5787        .collect()
5788    }
5789
5790    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
5791    /// behavior of unwinding instead of inserting caller tokens at EOF.
5792    fn eof_consuming_failure_fallback(
5793        &mut self,
5794        fallback: ConsumingFailureFallback<'_>,
5795        expected: &ExpectedTokens,
5796    ) -> Vec<RecognizeOutcome> {
5797        let request = fallback.request;
5798        if request.index == request.rule_start_index {
5799            return Vec::new();
5800        }
5801        let diagnostic =
5802            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
5803        vec![RecognizeOutcome {
5804            index: request.index,
5805            consumed_eof: request.consumed_eof,
5806            alt_number: request.rule_alt_number,
5807            member_values: request.member_values,
5808            return_values: request.return_values,
5809            diagnostics: vec![diagnostic],
5810            decisions: Vec::new(),
5811            actions: Vec::new(),
5812            nodes: Vec::new(),
5813        }]
5814    }
5815
5816    /// Explores single-token insertion recovery while adding a conjured
5817    /// missing-token error node to the selected parse tree path.
5818    fn single_token_insertion_recovery(
5819        &mut self,
5820        recovery: RecoveryRequest<'_, '_>,
5821    ) -> Vec<RecognizeOutcome> {
5822        let RecoveryRequest {
5823            atn,
5824            transition,
5825            expected_symbols,
5826            target,
5827            request,
5828            visiting,
5829            memo,
5830            expected,
5831        } = recovery;
5832        let RecognizeRequest {
5833            stop_state,
5834            index,
5835            rule_start_index,
5836            decision_start_index,
5837            init_action_rules,
5838            predicates,
5839            rule_args,
5840            member_actions,
5841            return_actions,
5842            local_int_arg,
5843            member_values,
5844            return_values,
5845            rule_alt_number,
5846            track_alt_numbers,
5847            consumed_eof,
5848            precedence,
5849            depth,
5850            ..
5851        } = request;
5852        let follow_symbols = state_expected_symbols(atn, transition.target());
5853        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
5854            transition,
5855            index,
5856            atn.max_token_type(),
5857            &expected_symbols,
5858            &follow_symbols,
5859        ) else {
5860            return Vec::new();
5861        };
5862        self.recognize_state(
5863            atn,
5864            RecognizeRequest {
5865                state_number: target,
5866                stop_state,
5867                index,
5868                rule_start_index,
5869                decision_start_index,
5870                init_action_rules,
5871                predicates,
5872                rule_args,
5873                member_actions,
5874                return_actions,
5875                local_int_arg,
5876                member_values,
5877                return_values,
5878                rule_alt_number,
5879                track_alt_numbers,
5880                consumed_eof,
5881                precedence,
5882                depth: depth + 1,
5883                recovery_symbols: BTreeSet::new(),
5884                recovery_state: None,
5885            },
5886            visiting,
5887            memo,
5888            expected,
5889        )
5890        .into_iter()
5891        .map(|mut outcome| {
5892            outcome.diagnostics.insert(0, diagnostic.clone());
5893            outcome.nodes.insert(
5894                0,
5895                RecognizedNode::MissingToken {
5896                    token_type,
5897                    at_index: index,
5898                    text: text.clone(),
5899                },
5900            );
5901            outcome
5902        })
5903        .collect()
5904    }
5905
5906    /// Attempts to reach `stop_state` and carries semantic actions for the
5907    /// selected parser path.
5908    #[allow(clippy::too_many_lines)]
5909    fn recognize_state(
5910        &mut self,
5911        atn: &Atn,
5912        request: RecognizeRequest<'_>,
5913        visiting: &mut BTreeSet<RecognizeKey>,
5914        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
5915        expected: &mut ExpectedTokens,
5916    ) -> Vec<RecognizeOutcome> {
5917        let request_template = request.clone();
5918        let RecognizeRequest {
5919            state_number,
5920            stop_state,
5921            index,
5922            rule_start_index,
5923            decision_start_index,
5924            init_action_rules,
5925            predicates,
5926            rule_args,
5927            member_actions,
5928            return_actions,
5929            local_int_arg,
5930            member_values,
5931            return_values,
5932            rule_alt_number,
5933            track_alt_numbers,
5934            consumed_eof,
5935            precedence,
5936            depth,
5937            recovery_symbols,
5938            recovery_state,
5939        } = request;
5940        if depth > RECOGNITION_DEPTH_LIMIT {
5941            return Vec::new();
5942        }
5943        if state_number == stop_state {
5944            return stop_outcome(
5945                index,
5946                consumed_eof,
5947                rule_alt_number,
5948                member_values,
5949                return_values,
5950            );
5951        }
5952        let key = RecognizeKey {
5953            state_number,
5954            stop_state,
5955            index,
5956            rule_start_index,
5957            decision_start_index,
5958            local_int_arg,
5959            member_values: member_values.clone(),
5960            return_values: return_values.clone(),
5961            rule_alt_number,
5962            track_alt_numbers,
5963            consumed_eof,
5964            precedence,
5965            recovery_symbols: recovery_symbols.clone(),
5966            recovery_state,
5967        };
5968        if let Some(outcomes) = memo.get(&key) {
5969            return outcomes.clone();
5970        }
5971
5972        let visit_key = key.clone();
5973        if !visiting.insert(visit_key.clone()) {
5974            return Vec::new();
5975        }
5976
5977        let Some(state) = atn.state(state_number) else {
5978            visiting.remove(&visit_key);
5979            return Vec::new();
5980        };
5981        let next_decision_start_index = if starts_prediction_decision(state) {
5982            Some(index)
5983        } else {
5984            decision_start_index
5985        };
5986        let (epsilon_recovery_symbols, epsilon_recovery_state) =
5987            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
5988        let mut outcomes = Vec::new();
5989        for (transition_index, transition) in state.transitions.iter().enumerate() {
5990            let decision = transition_decision(atn, state, transition_index, predicates);
5991            let next_alt_number =
5992                next_alt_number(state, transition_index, rule_alt_number, track_alt_numbers);
5993            match transition {
5994                Transition::Epsilon { target } | Transition::Action { target, .. } => {
5995                    let action_rule_index = match transition {
5996                        Transition::Action { rule_index, .. } => Some(*rule_index),
5997                        _ => None,
5998                    };
5999                    outcomes.extend(self.recognize_epsilon_or_action_step(
6000                        atn,
6001                        &request_template,
6002                        EpsilonActionStep {
6003                            source_state: state_number,
6004                            target: *target,
6005                            action_rule_index,
6006                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
6007                            decision,
6008                            decision_start_index: next_decision_start_index,
6009                            alt_number: next_alt_number,
6010                            recovery_symbols: epsilon_recovery_symbols.clone(),
6011                            recovery_state: epsilon_recovery_state,
6012                        },
6013                        RecognizeScratch {
6014                            visiting,
6015                            memo,
6016                            expected,
6017                        },
6018                    ));
6019                }
6020                Transition::Predicate {
6021                    target,
6022                    rule_index,
6023                    pred_index,
6024                    ..
6025                } => {
6026                    let predicate = PredicateEval {
6027                        index,
6028                        rule_index: *rule_index,
6029                        pred_index: *pred_index,
6030                        predicates,
6031                        context: None,
6032                        local_int_arg,
6033                        member_values: &member_values,
6034                    };
6035                    if self.parser_predicate_matches(predicate) {
6036                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
6037                        outcomes.extend(
6038                            self.recognize_state(
6039                                atn,
6040                                RecognizeRequest {
6041                                    state_number: *target,
6042                                    stop_state,
6043                                    index,
6044                                    rule_start_index,
6045                                    decision_start_index: next_decision_start_index,
6046                                    init_action_rules,
6047                                    predicates,
6048                                    rule_args,
6049                                    member_actions,
6050                                    return_actions,
6051                                    local_int_arg,
6052                                    member_values: member_values.clone(),
6053                                    return_values: return_values.clone(),
6054                                    rule_alt_number: next_alt_number,
6055                                    track_alt_numbers,
6056                                    consumed_eof,
6057                                    precedence,
6058                                    depth: depth + 1,
6059                                    recovery_symbols: epsilon_recovery_symbols.clone(),
6060                                    recovery_state: epsilon_recovery_state,
6061                                },
6062                                visiting,
6063                                memo,
6064                                expected,
6065                            )
6066                            .into_iter()
6067                            .map(|mut outcome| {
6068                                prepend_decision(&mut outcome, decision);
6069                                if let Some(rule_index) = left_recursive_boundary {
6070                                    outcome.nodes.insert(
6071                                        0,
6072                                        RecognizedNode::LeftRecursiveBoundary { rule_index },
6073                                    );
6074                                }
6075                                outcome
6076                            }),
6077                        );
6078                    } else if let Some(message) =
6079                        self.parser_predicate_failure_message(*rule_index, *pred_index, predicates)
6080                    {
6081                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
6082                            rule_index: *rule_index,
6083                            index,
6084                            message,
6085                            member_values: member_values.clone(),
6086                            return_values: return_values.clone(),
6087                            rule_alt_number,
6088                        }));
6089                    } else {
6090                        record_predicate_no_viable(expected, next_decision_start_index, index);
6091                    }
6092                }
6093                Transition::Precedence {
6094                    target,
6095                    precedence: transition_precedence,
6096                } => {
6097                    if *transition_precedence >= precedence {
6098                        outcomes.extend(
6099                            self.recognize_state(
6100                                atn,
6101                                RecognizeRequest {
6102                                    state_number: *target,
6103                                    stop_state,
6104                                    index,
6105                                    rule_start_index,
6106                                    decision_start_index: next_decision_start_index,
6107                                    init_action_rules,
6108                                    predicates,
6109                                    rule_args,
6110                                    member_actions,
6111                                    return_actions,
6112                                    local_int_arg,
6113                                    member_values: member_values.clone(),
6114                                    return_values: return_values.clone(),
6115                                    rule_alt_number: next_alt_number,
6116                                    track_alt_numbers,
6117                                    consumed_eof,
6118                                    precedence,
6119                                    depth: depth + 1,
6120                                    recovery_symbols: epsilon_recovery_symbols.clone(),
6121                                    recovery_state: epsilon_recovery_state,
6122                                },
6123                                visiting,
6124                                memo,
6125                                expected,
6126                            )
6127                            .into_iter()
6128                            .map(|mut outcome| {
6129                                prepend_decision(&mut outcome, decision);
6130                                outcome
6131                            }),
6132                        );
6133                    }
6134                }
6135                Transition::Rule {
6136                    target,
6137                    rule_index,
6138                    follow_state,
6139                    precedence: rule_precedence,
6140                    ..
6141                } => {
6142                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
6143                    else {
6144                        continue;
6145                    };
6146                    let child_local_int_arg =
6147                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
6148                    let expected_before_child = expected.clone();
6149                    let children = self.recognize_state(
6150                        atn,
6151                        RecognizeRequest {
6152                            state_number: *target,
6153                            stop_state: child_stop,
6154                            index,
6155                            rule_start_index: index,
6156                            decision_start_index: None,
6157                            init_action_rules,
6158                            predicates,
6159                            rule_args,
6160                            member_actions,
6161                            return_actions,
6162                            local_int_arg: child_local_int_arg,
6163                            member_values: member_values.clone(),
6164                            return_values: BTreeMap::new(),
6165                            rule_alt_number: 0,
6166                            track_alt_numbers,
6167                            consumed_eof: false,
6168                            precedence: *rule_precedence,
6169                            depth: depth + 1,
6170                            recovery_symbols: epsilon_recovery_symbols.clone(),
6171                            recovery_state: epsilon_recovery_state,
6172                        },
6173                        visiting,
6174                        memo,
6175                        expected,
6176                    );
6177                    let children = if children.is_empty() {
6178                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
6179                            atn,
6180                            rule_index: *rule_index,
6181                            start_index: index,
6182                            follow_state: *follow_state,
6183                            stop_state,
6184                            member_values: member_values.clone(),
6185                            expected,
6186                        })
6187                    } else {
6188                        children
6189                    };
6190                    let preserve_child_expected =
6191                        self.child_expected_reaches_clean_eof(&children, expected);
6192                    restore_expected(
6193                        &children,
6194                        index,
6195                        expected,
6196                        expected_before_child,
6197                        preserve_child_expected,
6198                    );
6199                    for child in children {
6200                        let child_node = RecognizedNode::Rule {
6201                            rule_index: *rule_index,
6202                            invoking_state: invoking_state_number(state_number),
6203                            alt_number: child.alt_number,
6204                            start_index: index,
6205                            stop_index: self.rule_stop_token_index(child.index, child.consumed_eof),
6206                            return_values: child.return_values.clone(),
6207                            children: fold_left_recursive_boundaries(child.nodes.clone()),
6208                        };
6209                        outcomes.extend(
6210                            self.recognize_state(
6211                                atn,
6212                                RecognizeRequest {
6213                                    state_number: *follow_state,
6214                                    stop_state,
6215                                    index: child.index,
6216                                    rule_start_index,
6217                                    decision_start_index: next_decision_start_index,
6218                                    init_action_rules,
6219                                    predicates,
6220                                    rule_args,
6221                                    member_actions,
6222                                    return_actions,
6223                                    local_int_arg,
6224                                    member_values: child.member_values.clone(),
6225                                    return_values: return_values.clone(),
6226                                    rule_alt_number,
6227                                    track_alt_numbers,
6228                                    consumed_eof: consumed_eof || child.consumed_eof,
6229                                    precedence,
6230                                    depth: depth + 1,
6231                                    recovery_symbols: BTreeSet::new(),
6232                                    recovery_state: None,
6233                                },
6234                                visiting,
6235                                memo,
6236                                expected,
6237                            )
6238                            .into_iter()
6239                            .map(|mut outcome| {
6240                                outcome.consumed_eof |= child.consumed_eof;
6241                                let mut diagnostics = child.diagnostics.clone();
6242                                diagnostics.append(&mut outcome.diagnostics);
6243                                outcome.diagnostics = diagnostics;
6244                                let mut decisions = child.decisions.clone();
6245                                decisions.append(&mut outcome.decisions);
6246                                outcome.decisions = decisions;
6247                                prepend_decision(&mut outcome, decision);
6248                                let mut actions = child.actions.clone();
6249                                if init_action_rules.contains(rule_index) {
6250                                    actions.insert(
6251                                        0,
6252                                        ParserAction::new_rule_init(
6253                                            *rule_index,
6254                                            index,
6255                                            Some(*follow_state),
6256                                        ),
6257                                    );
6258                                }
6259                                actions.append(&mut outcome.actions);
6260                                outcome.actions = actions;
6261                                outcome.nodes.insert(0, child_node.clone());
6262                                outcome
6263                            }),
6264                        );
6265                    }
6266                }
6267                Transition::Atom { target, .. }
6268                | Transition::Range { target, .. }
6269                | Transition::Set { target, .. }
6270                | Transition::NotSet { target, .. }
6271                | Transition::Wildcard { target, .. } => {
6272                    let symbol = self.token_type_at(index);
6273                    if transition.matches(symbol, 1, atn.max_token_type()) {
6274                        let next_index = self.consume_index(index, symbol);
6275                        outcomes.extend(
6276                            self.recognize_state(
6277                                atn,
6278                                RecognizeRequest {
6279                                    state_number: *target,
6280                                    stop_state,
6281                                    index: next_index,
6282                                    rule_start_index,
6283                                    decision_start_index: next_decision_start_index,
6284                                    init_action_rules,
6285                                    predicates,
6286                                    rule_args,
6287                                    member_actions,
6288                                    return_actions,
6289                                    local_int_arg,
6290                                    member_values: member_values.clone(),
6291                                    return_values: return_values.clone(),
6292                                    rule_alt_number: next_alt_number,
6293                                    track_alt_numbers,
6294                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
6295                                    precedence,
6296                                    depth: depth + 1,
6297                                    recovery_symbols: BTreeSet::new(),
6298                                    recovery_state: None,
6299                                },
6300                                visiting,
6301                                memo,
6302                                expected,
6303                            )
6304                            .into_iter()
6305                            .map(|mut outcome| {
6306                                prepend_decision(&mut outcome, decision);
6307                                outcome.consumed_eof |= symbol == TOKEN_EOF;
6308                                outcome.nodes.insert(0, RecognizedNode::Token { index });
6309                                outcome
6310                            }),
6311                        );
6312                    } else {
6313                        let expected_symbols =
6314                            recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
6315                        if expected_symbols.contains(&symbol) {
6316                            continue;
6317                        }
6318                        expected.record_transition(index, transition, atn.max_token_type());
6319                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
6320                        let before_recovery = outcomes.len();
6321                        let recovery_request = request_template.clone();
6322                        outcomes.extend(
6323                            self.single_token_deletion_recovery(RecoveryRequest {
6324                                atn,
6325                                transition,
6326                                expected_symbols: expected_symbols.clone(),
6327                                target: *target,
6328                                request: recovery_request.clone(),
6329                                visiting,
6330                                memo,
6331                                expected,
6332                            })
6333                            .into_iter()
6334                            .map(|mut outcome| {
6335                                prepend_decision(&mut outcome, decision);
6336                                outcome
6337                            }),
6338                        );
6339                        if !state_is_left_recursive_rule(atn, state) {
6340                            outcomes.extend(
6341                                self.single_token_insertion_recovery(RecoveryRequest {
6342                                    atn,
6343                                    transition,
6344                                    expected_symbols: expected_symbols.clone(),
6345                                    target: *target,
6346                                    request: recovery_request.clone(),
6347                                    visiting,
6348                                    memo,
6349                                    expected,
6350                                })
6351                                .into_iter()
6352                                .map(|mut outcome| {
6353                                    prepend_decision(&mut outcome, decision);
6354                                    outcome
6355                                }),
6356                            );
6357                        }
6358                        outcomes.extend(self.current_token_deletion_recovery(
6359                            CurrentTokenDeletionRequest {
6360                                atn,
6361                                expected_symbols: expected_symbols.clone(),
6362                                request: recovery_request.clone(),
6363                                visiting,
6364                                memo,
6365                                expected,
6366                            },
6367                        ));
6368                        if outcomes.len() == before_recovery {
6369                            outcomes.extend(self.consuming_failure_fallback(
6370                                ConsumingFailureFallback {
6371                                    atn,
6372                                    target: *target,
6373                                    request: recovery_request,
6374                                    symbol,
6375                                    expected_symbols,
6376                                    decision_start_index: next_decision_start_index,
6377                                    decision,
6378                                },
6379                                visiting,
6380                                memo,
6381                                expected,
6382                            ));
6383                        }
6384                    }
6385                }
6386            }
6387        }
6388
6389        visiting.remove(&visit_key);
6390        self.record_prediction_diagnostics(atn, state, index, &outcomes);
6391        if matches!(
6392            self.prediction_mode,
6393            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
6394        ) {
6395            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes);
6396        }
6397        dedupe_outcomes(&mut outcomes);
6398        memo.insert(key, outcomes.clone());
6399        outcomes
6400    }
6401
6402    /// Follows an epsilon or semantic-action transition while preserving the
6403    /// path-local side effects that may later become generated action output.
6404    fn recognize_epsilon_or_action_step(
6405        &mut self,
6406        atn: &Atn,
6407        request: &RecognizeRequest<'_>,
6408        step: EpsilonActionStep,
6409        scratch: RecognizeScratch<'_>,
6410    ) -> Vec<RecognizeOutcome> {
6411        let RecognizeScratch {
6412            visiting,
6413            memo,
6414            expected,
6415        } = scratch;
6416        let action = step.action_rule_index.map(|rule_index| {
6417            ParserAction::new(
6418                step.source_state,
6419                rule_index,
6420                request.rule_start_index,
6421                self.rule_stop_token_index(request.index, request.consumed_eof),
6422            )
6423        });
6424        let next_member_values = if action.is_some() {
6425            member_values_after_action(
6426                step.source_state,
6427                request.member_actions,
6428                &request.member_values,
6429            )
6430        } else {
6431            request.member_values.clone()
6432        };
6433        let next_return_values = action.map_or_else(
6434            || request.return_values.clone(),
6435            |action| {
6436                return_values_after_action(
6437                    step.source_state,
6438                    action.rule_index(),
6439                    request.return_actions,
6440                    &request.return_values,
6441                )
6442            },
6443        );
6444
6445        self.recognize_state(
6446            atn,
6447            RecognizeRequest {
6448                state_number: step.target,
6449                stop_state: request.stop_state,
6450                index: request.index,
6451                rule_start_index: request.rule_start_index,
6452                decision_start_index: step.decision_start_index,
6453                init_action_rules: request.init_action_rules,
6454                predicates: request.predicates,
6455                rule_args: request.rule_args,
6456                member_actions: request.member_actions,
6457                return_actions: request.return_actions,
6458                local_int_arg: request.local_int_arg,
6459                member_values: next_member_values,
6460                return_values: next_return_values,
6461                rule_alt_number: step.alt_number,
6462                track_alt_numbers: request.track_alt_numbers,
6463                consumed_eof: request.consumed_eof,
6464                precedence: request.precedence,
6465                depth: request.depth + 1,
6466                recovery_symbols: step.recovery_symbols,
6467                recovery_state: step.recovery_state,
6468            },
6469            visiting,
6470            memo,
6471            expected,
6472        )
6473        .into_iter()
6474        .map(|mut outcome| {
6475            prepend_decision(&mut outcome, step.decision);
6476            if let Some(rule_index) = step.left_recursive_boundary {
6477                outcome
6478                    .nodes
6479                    .insert(0, RecognizedNode::LeftRecursiveBoundary { rule_index });
6480            }
6481            if let Some(action) = action {
6482                outcome.actions.insert(0, action);
6483            }
6484            outcome
6485        })
6486        .collect()
6487    }
6488
6489    /// Reads the token type at an absolute token-stream index without moving
6490    /// the parser's stream cursor. The fast recognizer probes lookahead at
6491    /// every state visit, so avoiding the seek round-trip is a measurable
6492    /// hot-path win on long inputs.
6493    fn token_type_at(&mut self, index: usize) -> i32 {
6494        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
6495            self.input.fill();
6496        }
6497        self.input.token_type_at_index(index)
6498    }
6499
6500    /// Returns the cached `state_expected_symbols` set for an ATN state.
6501    ///
6502    /// The fast recognizer consults this set on every state visit through
6503    /// `next_recovery_context`; the underlying DFS is a pure function of the
6504    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
6505    ///
6506    /// Caching is layered through `intern_recovery_symbols` so two ATN states
6507    /// with the same expected-symbol set share one `Rc`. That invariant is
6508    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
6509    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
6510    /// always interned before it ends up in a key.
6511    fn cached_state_expected_symbols(
6512        &mut self,
6513        atn: &Atn,
6514        state_number: usize,
6515    ) -> Rc<BTreeSet<i32>> {
6516        if let Some(cached) = self.state_expected_cache.get(&state_number) {
6517            return Rc::clone(cached);
6518        }
6519        let symbols = state_expected_symbols(atn, state_number);
6520        let entry = self.intern_recovery_symbols(symbols);
6521        self.state_expected_cache
6522            .insert(state_number, Rc::clone(&entry));
6523        entry
6524    }
6525
6526    fn cached_state_expected_token_set(
6527        &mut self,
6528        atn: &Atn,
6529        state_number: usize,
6530    ) -> Rc<TokenBitSet> {
6531        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
6532            return Rc::clone(cached);
6533        }
6534        let symbols = Rc::new(state_expected_token_set(atn, state_number));
6535        self.state_expected_token_cache
6536            .insert(state_number, Rc::clone(&symbols));
6537        symbols
6538    }
6539
6540    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
6541        if self.rule_stop_reach_cache.len() <= state_number {
6542            self.rule_stop_reach_cache
6543                .resize_with(atn.states().len().max(state_number + 1), || None);
6544        }
6545        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
6546            return reaches;
6547        }
6548        let reaches = state_can_reach_rule_stop(atn, state_number);
6549        self.rule_stop_reach_cache[state_number] = Some(reaches);
6550        reaches
6551    }
6552
6553    /// Returns the parser's empty `recovery_symbols` singleton so callers can
6554    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
6555    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
6556        Rc::clone(&self.empty_recovery_symbols)
6557    }
6558
6559    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
6560    /// recognizer can hash and compare keys by pointer.
6561    ///
6562    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
6563    /// come from this method or the empty singleton; otherwise two
6564    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
6565    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
6566    /// recognition coordinates.
6567    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
6568        if set.is_empty() {
6569            return Rc::clone(&self.empty_recovery_symbols);
6570        }
6571        let candidate = Rc::new(set);
6572        match self.recovery_symbols_intern.get(&candidate) {
6573            Some(existing) => Rc::clone(existing),
6574            None => {
6575                self.recovery_symbols_intern
6576                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
6577                candidate
6578            }
6579        }
6580    }
6581
6582    /// Returns the cached look-1 entry for a decision state, computing it on
6583    /// first use. Multi-alternative states are visited many times during
6584    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
6585    /// hash lookup per visit.
6586    fn cached_decision_lookahead(
6587        &mut self,
6588        atn: &Atn,
6589        state: &AtnState,
6590        rule_stop_state: usize,
6591    ) -> Rc<DecisionLookahead> {
6592        // Hit the parser-instance cache first. Decision lookahead is purely
6593        // a function of the ATN/state, so on a warm cache we skip the
6594        // thread-local + RefCell + HashMap-entry dance through
6595        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
6596        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
6597        // hashmap-entry overhead in profiles.
6598        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number) {
6599            return Rc::clone(cached);
6600        }
6601        let entry = with_shared_atn_caches(atn, |cache| {
6602            if let Some(cached) = cache.decision_lookahead.get(&state.state_number) {
6603                return Rc::clone(cached);
6604            }
6605            let mut entry = DecisionLookahead {
6606                transitions: Vec::with_capacity(state.transitions.len()),
6607            };
6608            for transition in &state.transitions {
6609                entry.transitions.push(transition_first_set(
6610                    atn,
6611                    transition,
6612                    rule_stop_state,
6613                    &mut cache.first_set,
6614                ));
6615            }
6616            let entry = Rc::new(entry);
6617            cache
6618                .decision_lookahead
6619                .insert(state.state_number, Rc::clone(&entry));
6620            entry
6621        });
6622        self.decision_lookahead_cache
6623            .insert(state.state_number, Rc::clone(&entry));
6624        entry
6625    }
6626
6627    fn cached_rule_first_set(
6628        &mut self,
6629        atn: &Atn,
6630        target: usize,
6631        child_stop: usize,
6632    ) -> Rc<FirstSet> {
6633        if self.rule_first_set_cache.len() <= target {
6634            self.rule_first_set_cache
6635                .resize_with(atn.states().len().max(target + 1), || None);
6636        }
6637        if let Some(cached) = self
6638            .rule_first_set_cache
6639            .get(target)
6640            .and_then(Option::as_ref)
6641        {
6642            return Rc::clone(cached);
6643        }
6644        let first = with_shared_first_set_cache(atn, |cache| {
6645            rule_first_set(atn, target, child_stop, cache)
6646        });
6647        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
6648        first
6649    }
6650
6651    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
6652        if self.empty_cycle_cache.len() <= state_number {
6653            self.empty_cycle_cache
6654                .resize_with(atn.states().len().max(state_number + 1), || None);
6655        }
6656        if let Some(cached) = self.empty_cycle_cache[state_number] {
6657            return cached;
6658        }
6659        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
6660        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
6661        self.empty_cycle_cache[state_number] = Some(result);
6662        result
6663    }
6664
6665    fn empty_path_reaches_state(
6666        &mut self,
6667        atn: &Atn,
6668        state_number: usize,
6669        target_state: usize,
6670        visited: &mut FxHashSet<usize>,
6671    ) -> bool {
6672        if !visited.insert(state_number) {
6673            return false;
6674        }
6675        let Some(state) = atn.state(state_number) else {
6676            return false;
6677        };
6678        for transition in &state.transitions {
6679            match transition {
6680                Transition::Atom { .. }
6681                | Transition::Range { .. }
6682                | Transition::Set { .. }
6683                | Transition::NotSet { .. }
6684                | Transition::Wildcard { .. } => {}
6685                Transition::Rule {
6686                    target,
6687                    rule_index,
6688                    follow_state,
6689                    ..
6690                } => {
6691                    if *target == target_state
6692                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
6693                    {
6694                        return true;
6695                    }
6696                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
6697                    else {
6698                        continue;
6699                    };
6700                    if self
6701                        .cached_rule_first_set(atn, *target, child_stop)
6702                        .nullable
6703                        && (*follow_state == target_state
6704                            || self.empty_path_reaches_state(
6705                                atn,
6706                                *follow_state,
6707                                target_state,
6708                                visited,
6709                            ))
6710                    {
6711                        return true;
6712                    }
6713                }
6714                Transition::Epsilon { target }
6715                | Transition::Predicate { target, .. }
6716                | Transition::Action { target, .. }
6717                | Transition::Precedence { target, .. } => {
6718                    if *target == target_state
6719                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
6720                    {
6721                        return true;
6722                    }
6723                }
6724            }
6725        }
6726        false
6727    }
6728
6729    /// Decides whether a clean one-outcome entry is worth storing in the full
6730    /// outcome memo table for this parse.
6731    fn should_memoize_single_outcome(&mut self, key: &FastRecognizeKey) -> bool {
6732        match self.single_outcome_memo_mode {
6733            SingleOutcomeMemoMode::Promote => true,
6734            SingleOutcomeMemoMode::Sparse => false,
6735            SingleOutcomeMemoMode::Probe => {
6736                self.single_outcome_probe_samples += 1;
6737                if !self.single_outcome_probe_seen.insert(key.clone()) {
6738                    self.single_outcome_probe_repeats += 1;
6739                }
6740                if self.single_outcome_probe_repeats >= CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
6741                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Promote;
6742                    self.single_outcome_probe_seen.clear();
6743                    return true;
6744                }
6745                if self.single_outcome_probe_samples >= CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT {
6746                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Sparse;
6747                    self.single_outcome_probe_seen.clear();
6748                    return false;
6749                }
6750                true
6751            }
6752        }
6753    }
6754
6755    /// Clones the visible token at an absolute token-stream index.
6756    fn token_at(&mut self, index: usize) -> Option<CommonToken> {
6757        self.input.get(index).cloned()
6758    }
6759
6760    /// Clones the shared token handle at an absolute token-stream index.
6761    fn token_ref_at(&mut self, index: usize) -> Option<TokenRef> {
6762        self.input.get_ref(index)
6763    }
6764
6765    /// Normalizes the current token-stream cursor to the next parser-visible
6766    /// token before capturing a rule start boundary.
6767    fn current_visible_index(&mut self) -> usize {
6768        let index = self.input.index();
6769        self.input.seek(index);
6770        self.input.index()
6771    }
6772
6773    /// Reports whether a child rule reached EOF cleanly while also recording
6774    /// an EOF expectation from a longer path inside that child.
6775    fn child_expected_reaches_clean_eof(
6776        &mut self,
6777        children: &[RecognizeOutcome],
6778        expected: &ExpectedTokens,
6779    ) -> bool {
6780        let Some(index) = expected.index else {
6781            return false;
6782        };
6783        self.token_type_at(index) == TOKEN_EOF
6784            && children
6785                .iter()
6786                .any(|child| child.diagnostics.is_empty() && child.index == index)
6787    }
6788
6789    /// Finds the previous token visible to the parser before `index`.
6790    ///
6791    /// The token stream cursor skips hidden-channel tokens, so subtracting one
6792    /// from a visible-token index can point at whitespace. Parser intervals use
6793    /// this helper to stop at the previous visible token while preserving hidden
6794    /// text inside the rendered interval.
6795    fn previous_token_index(&mut self, index: usize) -> Option<usize> {
6796        self.input.previous_visible_token_index(index)
6797    }
6798
6799    /// Returns the token-stream index used as a rule stop boundary.
6800    ///
6801    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
6802    /// stop at `index` rather than at the previous visible token.
6803    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
6804        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
6805            Some(index)
6806        } else {
6807            self.previous_token_index(index)
6808        }
6809    }
6810
6811    /// Stop-token index for a rule's `@after` action, matching the boundary that
6812    /// `finish_rule` records on the rule context.
6813    ///
6814    /// A rule that matched EOF leaves the cursor parked on the EOF token
6815    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
6816    /// the current index rather than the previous visible token. Without this,
6817    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
6818    /// report the token before EOF (or `None` for empty input), diverging from
6819    /// the rule context that `finish_rule` builds.
6820    ///
6821    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
6822    /// rule ends right before EOF without matching it (the cursor is parked on
6823    /// EOF, but the rule did not consume it). Prefer
6824    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
6825    /// rule context already recorded with the real flag. Kept for callers without
6826    /// the rule tree in hand.
6827    #[must_use]
6828    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
6829        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
6830        self.rule_stop_token_index(current_index, consumed_eof)
6831    }
6832
6833    /// Stop-token index for a rule's `@after` action, taken from the stop token
6834    /// the rule context already recorded.
6835    ///
6836    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
6837    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
6838    /// the rule context — even when the rule ends immediately before EOF without
6839    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
6840    /// to the cursor-based inference only when the tree carries no rule stop.
6841    #[must_use]
6842    pub fn after_action_stop_index_for_tree(
6843        &mut self,
6844        tree: &ParseTree,
6845        current_index: usize,
6846    ) -> Option<usize> {
6847        if let ParseTree::Rule(rule) = tree {
6848            if let Some(stop) = rule.context().stop() {
6849                let token_index = stop.token_index();
6850                if token_index >= 0 {
6851                    return Some(token_index.unsigned_abs());
6852                }
6853            }
6854        }
6855        self.after_action_stop_index(current_index)
6856    }
6857
6858    /// Start-token index for a rule's `@after` action, taken from the start token
6859    /// the rule context already recorded.
6860    ///
6861    /// `enter_rule` sets the rule context start to the first visible token (it
6862    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
6863    /// `$text` in an `@after` action aligned with the rule context — even when the
6864    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
6865    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
6866    /// the tree carries no rule start.
6867    #[must_use]
6868    pub fn after_action_start_index_for_tree(
6869        &self,
6870        tree: &ParseTree,
6871        fallback_index: usize,
6872    ) -> usize {
6873        if let ParseTree::Rule(rule) = tree {
6874            if let Some(start) = rule.context().start() {
6875                let token_index = start.token_index();
6876                if token_index >= 0 {
6877                    return token_index.unsigned_abs();
6878                }
6879            }
6880        }
6881        fallback_index
6882    }
6883
6884    /// Returns the rule stop token for a selected parse path.
6885    ///
6886    /// EOF transitions do not advance the token-stream cursor, so an EOF match
6887    /// must use the current token rather than the previous visible token.
6888    fn rule_stop_token_ref(&mut self, index: usize, consumed_eof: bool) -> Option<TokenRef> {
6889        self.rule_stop_token_index(index, consumed_eof)
6890            .and_then(|token_index| self.token_ref_at(token_index))
6891    }
6892
6893    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
6894    ///
6895    /// Generated Java reports the failed-predicate message at the current
6896    /// lookahead, then consumes until rule recovery can resume. The metadata
6897    /// runtime models the same visible tree shape by keeping skipped tokens as
6898    /// error nodes and returning from the active rule at EOF.
6899    fn predicate_failure_recovery(
6900        &mut self,
6901        request: PredicateFailureRecovery<'_>,
6902    ) -> RecognizeOutcome {
6903        let PredicateFailureRecovery {
6904            rule_index,
6905            index,
6906            message,
6907            member_values,
6908            return_values,
6909            rule_alt_number,
6910        } = request;
6911        let rule_name = self
6912            .rule_names()
6913            .get(rule_index)
6914            .map_or_else(|| rule_index.to_string(), Clone::clone);
6915        let diagnostic = diagnostic_for_token(
6916            self.token_at(index).as_ref(),
6917            format!("rule {rule_name} {message}"),
6918        );
6919        let mut nodes = Vec::new();
6920        let mut next_index = index;
6921        loop {
6922            let symbol = self.token_type_at(next_index);
6923            if symbol == TOKEN_EOF {
6924                break;
6925            }
6926            nodes.push(RecognizedNode::ErrorToken { index: next_index });
6927            let after = self.consume_index(next_index, symbol);
6928            if after == next_index {
6929                break;
6930            }
6931            next_index = after;
6932        }
6933        RecognizeOutcome {
6934            index: next_index,
6935            consumed_eof: false,
6936            alt_number: rule_alt_number,
6937            member_values,
6938            return_values,
6939            diagnostics: vec![diagnostic],
6940            decisions: Vec::new(),
6941            actions: Vec::new(),
6942            nodes,
6943        }
6944    }
6945
6946    /// Evaluates a supported parser predicate at a speculative input index.
6947    ///
6948    /// Parser ATN simulation is index-based, so predicate evaluation seeks to
6949    /// the candidate index before applying lookahead. A missing predicate entry
6950    /// means the generator did not opt into runtime evaluation for that
6951    /// coordinate and the transition remains viable.
6952    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
6953        let PredicateEval {
6954            index,
6955            rule_index,
6956            pred_index,
6957            predicates,
6958            context,
6959            local_int_arg,
6960            member_values,
6961        } = eval;
6962        let Some((_, _, predicate)) = predicates
6963            .iter()
6964            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
6965        else {
6966            return true;
6967        };
6968        self.input.seek(index);
6969        match predicate {
6970            ParserPredicate::True => true,
6971            ParserPredicate::False => false,
6972            ParserPredicate::FalseWithMessage { .. } => false,
6973            ParserPredicate::Invoke { value } => {
6974                let key = (rule_index, pred_index);
6975                if !self.invoked_predicates.contains(&key) {
6976                    self.invoked_predicates.push(key);
6977                    use std::io::Write as _;
6978                    let mut stdout = std::io::stdout().lock();
6979                    let _ = writeln!(stdout, "eval={value}");
6980                }
6981                *value
6982            }
6983            ParserPredicate::LookaheadTextEquals { offset, text } => {
6984                self.input.lt(*offset).and_then(Token::text) == Some(*text)
6985            }
6986            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
6987                self.la(*offset) != *token_type
6988            }
6989            ParserPredicate::TokenPairAdjacent => {
6990                let Some(first) = self.input.lt(-2).map(Token::token_index) else {
6991                    return false;
6992                };
6993                let Some(second) = self.input.lt(-1).map(Token::token_index) else {
6994                    return false;
6995                };
6996                first + 1 == second
6997            }
6998            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
6999                .and_then(|context| {
7000                    context.children().iter().find_map(|child| match child {
7001                        ParseTree::Rule(rule) if rule.context().rule_index() == *rule_index => {
7002                            Some(child.text())
7003                        }
7004                        ParseTree::Rule(_) | ParseTree::Terminal(_) | ParseTree::Error(_) => None,
7005                    })
7006                })
7007                .is_none_or(|actual| actual != *text),
7008            ParserPredicate::LocalIntEquals { value } => {
7009                local_int_arg.is_none_or(|(_, actual)| actual == *value)
7010            }
7011            ParserPredicate::LocalIntLessOrEqual { value } => {
7012                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
7013            }
7014            ParserPredicate::MemberModuloEquals {
7015                member,
7016                modulus,
7017                value,
7018                equals,
7019            } => {
7020                if *modulus == 0 {
7021                    return false;
7022                }
7023                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
7024                (actual == *value) == *equals
7025            }
7026            ParserPredicate::MemberEquals {
7027                member,
7028                value,
7029                equals,
7030            } => {
7031                let actual = member_values.get(member).copied().unwrap_or_default();
7032                (actual == *value) == *equals
7033            }
7034        }
7035    }
7036
7037    /// Returns a generated fail-option message for a predicate coordinate.
7038    fn parser_predicate_failure_message(
7039        &self,
7040        rule_index: usize,
7041        pred_index: usize,
7042        predicates: &[(usize, usize, ParserPredicate)],
7043    ) -> Option<&'static str> {
7044        predicates
7045            .iter()
7046            .find_map(|(rule, pred, predicate)| match predicate {
7047                ParserPredicate::FalseWithMessage { message }
7048                    if *rule == rule_index && *pred == pred_index =>
7049                {
7050                    Some(*message)
7051                }
7052                _ => None,
7053            })
7054    }
7055
7056    /// Returns the token-stream index after consuming `symbol` at `index`.
7057    ///
7058    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
7059    /// index stable and rely on `consumed_eof` to record that EOF was matched.
7060    /// The parser's stream cursor is left untouched: speculative recognition
7061    /// reads ahead by absolute index, so paying for `seek` on every visited
7062    /// state would dominate the hot path. Real consumption is committed by
7063    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
7064    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
7065        if symbol == TOKEN_EOF {
7066            return index;
7067        }
7068        self.input.next_visible_after(index)
7069    }
7070
7071    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
7072    /// decision that failed after consuming a shared prefix.
7073    fn no_viable_alternative(
7074        &mut self,
7075        start_index: usize,
7076        error_index: usize,
7077    ) -> ParserDiagnostic {
7078        let text = display_input_text(&self.input.text(start_index, error_index));
7079        diagnostic_for_token(
7080            self.token_at(error_index).as_ref(),
7081            format!("no viable alternative at input '{text}'"),
7082        )
7083    }
7084
7085    /// Selects the diagnostic for a failed consuming transition after all
7086    /// recovery repairs have been ruled out.
7087    fn recovery_failure_diagnostic(
7088        &mut self,
7089        index: usize,
7090        decision_start_index: Option<usize>,
7091        expected_symbols: &BTreeSet<i32>,
7092    ) -> ParserDiagnostic {
7093        if expected_symbols.len() > 1 {
7094            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
7095                return self.no_viable_alternative(decision_start, index);
7096            }
7097        }
7098        diagnostic_for_token(
7099            self.token_at(index).as_ref(),
7100            format!(
7101                "mismatched input {} expecting {}",
7102                self.token_at(index)
7103                    .as_ref()
7104                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7105                self.expected_symbols_display(expected_symbols)
7106            ),
7107        )
7108    }
7109
7110    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
7111    /// instead of inserting missing tokens in the caller.
7112    fn eof_rule_recovery_diagnostic(
7113        &mut self,
7114        index: usize,
7115        expected_symbols: &BTreeSet<i32>,
7116        expected: &ExpectedTokens,
7117    ) -> ParserDiagnostic {
7118        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
7119            &expected.symbols
7120        } else {
7121            expected_symbols
7122        };
7123        diagnostic_for_token(
7124            self.token_at(index).as_ref(),
7125            format!(
7126                "mismatched input {} expecting {}",
7127                self.token_at(index)
7128                    .as_ref()
7129                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7130                self.expected_symbols_display(symbols)
7131            ),
7132        )
7133    }
7134
7135    /// Returns token text for a buffered token interval used by generated
7136    /// `$text` actions.
7137    ///
7138    /// ANTLR treats EOF as a range boundary rather than printable input text,
7139    /// even when an action interval explicitly stops at the EOF token.
7140    pub fn text_interval(&mut self, start: usize, stop: Option<usize>) -> String {
7141        let Some(stop) = stop else {
7142            return String::new();
7143        };
7144        let stop = if self
7145            .token_at(stop)
7146            .is_some_and(|token| token.token_type() == TOKEN_EOF)
7147        {
7148            let Some(previous) = self.previous_token_index(stop) else {
7149                return String::new();
7150            };
7151            previous
7152        } else {
7153            stop
7154        };
7155        self.input.text(start, stop)
7156    }
7157
7158    /// Resets per-parse prediction diagnostics while keeping the parser-level
7159    /// reporting flag configured by generated harness code.
7160    fn clear_prediction_diagnostics(&mut self) {
7161        self.prediction_diagnostics.clear();
7162        self.reported_prediction_diagnostics.clear();
7163    }
7164
7165    /// Drops every per-parse cache that depends on ATN identity or pins
7166    /// recovery-symbol allocations.
7167    ///
7168    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
7169    /// same parser instance can legally be driven against different grammars
7170    /// in sequence. The four caches reset here are keyed by raw ATN
7171    /// coordinates (state numbers, rule indexes) and would silently hand back
7172    /// entries from a previous ATN if reused — pruning lookahead against the
7173    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
7174    /// for the rest of the process. Clearing them on every parse entry keeps
7175    /// the perf wins (caches still amortize within one parse) without making
7176    /// long-lived parsers leak memory or surface stale ATN data:
7177    ///
7178    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
7179    ///   functions of the ATN's state graph.
7180    /// * `state_expected_cache`, `state_expected_token_cache`,
7181    ///   `rule_stop_reach_cache`, and
7182    ///   `recovery_symbols_intern` together form
7183    ///   the identity invariant that lets `FastRecognizeKey` hash
7184    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
7185    ///   so a stale interned `Rc` cannot outlive its map entry.
7186    fn reset_per_parse_caches(&mut self) {
7187        self.rule_first_set_cache.clear();
7188        self.decision_lookahead_cache.clear();
7189        self.ll1_decision_cache.clear();
7190        self.empty_cycle_cache.clear();
7191        self.rule_stop_reach_cache.clear();
7192        self.single_outcome_memo_mode = SingleOutcomeMemoMode::Probe;
7193        self.single_outcome_probe_seen.clear();
7194        self.single_outcome_probe_samples = 0;
7195        self.single_outcome_probe_repeats = 0;
7196        self.recovery_symbols_intern.clear();
7197        self.state_expected_cache.clear();
7198        self.state_expected_token_cache.clear();
7199    }
7200
7201    /// Buffers ANTLR-style diagnostic-listener messages for decision states
7202    /// where multiple clean alternatives survive full-context recognition.
7203    fn record_prediction_diagnostics(
7204        &mut self,
7205        atn: &Atn,
7206        state: &AtnState,
7207        start_index: usize,
7208        outcomes: &[RecognizeOutcome],
7209    ) {
7210        if !self.report_diagnostic_errors || state.transitions.len() < 2 {
7211            return;
7212        }
7213        let Some(decision) = atn
7214            .decision_to_state()
7215            .iter()
7216            .position(|state_number| *state_number == state.state_number)
7217        else {
7218            return;
7219        };
7220        let Some(rule_index) = state.rule_index else {
7221            return;
7222        };
7223        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
7224        for outcome in outcomes
7225            .iter()
7226            .filter(|outcome| outcome.diagnostics.is_empty())
7227        {
7228            let Some(alt) = outcome.decisions.first() else {
7229                continue;
7230            };
7231            alts_by_end
7232                .entry(outcome.index)
7233                .or_default()
7234                .insert(alt + 1);
7235        }
7236        let Some((&end_index, ambig_alts)) = alts_by_end
7237            .iter()
7238            .filter(|(_, alts)| alts.len() > 1)
7239            .max_by_key(|(end, _)| *end)
7240        else {
7241            return;
7242        };
7243        let rule_name = self
7244            .rule_names()
7245            .get(rule_index)
7246            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
7247        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
7248        let input = display_input_text(&self.input.text(start_index, stop_index));
7249        let alts = ambig_alts
7250            .iter()
7251            .map(usize::to_string)
7252            .collect::<Vec<_>>()
7253            .join(", ");
7254        let key = (decision, start_index, format!("{alts}:{input}"));
7255        if !self.reported_prediction_diagnostics.insert(key) {
7256            return;
7257        }
7258        let start_token = self.token_at(start_index);
7259        let stop_token = self.token_at(stop_index);
7260        self.prediction_diagnostics.push(diagnostic_for_token(
7261            start_token.as_ref(),
7262            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
7263        ));
7264        self.prediction_diagnostics.push(diagnostic_for_token(
7265            stop_token.as_ref(),
7266            format!(
7267                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
7268            ),
7269        ));
7270    }
7271
7272    /// Formats the tokens expected from an ATN state using ANTLR display names.
7273    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
7274        expected_symbols_display(
7275            &state_expected_symbols(atn, state_number),
7276            self.vocabulary(),
7277        )
7278    }
7279
7280    /// Formats a buffered token in ANTLR's diagnostic token display form.
7281    pub fn token_display_at(&mut self, index: usize) -> Option<String> {
7282        self.token_at(index).map(|token| format!("{token}"))
7283    }
7284
7285    /// Converts a recognized internal node into a public parse-tree node.
7286    fn recognized_node_tree(
7287        &mut self,
7288        node: &RecognizedNode,
7289        track_alt_numbers: bool,
7290    ) -> Result<ParseTree, AntlrError> {
7291        match node {
7292            RecognizedNode::Token { index } => {
7293                let token = self
7294                    .input
7295                    .get_ref(*index)
7296                    .ok_or_else(|| AntlrError::ParserError {
7297                        line: 0,
7298                        column: 0,
7299                        message: format!("missing token at index {index}"),
7300                    })?;
7301                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
7302            }
7303            RecognizedNode::ErrorToken { index } => {
7304                let token = self
7305                    .input
7306                    .get_ref(*index)
7307                    .ok_or_else(|| AntlrError::ParserError {
7308                        line: 0,
7309                        column: 0,
7310                        message: format!("missing error token at index {index}"),
7311                    })?;
7312                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
7313            }
7314            RecognizedNode::MissingToken {
7315                token_type,
7316                at_index,
7317                text,
7318            } => {
7319                let current = self.token_at(*at_index);
7320                let token = CommonToken::new(*token_type)
7321                    .with_text(text.as_str())
7322                    .with_span(usize::MAX, usize::MAX)
7323                    .with_position(
7324                        current.as_ref().map(Token::line).unwrap_or_default(),
7325                        current.as_ref().map(Token::column).unwrap_or_default(),
7326                    );
7327                Ok(ParseTree::Error(ErrorNode::new(token)))
7328            }
7329            RecognizedNode::Rule {
7330                rule_index,
7331                invoking_state,
7332                alt_number,
7333                start_index,
7334                stop_index,
7335                return_values,
7336                children,
7337            } => {
7338                let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
7339                if track_alt_numbers {
7340                    context.set_alt_number(*alt_number);
7341                }
7342                for (name, value) in return_values {
7343                    context.set_int_return(name.clone(), *value);
7344                }
7345                if let Some(token) = self.token_ref_at(*start_index) {
7346                    context.set_start_ref(token);
7347                }
7348                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
7349                    context.set_stop_ref(token);
7350                }
7351                for child in children {
7352                    context.add_child(self.recognized_node_tree(child, track_alt_numbers)?);
7353                }
7354                Ok(self.rule_node(context))
7355            }
7356            RecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
7357                format!("unfolded left-recursive boundary for rule {rule_index}"),
7358            )),
7359        }
7360    }
7361}
7362
7363impl<S> DirectAdaptiveParser<'_, '_, S>
7364where
7365    S: TokenSource,
7366{
7367    fn parse_rule(
7368        &mut self,
7369        rule_index: usize,
7370        invoking_state: isize,
7371        precedence: i32,
7372    ) -> DirectAdaptiveParseResult<ParseTree> {
7373        let start_state = *self.atn.rule_to_start_state().get(rule_index).ok_or(
7374            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
7375        )?;
7376        let stop_state = *self
7377            .atn
7378            .rule_to_stop_state()
7379            .get(rule_index)
7380            .filter(|state| **state != usize::MAX)
7381            .ok_or(DirectAdaptiveParseControl::Fallback(
7382                DirectAdaptiveFallback::MissingAtn,
7383            ))?;
7384        let start_index = self.parser.current_visible_index();
7385        let mut children = Vec::new();
7386        let mut state_number = start_state;
7387        let mut consumed_eof = false;
7388        while state_number != stop_state {
7389            self.step()?;
7390            let (transition, boundary) = self.next_transition(state_number, precedence)?;
7391            if boundary.is_some() {
7392                return Err(DirectAdaptiveParseControl::Fallback(
7393                    DirectAdaptiveFallback::LeftRecursiveBoundary,
7394                ));
7395            }
7396            match transition {
7397                Transition::Epsilon { target } => {
7398                    state_number = target;
7399                }
7400                Transition::Precedence {
7401                    target,
7402                    precedence: transition_precedence,
7403                } => {
7404                    if transition_precedence < precedence {
7405                        return Err(DirectAdaptiveParseControl::Fallback(
7406                            DirectAdaptiveFallback::Precedence,
7407                        ));
7408                    }
7409                    state_number = target;
7410                }
7411                Transition::Rule {
7412                    rule_index,
7413                    follow_state,
7414                    precedence: rule_precedence,
7415                    ..
7416                } => {
7417                    let child = self.parse_rule(
7418                        rule_index,
7419                        invoking_state_number(state_number),
7420                        rule_precedence,
7421                    )?;
7422                    if self.parser.build_parse_trees {
7423                        children.push(child);
7424                    }
7425                    state_number = follow_state;
7426                }
7427                Transition::Atom { .. }
7428                | Transition::Range { .. }
7429                | Transition::Set { .. }
7430                | Transition::NotSet { .. }
7431                | Transition::Wildcard { .. } => {
7432                    let (matched_eof, child) = self.consume_transition(&transition)?;
7433                    consumed_eof |= matched_eof;
7434                    if let Some(child) = child {
7435                        children.push(child);
7436                    }
7437                    state_number = transition.target();
7438                }
7439                Transition::Predicate { .. } => {
7440                    return Err(DirectAdaptiveParseControl::Fallback(
7441                        DirectAdaptiveFallback::Predicate,
7442                    ));
7443                }
7444                Transition::Action { .. } => {
7445                    return Err(DirectAdaptiveParseControl::Fallback(
7446                        DirectAdaptiveFallback::Action,
7447                    ));
7448                }
7449            }
7450        }
7451
7452        let mut context = ParserRuleContext::with_child_capacity(
7453            rule_index,
7454            invoking_state,
7455            if self.parser.build_parse_trees {
7456                children.len()
7457            } else {
7458                0
7459            },
7460        );
7461        if let Some(token) = self.parser.token_ref_at(start_index) {
7462            context.set_start_ref(token);
7463        }
7464        let stop_index = self
7465            .parser
7466            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
7467        if let Some(token) = stop_index.and_then(|index| self.parser.token_ref_at(index)) {
7468            context.set_stop_ref(token);
7469        }
7470        if self.parser.build_parse_trees {
7471            for child in children {
7472                context.add_child(child);
7473            }
7474        }
7475        Ok(self.parser.rule_node(context))
7476    }
7477
7478    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
7479        self.steps += 1;
7480        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
7481            return Err(DirectAdaptiveParseControl::Fallback(
7482                DirectAdaptiveFallback::StepLimit,
7483            ));
7484        }
7485        Ok(())
7486    }
7487
7488    fn next_transition(
7489        &mut self,
7490        state_number: usize,
7491        precedence: i32,
7492    ) -> DirectAdaptiveParseResult<(Transition, Option<usize>)> {
7493        let state = self
7494            .atn
7495            .state(state_number)
7496            .ok_or(DirectAdaptiveParseControl::Fallback(
7497                DirectAdaptiveFallback::MissingAtn,
7498            ))?;
7499        if state.is_rule_stop() {
7500            return Err(DirectAdaptiveParseControl::Fallback(
7501                DirectAdaptiveFallback::RuleStop,
7502            ));
7503        }
7504        let transition_index =
7505            self.transition_index(state_number, state.transitions.len(), precedence)?;
7506        let transition = state.transitions.get(transition_index).cloned().ok_or(
7507            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
7508        )?;
7509        let boundary = match &transition {
7510            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
7511                left_recursive_boundary(self.atn, state, *target)
7512            }
7513            _ => None,
7514        };
7515        Ok((transition, boundary))
7516    }
7517
7518    fn transition_index(
7519        &mut self,
7520        state_number: usize,
7521        transition_count: usize,
7522        precedence: i32,
7523    ) -> DirectAdaptiveParseResult<usize> {
7524        match transition_count {
7525            0 => Err(DirectAdaptiveParseControl::Fallback(
7526                DirectAdaptiveFallback::NoTransition,
7527            )),
7528            1 => Ok(0),
7529            _ => {
7530                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
7531                    return Ok(alt);
7532                }
7533                let decision = self
7534                    .decision_by_state
7535                    .get(state_number)
7536                    .and_then(|decision| *decision)
7537                    .ok_or(DirectAdaptiveParseControl::Fallback(
7538                        DirectAdaptiveFallback::UnknownDecision,
7539                    ))?;
7540                let prediction = self
7541                    .simulator
7542                    .adaptive_predict_stream_info_with_precedence(
7543                        decision,
7544                        direct_precedence(precedence),
7545                        &mut self.parser.input,
7546                    )
7547                    .map_err(|_| {
7548                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
7549                    })?;
7550                if prediction.has_semantic_context {
7551                    return Err(DirectAdaptiveParseControl::Fallback(
7552                        DirectAdaptiveFallback::SemanticContext,
7553                    ));
7554                }
7555                prediction
7556                    .alt
7557                    .checked_sub(1)
7558                    .filter(|index| *index < transition_count)
7559                    .ok_or(DirectAdaptiveParseControl::Fallback(
7560                        DirectAdaptiveFallback::InvalidAlt,
7561                    ))
7562            }
7563        }
7564    }
7565
7566    fn ll1_transition_index(
7567        &mut self,
7568        state_number: usize,
7569        transition_count: usize,
7570    ) -> DirectAdaptiveParseResult<Option<usize>> {
7571        let state = self
7572            .atn
7573            .state(state_number)
7574            .ok_or(DirectAdaptiveParseControl::Fallback(
7575                DirectAdaptiveFallback::MissingAtn,
7576            ))?;
7577        if state.precedence_rule_decision {
7578            return Ok(None);
7579        }
7580        let Some(rule_stop) = state
7581            .rule_index
7582            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index).copied())
7583        else {
7584            return Ok(None);
7585        };
7586        let symbol = self.parser.input.la_token(1);
7587        let entry = self
7588            .parser
7589            .cached_decision_lookahead(self.atn, state, rule_stop);
7590        Ok(ll1_greedy_alt(&entry, symbol, state.non_greedy).filter(|alt| *alt < transition_count))
7591    }
7592
7593    fn consume_transition(
7594        &mut self,
7595        transition: &Transition,
7596    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
7597        let symbol = self.parser.input.la_token(1);
7598        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
7599            return Err(DirectAdaptiveParseControl::Fallback(
7600                DirectAdaptiveFallback::TokenMismatch,
7601            ));
7602        }
7603        let token = self
7604            .parser
7605            .input
7606            .lt_ref(1)
7607            .ok_or(DirectAdaptiveParseControl::Fallback(
7608                DirectAdaptiveFallback::TokenMismatch,
7609            ))?;
7610        let matched_eof = symbol == TOKEN_EOF;
7611        if !matched_eof {
7612            self.parser.consume();
7613        }
7614        let child = self
7615            .parser
7616            .build_parse_trees
7617            .then(|| ParseTree::Terminal(TerminalNode::from_ref(token)));
7618        Ok((matched_eof, child))
7619    }
7620}
7621
7622/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
7623/// transformed left-recursive rule.
7624fn left_recursive_boundary(atn: &Atn, state: &AtnState, target: usize) -> Option<usize> {
7625    if !state.precedence_rule_decision {
7626        return None;
7627    }
7628    let target_state = atn.state(target)?;
7629    if target_state.kind == AtnStateKind::LoopEnd {
7630        return None;
7631    }
7632    state.rule_index
7633}
7634
7635/// Selects the first outer alternative observed for a rule path.
7636///
7637/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
7638/// outer decision. The metadata recognizer only needs this when a generated
7639/// grammar opts into that target template; otherwise the value remains `0` and
7640/// parse-tree rendering is unchanged.
7641const fn next_alt_number(
7642    state: &AtnState,
7643    transition_index: usize,
7644    current_alt_number: usize,
7645    track_alt_numbers: bool,
7646) -> usize {
7647    if !track_alt_numbers || current_alt_number != 0 || state.transitions.len() <= 1 {
7648        return current_alt_number;
7649    }
7650    if matches!(
7651        state.kind,
7652        AtnStateKind::Basic
7653            | AtnStateKind::BlockStart
7654            | AtnStateKind::PlusBlockStart
7655            | AtnStateKind::StarBlockStart
7656            | AtnStateKind::StarLoopEntry
7657    ) && !state.precedence_rule_decision
7658    {
7659        return transition_index + 1;
7660    }
7661    current_alt_number
7662}
7663
7664/// Folds boundary markers emitted at precedence-loop entries into nested rule
7665/// nodes, matching ANTLR's recursive-context parse-tree shape.
7666fn fold_left_recursive_boundaries(nodes: Vec<RecognizedNode>) -> Vec<RecognizedNode> {
7667    let mut folded = Vec::new();
7668    for node in nodes {
7669        match node {
7670            RecognizedNode::LeftRecursiveBoundary { rule_index } => {
7671                if !folded.is_empty() {
7672                    let children = std::mem::take(&mut folded);
7673                    let start_index = recognized_nodes_start_index(&children).unwrap_or_default();
7674                    let stop_index = recognized_nodes_stop_index(&children);
7675                    folded.push(RecognizedNode::Rule {
7676                        rule_index,
7677                        invoking_state: -1,
7678                        alt_number: 0,
7679                        start_index,
7680                        stop_index,
7681                        return_values: BTreeMap::new(),
7682                        children,
7683                    });
7684                }
7685            }
7686            node => folded.push(node),
7687        }
7688    }
7689    folded
7690}
7691
7692/// Mirrors [`fold_left_recursive_boundaries`] for [`FastRecognizedNode`].
7693fn fold_fast_left_recursive_boundaries(
7694    nodes: Vec<Rc<FastRecognizedNode>>,
7695) -> Vec<Rc<FastRecognizedNode>> {
7696    // Most rule invocations have no left-recursive boundaries, so skip the
7697    // fold work entirely when none are present. The boundary marker is only
7698    // emitted at precedence-rule loop entries, which are rare relative to
7699    // every rule call the recognizer fans out.
7700    if !nodes.iter().any(|node| {
7701        matches!(
7702            node.as_ref(),
7703            FastRecognizedNode::LeftRecursiveBoundary { .. }
7704        )
7705    }) {
7706        return nodes;
7707    }
7708    let mut folded: Vec<Rc<FastRecognizedNode>> = Vec::with_capacity(nodes.len());
7709    for node in nodes {
7710        match node.as_ref() {
7711            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
7712                if !folded.is_empty() {
7713                    let children = std::mem::take(&mut folded);
7714                    let start_index =
7715                        fast_recognized_nodes_start_index(&children).unwrap_or_default();
7716                    let stop_index = fast_recognized_nodes_stop_index(&children);
7717                    folded.push(Rc::new(FastRecognizedNode::Rule {
7718                        rule_index: *rule_index,
7719                        invoking_state: -1,
7720                        start_index,
7721                        stop_index,
7722                        children: NodeList::from_vec(children),
7723                    }));
7724                }
7725            }
7726            _ => folded.push(node),
7727        }
7728    }
7729    folded
7730}
7731
7732fn fast_node_has_left_recursive_boundary(node: &FastRecognizedNode) -> bool {
7733    match node {
7734        FastRecognizedNode::LeftRecursiveBoundary { .. } => true,
7735        FastRecognizedNode::Rule { children, .. } => children.has_left_recursive_boundary(),
7736        FastRecognizedNode::Token { .. }
7737        | FastRecognizedNode::ErrorToken { .. }
7738        | FastRecognizedNode::MissingToken { .. } => false,
7739    }
7740}
7741
7742fn fast_recognized_nodes_start_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
7743    nodes
7744        .iter()
7745        .find_map(|node| fast_recognized_node_start_index(node.as_ref()))
7746}
7747
7748const fn fast_recognized_node_start_index(node: &FastRecognizedNode) -> Option<usize> {
7749    match node {
7750        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
7751            Some(*index)
7752        }
7753        FastRecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
7754        FastRecognizedNode::Rule { start_index, .. } => Some(*start_index),
7755        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
7756    }
7757}
7758
7759const fn fast_recognized_node_span(node: &FastRecognizedNode) -> Option<(usize, Option<usize>)> {
7760    match node {
7761        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
7762            Some((*index, Some(*index)))
7763        }
7764        FastRecognizedNode::MissingToken { at_index, .. } => Some((*at_index, None)),
7765        FastRecognizedNode::Rule {
7766            start_index,
7767            stop_index,
7768            ..
7769        } => Some((*start_index, *stop_index)),
7770        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
7771    }
7772}
7773
7774fn fast_recognized_nodes_stop_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
7775    nodes
7776        .iter()
7777        .rev()
7778        .find_map(|node| fast_recognized_node_stop_index(node.as_ref()))
7779}
7780
7781const fn fast_recognized_node_stop_index(node: &FastRecognizedNode) -> Option<usize> {
7782    match node {
7783        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
7784            Some(*index)
7785        }
7786        FastRecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
7787        FastRecognizedNode::Rule { stop_index, .. } => *stop_index,
7788        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
7789    }
7790}
7791
7792fn recognized_nodes_start_index(nodes: &[RecognizedNode]) -> Option<usize> {
7793    nodes.iter().find_map(recognized_node_start_index)
7794}
7795
7796const fn recognized_node_start_index(node: &RecognizedNode) -> Option<usize> {
7797    match node {
7798        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
7799        RecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
7800        RecognizedNode::Rule { start_index, .. } => Some(*start_index),
7801        RecognizedNode::LeftRecursiveBoundary { .. } => None,
7802    }
7803}
7804
7805fn recognized_nodes_stop_index(nodes: &[RecognizedNode]) -> Option<usize> {
7806    nodes.iter().rev().find_map(recognized_node_stop_index)
7807}
7808
7809/// Converts an ATN state number into the signed invoking-state slot used by
7810/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
7811fn invoking_state_number(state_number: usize) -> isize {
7812    isize::try_from(state_number).unwrap_or(isize::MAX)
7813}
7814
7815fn direct_precedence(precedence: i32) -> usize {
7816    usize::try_from(precedence.max(0)).unwrap_or_default()
7817}
7818
7819const fn recognized_node_stop_index(node: &RecognizedNode) -> Option<usize> {
7820    match node {
7821        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
7822        RecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
7823        RecognizedNode::Rule { stop_index, .. } => *stop_index,
7824        RecognizedNode::LeftRecursiveBoundary { .. } => None,
7825    }
7826}
7827
7828fn token_input_display(token: &impl Token) -> String {
7829    format!("'{}'", token.text().unwrap_or("<EOF>"))
7830}
7831
7832fn display_input_text(text: &str) -> String {
7833    let mut out = String::new();
7834    for ch in text.chars() {
7835        match ch {
7836            '\n' => out.push_str("\\n"),
7837            '\r' => out.push_str("\\r"),
7838            '\t' => out.push_str("\\t"),
7839            other => out.push(other),
7840        }
7841    }
7842    out
7843}
7844
7845fn diagnostic_for_token(token: Option<&impl Token>, message: String) -> ParserDiagnostic {
7846    ParserDiagnostic {
7847        line: token.map(Token::line).unwrap_or_default(),
7848        column: token.map(Token::column).unwrap_or_default(),
7849        message,
7850    }
7851}
7852
7853/// Emits parser diagnostics for the selected recovered parse path.
7854#[allow(clippy::print_stderr)]
7855fn report_parser_diagnostics(diagnostics: &[ParserDiagnostic]) {
7856    for diagnostic in diagnostics {
7857        eprintln!(
7858            "line {}:{} {}",
7859            diagnostic.line, diagnostic.column, diagnostic.message
7860        );
7861    }
7862}
7863
7864/// Emits generated parser diagnostics and lexer diagnostics in the same
7865/// source-position order as ANTLR's lazy token stream reports them.
7866#[allow(clippy::print_stderr)]
7867fn report_generated_diagnostics(
7868    parser_diagnostics: &[ParserDiagnostic],
7869    token_errors: &[TokenSourceError],
7870) {
7871    #[derive(Clone, Copy)]
7872    enum DiagnosticSource {
7873        Token(usize),
7874        Parser(usize),
7875    }
7876
7877    let mut ordered = Vec::with_capacity(parser_diagnostics.len() + token_errors.len());
7878    ordered.extend(token_errors.iter().enumerate().map(|(index, error)| {
7879        (
7880            error.line,
7881            error.column,
7882            0_usize,
7883            index,
7884            DiagnosticSource::Token(index),
7885        )
7886    }));
7887    ordered.extend(
7888        parser_diagnostics
7889            .iter()
7890            .enumerate()
7891            .map(|(index, diagnostic)| {
7892                (
7893                    diagnostic.line,
7894                    diagnostic.column,
7895                    1_usize,
7896                    index,
7897                    DiagnosticSource::Parser(index),
7898                )
7899            }),
7900    );
7901    ordered.sort_by_key(|(line, column, source_order, index, _)| {
7902        (*line, *column, *source_order, *index)
7903    });
7904
7905    for (_, _, _, _, source) in ordered {
7906        match source {
7907            DiagnosticSource::Token(index) => {
7908                let error = &token_errors[index];
7909                eprintln!("line {}:{} {}", error.line, error.column, error.message);
7910            }
7911            DiagnosticSource::Parser(index) => {
7912                let diagnostic = &parser_diagnostics[index];
7913                eprintln!(
7914                    "line {}:{} {}",
7915                    diagnostic.line, diagnostic.column, diagnostic.message
7916                );
7917            }
7918        }
7919    }
7920}
7921
7922/// Emits buffered token-source diagnostics after parser diagnostics that were
7923/// discovered while speculatively reading the same token stream.
7924#[allow(clippy::print_stderr)]
7925fn report_token_source_errors(errors: &[TokenSourceError]) {
7926    for error in errors {
7927        eprintln!("line {}:{} {}", error.line, error.column, error.message);
7928    }
7929}
7930
7931fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
7932    let items = symbols
7933        .iter()
7934        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
7935        .collect::<Vec<_>>();
7936    if let [single] = items.as_slice() {
7937        return single.clone();
7938    }
7939    format!("{{{}}}", items.join(", "))
7940}
7941
7942fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
7943    if symbol == TOKEN_EOF {
7944        return "<EOF>".to_owned();
7945    }
7946    vocabulary.display_name(symbol)
7947}
7948
7949fn is_caller_follow_boundary_text(text: &str) -> bool {
7950    text.chars().any(|ch| ch == ';' || ch == '\n')
7951        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
7952}
7953
7954fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
7955    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
7956}
7957
7958/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
7959/// Inline insertion in those precedence loops can synthesize a missing operand
7960/// before an operator and then block the legitimate loop-exit path.
7961fn state_is_left_recursive_rule(atn: &Atn, state: &AtnState) -> bool {
7962    let Some(rule_index) = state.rule_index else {
7963        return false;
7964    };
7965    atn.rule_to_start_state()
7966        .get(rule_index)
7967        .and_then(|state_number| atn.state(*state_number))
7968        .is_some_and(|rule_start| rule_start.left_recursive_rule)
7969}
7970
7971/// Picks the better of two `parse_atn_rule` passes (with and without the
7972/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
7973/// recovered one; among recovered outcomes the second pass is preferred
7974/// because the no-prefilter walk reaches ANTLR-style recovery inside child
7975/// rules. If both passes failed, the second pass's expected-token snapshot
7976/// is returned so the caller renders the same diagnostic ANTLR would.
7977fn select_better_top_outcome(
7978    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
7979    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
7980) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
7981    match (first, second) {
7982        (Ok(first), Ok(second)) => {
7983            if first.0.diagnostics.is_empty() {
7984                Ok(first)
7985            } else {
7986                Ok(second)
7987            }
7988        }
7989        (Ok(first), Err(_)) => Ok(first),
7990        (Err(_), Ok(second)) => Ok(second),
7991        (Err(_), Err(second_expected)) => Err(second_expected),
7992    }
7993}
7994
7995/// Chooses the outermost parse result that consumed the most input.
7996///
7997/// The recognizer intentionally keeps shorter endpoints available while walking
7998/// nested rule transitions so callers can satisfy following tokens such as
7999/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
8000fn select_best_fast_outcome(
8001    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
8002    prediction_mode: PredictionMode,
8003    caller_follow: Option<&TokenBitSet>,
8004    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
8005) -> Option<FastRecognizeOutcome> {
8006    let mut best = None;
8007    let mut best_caller_follow = None;
8008    for outcome in outcomes {
8009        if matches!(
8010            prediction_mode,
8011            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
8012        ) && outcome.diagnostics.is_empty()
8013            && let Some(follow) = caller_follow
8014        {
8015            let (token_type, is_boundary, _) = token_info_at(outcome.index);
8016            if is_boundary && follow.contains(token_type) {
8017                let replace =
8018                    best_caller_follow
8019                        .as_ref()
8020                        .is_none_or(|existing: &FastRecognizeOutcome| {
8021                            (outcome.index, outcome.consumed_eof)
8022                                < (existing.index, existing.consumed_eof)
8023                        });
8024                if replace {
8025                    best_caller_follow = Some(outcome.clone());
8026                }
8027            }
8028        }
8029        let Some(existing) = best else {
8030            best = Some(outcome);
8031            continue;
8032        };
8033        let outcome_position = (outcome.index, outcome.consumed_eof);
8034        let best_position = (existing.index, existing.consumed_eof);
8035        let better = match prediction_mode {
8036            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
8037                outcome_position,
8038                &outcome.diagnostics,
8039                best_position,
8040                &existing.diagnostics,
8041            ),
8042            PredictionMode::Sll => outcome.index > existing.index,
8043        };
8044        best = Some(if better { outcome } else { existing });
8045    }
8046    let should_use_caller_follow =
8047        best_caller_follow
8048            .as_ref()
8049            .zip(best.as_ref())
8050            .is_some_and(|(candidate, selected)| {
8051                if !selected.diagnostics.is_empty() {
8052                    return true;
8053                }
8054                candidate.index < selected.index
8055                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
8056            });
8057    if should_use_caller_follow {
8058        best_caller_follow
8059    } else {
8060        best
8061    }
8062}
8063
8064fn select_best_outcome(
8065    outcomes: impl Iterator<Item = RecognizeOutcome>,
8066    prediction_mode: PredictionMode,
8067) -> Option<RecognizeOutcome> {
8068    let outcomes = outcomes.collect::<Vec<_>>();
8069    let prefer_first_tie = outcomes
8070        .iter()
8071        .any(|outcome| nodes_need_stable_tie(&outcome.nodes));
8072    outcomes.into_iter().reduce(|best, outcome| {
8073        let outcome_position = (outcome.index, outcome.consumed_eof);
8074        let best_position = (best.index, best.consumed_eof);
8075        let better = match prediction_mode {
8076            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
8077                outcome_is_better(
8078                    outcome_position,
8079                    &outcome.diagnostics,
8080                    best_position,
8081                    &best.diagnostics,
8082                ) || (!prefer_first_tie
8083                    && outcome_position == best_position
8084                    && outcome.diagnostics.len() == best.diagnostics.len()
8085                    && diagnostic_recovery_rank(&outcome.diagnostics)
8086                        == diagnostic_recovery_rank(&best.diagnostics)
8087                    && (outcome.decisions < best.decisions
8088                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
8089            }
8090            PredictionMode::Sll => {
8091                outcome_position > best_position
8092                    || (outcome_position == best_position
8093                        && !prefer_first_tie
8094                        && (outcome.decisions < best.decisions
8095                            || (outcome.decisions == best.decisions
8096                                && outcome_is_better(
8097                                    outcome_position,
8098                                    &outcome.diagnostics,
8099                                    best_position,
8100                                    &best.diagnostics,
8101                                ))))
8102            }
8103        };
8104        if better {
8105            return outcome;
8106        }
8107        best
8108    })
8109}
8110
8111/// Records the serialized transition order at parser decision states.
8112///
8113/// When two clean paths consume the same input, ANTLR's adaptive prediction
8114/// chooses by alternative order. Keeping this compact trace lets the metadata
8115/// recognizer distinguish greedy and non-greedy optional blocks without a full
8116/// prediction simulator.
8117fn transition_decision(
8118    atn: &Atn,
8119    state: &AtnState,
8120    transition_index: usize,
8121    predicates: &[(usize, usize, ParserPredicate)],
8122) -> Option<usize> {
8123    if state.transitions.len() <= 1
8124        || state.precedence_rule_decision
8125        || decision_reaches_unsupported_predicate(atn, state, predicates)
8126    {
8127        return None;
8128    }
8129    Some(transition_index)
8130}
8131
8132/// Reports whether a state should reset the active no-viable decision start.
8133///
8134/// Loop entry/back states are continuations of the surrounding adaptive
8135/// prediction; resetting at those states would turn LL-star failures back into
8136/// ordinary mismatches.
8137const fn starts_prediction_decision(state: &AtnState) -> bool {
8138    state.transitions.len() > 1
8139        && !matches!(
8140            state.kind,
8141            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
8142        )
8143}
8144
8145/// Marks a farthest expected-token set as no-viable when multiple alternatives
8146/// failed after the active decision had already consumed input.
8147fn record_no_viable_if_ambiguous(
8148    expected: &mut ExpectedTokens,
8149    decision_start_index: Option<usize>,
8150    index: usize,
8151) {
8152    if expected.index == Some(index) && expected.symbols.len() > 1 {
8153        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
8154            expected.record_no_viable(decision_start, index);
8155        }
8156    }
8157}
8158
8159/// Records a no-viable decision caused by a failed semantic predicate before
8160/// any consuming transition can contribute an expected-token set.
8161const fn record_predicate_no_viable(
8162    expected: &mut ExpectedTokens,
8163    decision_start_index: Option<usize>,
8164    index: usize,
8165) {
8166    if let Some(decision_start) = decision_start_index {
8167        expected.record_no_viable(decision_start, index);
8168    }
8169}
8170
8171/// Returns the active decision start only when the error is past that start.
8172const fn no_viable_decision_start(
8173    decision_start_index: Option<usize>,
8174    index: usize,
8175) -> Option<usize> {
8176    match decision_start_index {
8177        Some(start) if index > start => Some(start),
8178        _ => None,
8179    }
8180}
8181
8182/// Restores expected-token bookkeeping when a child rule found a clean
8183/// consuming path; failures in longer child alternatives should not pollute the
8184/// caller's final expectation set.
8185fn restore_expected(
8186    children: &[RecognizeOutcome],
8187    child_start_index: usize,
8188    expected: &mut ExpectedTokens,
8189    snapshot: ExpectedTokens,
8190    preserve_child_expected: bool,
8191) {
8192    if preserve_child_expected {
8193        return;
8194    }
8195    if children
8196        .iter()
8197        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
8198    {
8199        *expected = snapshot;
8200    }
8201}
8202
8203/// Reports whether a decision can reach a predicate the generator did not
8204/// translate. Static alternative order is unsafe for those context predicates.
8205fn decision_reaches_unsupported_predicate(
8206    atn: &Atn,
8207    state: &AtnState,
8208    predicates: &[(usize, usize, ParserPredicate)],
8209) -> bool {
8210    state.transitions.iter().any(|transition| {
8211        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
8212    })
8213}
8214
8215/// Walks epsilon-like edges from one transition to find unsupported predicates.
8216fn transition_reaches_unsupported_predicate(
8217    atn: &Atn,
8218    transition: &Transition,
8219    predicates: &[(usize, usize, ParserPredicate)],
8220    visited: &mut BTreeSet<usize>,
8221) -> bool {
8222    match transition {
8223        Transition::Predicate {
8224            rule_index,
8225            pred_index,
8226            ..
8227        } => !predicates
8228            .iter()
8229            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
8230        Transition::Epsilon { target }
8231        | Transition::Action { target, .. }
8232        | Transition::Rule { target, .. } => {
8233            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
8234        }
8235        Transition::Precedence { .. }
8236        | Transition::Atom { .. }
8237        | Transition::Range { .. }
8238        | Transition::Set { .. }
8239        | Transition::NotSet { .. }
8240        | Transition::Wildcard { .. } => false,
8241    }
8242}
8243
8244/// Finds an unsupported predicate reachable before a consuming transition.
8245fn state_reaches_unsupported_predicate(
8246    atn: &Atn,
8247    state_number: usize,
8248    predicates: &[(usize, usize, ParserPredicate)],
8249    visited: &mut BTreeSet<usize>,
8250) -> bool {
8251    if !visited.insert(state_number) {
8252        return false;
8253    }
8254    let Some(state) = atn.state(state_number) else {
8255        return false;
8256    };
8257    state.transitions.iter().any(|transition| {
8258        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
8259    })
8260}
8261
8262/// Adds a decision step to the front of an already-recognized suffix path.
8263fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
8264    if let Some(decision) = decision {
8265        outcome.decisions.insert(0, decision);
8266    }
8267}
8268
8269fn outcome_is_better(
8270    outcome_position: (usize, bool),
8271    outcome_diagnostics: &[ParserDiagnostic],
8272    best_position: (usize, bool),
8273    best_diagnostics: &[ParserDiagnostic],
8274) -> bool {
8275    outcome_position > best_position
8276        || (outcome_position == best_position
8277            && (outcome_diagnostics.len() < best_diagnostics.len()
8278                || (outcome_diagnostics.len() == best_diagnostics.len()
8279                    && diagnostic_recovery_rank(outcome_diagnostics)
8280                        < diagnostic_recovery_rank(best_diagnostics))))
8281}
8282
8283/// Ranks concrete recovery repairs ahead of generic non-EOF mismatch fallbacks
8284/// when speculative paths otherwise consume the same input.
8285fn diagnostic_recovery_rank(diagnostics: &[ParserDiagnostic]) -> usize {
8286    diagnostics
8287        .iter()
8288        .filter(|diagnostic| {
8289            diagnostic.message.starts_with("mismatched input ")
8290                && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
8291        })
8292        .count()
8293}
8294
8295fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
8296    if outcomes
8297        .iter()
8298        .any(|outcome| outcome.diagnostics.is_empty())
8299    {
8300        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
8301    }
8302}
8303
8304fn discard_recovered_outcomes_if_clean_path_exists(outcomes: &mut Vec<RecognizeOutcome>) {
8305    if outcomes.iter().any(outcome_has_rule_failure_diagnostic) {
8306        return;
8307    }
8308    if outcomes
8309        .iter()
8310        .any(|outcome| outcome.diagnostics.is_empty())
8311    {
8312        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
8313    }
8314}
8315
8316/// Reports whether a recovered outcome came from an explicit predicate
8317/// fail-option and therefore should compete with shorter clean loop exits.
8318fn outcome_has_rule_failure_diagnostic(outcome: &RecognizeOutcome) -> bool {
8319    outcome
8320        .diagnostics
8321        .iter()
8322        .any(|diagnostic| diagnostic.message.starts_with("rule "))
8323}
8324
8325/// Reports whether a candidate contains recursive tree structure where ANTLR's
8326/// first viable candidate preserves the correct left-recursive context shape.
8327fn nodes_need_stable_tie(nodes: &[RecognizedNode]) -> bool {
8328    nodes.iter().any(node_needs_stable_tie)
8329}
8330
8331fn node_needs_stable_tie(node: &RecognizedNode) -> bool {
8332    match node {
8333        RecognizedNode::Token { .. }
8334        | RecognizedNode::ErrorToken { .. }
8335        | RecognizedNode::MissingToken { .. } => false,
8336        RecognizedNode::LeftRecursiveBoundary { .. } => true,
8337        RecognizedNode::Rule {
8338            rule_index,
8339            children,
8340            ..
8341        } => children.iter().any(|child| {
8342            matches!(
8343                child,
8344                RecognizedNode::Rule {
8345                    rule_index: child_rule,
8346                    ..
8347                } if child_rule == rule_index
8348            ) || node_needs_stable_tie(child)
8349        }),
8350    }
8351}
8352
8353/// Removes equivalent endpoints before memoizing a state result while
8354/// preserving ATN transition-discovery order.
8355///
8356/// Outcomes are compared on observable recognition state — the input index,
8357/// EOF consumption, and diagnostics — without descending into the parse-tree
8358/// fragment carried by `nodes`. Two paths reaching the same point with
8359/// different node trees would otherwise prevent memoization from collapsing
8360/// equivalent suffixes and explode the speculative-path cache.
8361///
8362/// The first occurrence per recognition key wins, which matches ANTLR's
8363/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
8364/// transitions before loop-exit, so the first-discovered outcome carries the
8365/// greedy parse-tree fragment.
8366fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
8367    if outcomes.len() < 2 {
8368        return;
8369    }
8370    let mut keep = Vec::with_capacity(outcomes.len());
8371    let mut seen: BTreeMap<(usize, bool), Vec<usize>> = BTreeMap::new();
8372    'outcomes: for (index, outcome) in outcomes.iter().enumerate() {
8373        let bucket = seen
8374            .entry((outcome.index, outcome.consumed_eof))
8375            .or_default();
8376        for &previous in bucket.iter() {
8377            if outcomes[previous].diagnostics == outcome.diagnostics {
8378                continue 'outcomes;
8379            }
8380        }
8381        bucket.push(index);
8382        keep.push(index);
8383    }
8384    if keep.len() == outcomes.len() {
8385        return;
8386    }
8387    let mut iter = keep.into_iter();
8388    let mut next_keep = iter.next();
8389    let mut current = 0_usize;
8390    outcomes.retain(|_| {
8391        let result = next_keep == Some(current);
8392        if result {
8393            next_keep = iter.next();
8394        }
8395        current += 1;
8396        result
8397    });
8398}
8399
8400fn dedupe_clean_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
8401    if outcomes.len() < 2 {
8402        return;
8403    }
8404    // Most outcomes lists are 2-4 entries; an inline scan beats BTreeSet
8405    // here because BTreeSet's allocation + per-insert balancing dominates
8406    // O(log n) wins on tiny n. Retains the original order so callers that
8407    // depend on alt ordering (e.g. fast outcome selection) stay correct.
8408    //
8409    // Beyond the inline buffer we promote to a heap Vec so all kept entries
8410    // continue to participate in dedup — leaking duplicates here on
8411    // pathological grammars (e.g. ktor's deeply ambiguous Kotlin parse)
8412    // explodes the speculative cache one step up the recursion.
8413    let mut inline_keys: [(usize, bool); 8] = [(0, false); 8];
8414    let mut inline_len = 0_usize;
8415    let mut overflow: Vec<(usize, bool)> = Vec::new();
8416    outcomes.retain(|outcome| {
8417        let key = (outcome.index, outcome.consumed_eof);
8418        for &existing in &inline_keys[..inline_len] {
8419            if existing == key {
8420                return false;
8421            }
8422        }
8423        if !overflow.is_empty() {
8424            for &existing in &overflow {
8425                if existing == key {
8426                    return false;
8427                }
8428            }
8429        }
8430        if inline_len < inline_keys.len() {
8431            inline_keys[inline_len] = key;
8432            inline_len += 1;
8433        } else {
8434            overflow.push(key);
8435        }
8436        true
8437    });
8438}
8439
8440/// Sorts and removes equivalent endpoints, including their action traces.
8441fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>) {
8442    outcomes.sort_unstable();
8443    outcomes.dedup();
8444}
8445
8446impl<S> Recognizer for BaseParser<S>
8447where
8448    S: TokenSource,
8449{
8450    fn data(&self) -> &RecognizerData {
8451        &self.data
8452    }
8453
8454    fn data_mut(&mut self) -> &mut RecognizerData {
8455        &mut self.data
8456    }
8457}
8458
8459impl<S> Parser for BaseParser<S>
8460where
8461    S: TokenSource,
8462{
8463    fn build_parse_trees(&self) -> bool {
8464        self.build_parse_trees
8465    }
8466
8467    fn set_build_parse_trees(&mut self, build: bool) {
8468        self.build_parse_trees = build;
8469    }
8470
8471    fn number_of_syntax_errors(&self) -> usize {
8472        Self::number_of_syntax_errors(self)
8473    }
8474
8475    fn report_diagnostic_errors(&self) -> bool {
8476        self.report_diagnostic_errors
8477    }
8478
8479    fn set_report_diagnostic_errors(&mut self, report: bool) {
8480        self.report_diagnostic_errors = report;
8481    }
8482
8483    fn prediction_mode(&self) -> PredictionMode {
8484        self.prediction_mode
8485    }
8486
8487    fn set_prediction_mode(&mut self, mode: PredictionMode) {
8488        self.prediction_mode = mode;
8489    }
8490}
8491
8492#[cfg(test)]
8493mod tests {
8494    use super::*;
8495    use crate::atn::AtnType;
8496    use crate::atn::IntervalSet;
8497    use crate::atn::parser::{
8498        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
8499    };
8500    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
8501    use crate::token::{CommonToken, HIDDEN_CHANNEL, Token};
8502    use crate::token_stream::CommonTokenStream;
8503    use crate::vocabulary::Vocabulary;
8504
8505    #[test]
8506    fn fx_hasher_write_matches_typed_methods_for_full_words() {
8507        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
8508        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
8509        // fields) must hash the same as the typed methods, otherwise an
8510        // `FxHashMap` keyed on such a type silently disagrees with itself
8511        // depending on which entry point the caller used. Verify the
8512        // little-endian word equivalence this PR established.
8513        let value: u64 = 0x0102_0304_0506_0708;
8514        let mut typed = FxHasher::default();
8515        typed.write_u64(value);
8516        let mut bytewise = FxHasher::default();
8517        bytewise.write(&value.to_le_bytes());
8518        assert_eq!(typed.finish(), bytewise.finish());
8519    }
8520
8521    #[derive(Debug)]
8522    struct Source {
8523        tokens: Vec<CommonToken>,
8524        index: usize,
8525    }
8526
8527    impl TokenSource for Source {
8528        fn next_token(&mut self) -> CommonToken {
8529            let token = self
8530                .tokens
8531                .get(self.index)
8532                .cloned()
8533                .unwrap_or_else(|| CommonToken::eof("parser-test", self.index, 1, self.index));
8534            self.index += 1;
8535            token
8536        }
8537
8538        fn line(&self) -> usize {
8539            1
8540        }
8541
8542        fn column(&self) -> usize {
8543            self.index
8544        }
8545
8546        fn source_name(&self) -> &'static str {
8547            "parser-test"
8548        }
8549    }
8550
8551    fn mini_parser(tokens: Vec<CommonToken>) -> BaseParser<Source> {
8552        let data = RecognizerData::new(
8553            "Mini.g4",
8554            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
8555        );
8556        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
8557    }
8558
8559    fn token_then_eof_atn() -> Atn {
8560        AtnDeserializer::new(&SerializedAtn::from_i32(&[
8561            4, 1, 2, // version, parser, max token type
8562            3, // states
8563            2, 0, // rule start
8564            1, 0, // basic
8565            7, 0, // rule stop
8566            0, // non-greedy states
8567            0, // precedence states
8568            1, // rules
8569            0, // rule 0 start
8570            0, // modes
8571            0, // sets
8572            2, // transitions
8573            0, 1, 5, 1, 0, 0, // match token 1
8574            1, 2, 5, -1, 0, 0, // match EOF
8575            0, // decisions
8576        ]))
8577        .deserialize()
8578        .expect("artificial parser ATN should deserialize")
8579    }
8580
8581    fn eof_then_action_atn() -> Atn {
8582        AtnDeserializer::new(&SerializedAtn::from_i32(&[
8583            4, 1, 1, // version, parser, max token type
8584            3, // states
8585            2, 0, // rule start
8586            1, 0, // basic
8587            7, 0, // rule stop
8588            0, // non-greedy states
8589            0, // precedence states
8590            1, // rules
8591            0, // rule 0 start
8592            0, // modes
8593            0, // sets
8594            2, // transitions
8595            0, 1, 5, -1, 0, 0, // match EOF
8596            1, 2, 6, 0, 0, 0, // parser action
8597            0, // decisions
8598        ]))
8599        .deserialize()
8600        .expect("artificial parser ATN should deserialize")
8601    }
8602
8603    fn two_alt_decision_atn() -> Atn {
8604        let mut atn = Atn::new(AtnType::Parser, 2);
8605        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8606        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
8607        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
8608        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
8609        atn.add_state(AtnState::new(4, AtnStateKind::BlockEnd).with_rule_index(0));
8610        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
8611        atn.set_rule_to_start_state(vec![0]);
8612        atn.set_rule_to_stop_state(vec![5]);
8613        atn.add_decision_state(1);
8614        atn.state_mut(0)
8615            .expect("state 0")
8616            .add_transition(Transition::Epsilon { target: 1 });
8617        atn.state_mut(1)
8618            .expect("state 1")
8619            .add_transition(Transition::Atom {
8620                target: 2,
8621                label: 1,
8622            });
8623        atn.state_mut(1)
8624            .expect("state 1")
8625            .add_transition(Transition::Atom {
8626                target: 3,
8627                label: 2,
8628            });
8629        atn.state_mut(2)
8630            .expect("state 2")
8631            .add_transition(Transition::Epsilon { target: 4 });
8632        atn.state_mut(3)
8633            .expect("state 3")
8634            .add_transition(Transition::Epsilon { target: 4 });
8635        atn.state_mut(4)
8636            .expect("state 4")
8637            .add_transition(Transition::Epsilon { target: 5 });
8638        atn
8639    }
8640
8641    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
8642    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
8643    fn optional_then_b_eof_atn() -> Atn {
8644        let mut atn = Atn::new(AtnType::Parser, 3);
8645        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8646        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
8647        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
8648        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
8649        atn.add_state(AtnState::new(4, AtnStateKind::Basic).with_rule_index(0));
8650        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
8651        atn.set_rule_to_start_state(vec![0]);
8652        atn.set_rule_to_stop_state(vec![5]);
8653        atn.add_decision_state(1);
8654        atn.state_mut(0)
8655            .expect("state 0")
8656            .add_transition(Transition::Epsilon { target: 1 });
8657        // Optional block: match A then fall through, or skip straight to state 3.
8658        atn.state_mut(1)
8659            .expect("state 1")
8660            .add_transition(Transition::Atom {
8661                target: 3,
8662                label: 1,
8663            });
8664        atn.state_mut(1)
8665            .expect("state 1")
8666            .add_transition(Transition::Epsilon { target: 3 });
8667        // Match B, then EOF.
8668        atn.state_mut(3)
8669            .expect("state 3")
8670            .add_transition(Transition::Atom {
8671                target: 4,
8672                label: 2,
8673            });
8674        atn.state_mut(4)
8675            .expect("state 4")
8676            .add_transition(Transition::Atom {
8677                target: 5,
8678                label: TOKEN_EOF,
8679            });
8680        atn
8681    }
8682
8683    #[test]
8684    fn sync_decision_deletes_only_a_single_token() {
8685        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
8686        // expected. `(A)? B EOF` at the optional-block decision:
8687        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
8688        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
8689        //               without consuming and records the expected set for the
8690        //               subsequent mismatch (the parser must not over-consume both
8691        //               `C`s and accept the input).
8692        let atn = optional_then_b_eof_atn();
8693
8694        let mut single = mini_parser(vec![
8695            CommonToken::new(3).with_text("c"),
8696            CommonToken::new(2).with_text("b"),
8697            CommonToken::eof("parser-test", 1, 2, 2),
8698        ]);
8699        single.rule_context_stack = vec![RuleContextFrame {
8700            rule_index: 0,
8701            invoking_state: 0,
8702        }];
8703        let children = single
8704            .sync_decision(&atn, 1, true, false)
8705            .expect("single extraneous token recovers");
8706        assert_eq!(children.len(), 1);
8707        assert!(matches!(children[0], ParseTree::Error(_)));
8708        assert_eq!(single.number_of_syntax_errors(), 1);
8709        // Exactly one token consumed (the cursor now sits on `b`).
8710        assert_eq!(single.la(1), 2);
8711
8712        let mut double = mini_parser(vec![
8713            CommonToken::new(3).with_text("c"),
8714            CommonToken::new(3).with_text("c"),
8715            CommonToken::new(2).with_text("b"),
8716            CommonToken::eof("parser-test", 1, 3, 3),
8717        ]);
8718        double.rule_context_stack = vec![RuleContextFrame {
8719            rule_index: 0,
8720            invoking_state: 0,
8721        }];
8722        let result = double.sync_decision(&atn, 1, true, false);
8723        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
8724        // consume either `c`. It reports the mismatch at the first `c` (so the parser
8725        // does not over-consume both and accept the input). Nothing is consumed, so
8726        // the cursor still sits on the first `c` for rule-level recovery.
8727        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
8728        match error {
8729            AntlrError::ParserError { message, .. } => {
8730                assert!(message.starts_with("mismatched input"), "got: {message}");
8731            }
8732            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
8733        }
8734        assert_eq!(double.la(1), 3);
8735    }
8736
8737    /// The real serialized ATN that `antlr4-rust-gen` emits for
8738    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
8739    /// the loop is `EOF`. The loop decision is state 5.
8740    fn star_loop_then_eof_atn() -> Atn {
8741        AtnDeserializer::new(&SerializedAtn::from_i32(&[
8742            4, 1, 3, 11, 2, 0, 7, 0, 1, 0, 5, 0, 4, 8, 0, 10, 0, 12, 0, 7, 9, 0, 1, 0, 1, 0, 1, 0,
8743            0, 0, 1, 0, 0, 0, 10, 0, 5, 1, 0, 0, 0, 2, 4, 5, 1, 0, 0, 3, 2, 1, 0, 0, 0, 4, 7, 1, 0,
8744            0, 0, 5, 3, 1, 0, 0, 0, 5, 6, 1, 0, 0, 0, 6, 8, 1, 0, 0, 0, 7, 5, 1, 0, 0, 0, 8, 9, 5,
8745            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
8746        ]))
8747        .deserialize()
8748        .expect("star-loop-then-EOF ATN should deserialize")
8749    }
8750
8751    #[test]
8752    fn sync_decision_deletes_token_before_eof_at_loop_back() {
8753        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
8754        // At the loop ENTRY (loop_back = false) a single unexpected token before
8755        // EOF is deleted as an error node (then the generated EOF match consumes
8756        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
8757        // EOF must be a valid scan-stop for this to fire.
8758        let atn = star_loop_then_eof_atn();
8759        let mut parser = mini_parser(vec![
8760            CommonToken::new(2).with_text("c"),
8761            CommonToken::eof("parser-test", 1, 1, 1),
8762        ]);
8763        parser.rule_context_stack = vec![RuleContextFrame {
8764            rule_index: 0,
8765            invoking_state: 0,
8766        }];
8767        let children = parser
8768            .sync_decision(&atn, 5, true, false)
8769            .expect("single token before EOF recovers");
8770        assert_eq!(children.len(), 1);
8771        assert!(matches!(children[0], ParseTree::Error(_)));
8772        assert_eq!(parser.number_of_syntax_errors(), 1);
8773        assert_eq!(
8774            parser.la(1),
8775            TOKEN_EOF,
8776            "EOF is left for the rule's EOF match"
8777        );
8778    }
8779
8780    #[test]
8781    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
8782        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
8783        // single-token deletion, which fails because LA(2) = `c` is not expected —
8784        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
8785        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
8786        let atn = star_loop_then_eof_atn();
8787        let mut parser = mini_parser(vec![
8788            CommonToken::new(2).with_text("c"),
8789            CommonToken::new(2).with_text("c"),
8790            CommonToken::eof("parser-test", 1, 2, 2),
8791        ]);
8792        parser.rule_context_stack = vec![RuleContextFrame {
8793            rule_index: 0,
8794            invoking_state: 0,
8795        }];
8796        let error = parser
8797            .sync_decision(&atn, 5, true, false)
8798            .expect_err("two tokens at the loop entry must not be deleted");
8799        match error {
8800            AntlrError::ParserError { message, .. } => {
8801                assert!(message.starts_with("mismatched input"), "got: {message}");
8802            }
8803            other => panic!("expected mismatched-input ParserError, got {other:?}"),
8804        }
8805        assert_eq!(
8806            parser.la(1),
8807            2,
8808            "nothing consumed; cursor still on first `c`"
8809        );
8810    }
8811
8812    #[test]
8813    fn sync_decision_consumes_until_eof_at_loop_back() {
8814        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
8815        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
8816        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
8817        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
8818        // post-`a` state directly: `c c <EOF>` with loop_back = true.
8819        let atn = star_loop_then_eof_atn();
8820        let mut parser = mini_parser(vec![
8821            CommonToken::new(2).with_text("c"),
8822            CommonToken::new(2).with_text("c"),
8823            CommonToken::eof("parser-test", 1, 2, 2),
8824        ]);
8825        parser.rule_context_stack = vec![RuleContextFrame {
8826            rule_index: 0,
8827            invoking_state: 0,
8828        }];
8829        let children = parser
8830            .sync_decision(&atn, 5, false, true)
8831            .expect("loop-back multi-token deletion recovers onto EOF");
8832        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
8833        assert!(children.iter().all(|c| matches!(c, ParseTree::Error(_))));
8834        assert_eq!(parser.number_of_syntax_errors(), 1);
8835        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
8836    }
8837
8838    fn predicate_after_token_atn() -> Atn {
8839        let mut atn = Atn::new(AtnType::Parser, 2);
8840        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8841        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
8842        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
8843        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
8844        atn.add_state(AtnState::new(4, AtnStateKind::RuleStop).with_rule_index(0));
8845        atn.set_rule_to_start_state(vec![0]);
8846        atn.set_rule_to_stop_state(vec![4]);
8847        atn.state_mut(0)
8848            .expect("state 0")
8849            .add_transition(Transition::Atom {
8850                target: 1,
8851                label: 1,
8852            });
8853        atn.state_mut(1)
8854            .expect("state 1")
8855            .add_transition(Transition::Predicate {
8856                target: 2,
8857                rule_index: 0,
8858                pred_index: 0,
8859                context_dependent: false,
8860            });
8861        atn.state_mut(2)
8862            .expect("state 2")
8863            .add_transition(Transition::Atom {
8864                target: 3,
8865                label: 2,
8866            });
8867        atn.state_mut(3)
8868            .expect("state 3")
8869            .add_transition(Transition::Epsilon { target: 4 });
8870        atn
8871    }
8872
8873    fn nested_nullable_context_atn() -> Atn {
8874        let mut atn = Atn::new(AtnType::Parser, 1);
8875        for state_number in 0..=20 {
8876            let kind = match state_number {
8877                0 | 10 | 16 => AtnStateKind::RuleStart,
8878                9 | 15 | 20 => AtnStateKind::RuleStop,
8879                _ => AtnStateKind::Basic,
8880            };
8881            let rule_index = match state_number {
8882                0..=9 => 0,
8883                10..=15 => 1,
8884                _ => 2,
8885            };
8886            atn.add_state(AtnState::new(state_number, kind).with_rule_index(rule_index));
8887        }
8888        atn.set_rule_to_start_state(vec![0, 10, 16]);
8889        atn.set_rule_to_stop_state(vec![9, 15, 20]);
8890        atn.state_mut(1)
8891            .expect("state 1")
8892            .add_transition(Transition::Rule {
8893                target: 10,
8894                rule_index: 1,
8895                follow_state: 8,
8896                precedence: 0,
8897            });
8898        atn.state_mut(8)
8899            .expect("state 8")
8900            .add_transition(Transition::Atom {
8901                target: 9,
8902                label: 1,
8903            });
8904        atn.state_mut(8)
8905            .expect("state 8")
8906            .add_transition(Transition::Epsilon { target: 9 });
8907        atn.state_mut(2)
8908            .expect("state 2")
8909            .add_transition(Transition::Rule {
8910                target: 16,
8911                rule_index: 2,
8912                follow_state: 14,
8913                precedence: 0,
8914            });
8915        atn.state_mut(14)
8916            .expect("state 14")
8917            .add_transition(Transition::Epsilon { target: 15 });
8918        atn
8919    }
8920
8921    fn generated_match_recovery_atn() -> Atn {
8922        let mut atn = Atn::new(AtnType::Parser, 2);
8923        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8924        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
8925        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
8926        atn.add_state(AtnState::new(3, AtnStateKind::RuleStop).with_rule_index(0));
8927        atn.add_state(AtnState::new(4, AtnStateKind::RuleStart).with_rule_index(1));
8928        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(1));
8929        atn.set_rule_to_start_state(vec![0, 4]);
8930        atn.set_rule_to_stop_state(vec![3, 5]);
8931        atn.state_mut(1)
8932            .expect("state 1")
8933            .add_transition(Transition::Rule {
8934                target: 4,
8935                rule_index: 1,
8936                follow_state: 2,
8937                precedence: 0,
8938            });
8939        atn.state_mut(2)
8940            .expect("state 2")
8941            .add_transition(Transition::Atom {
8942                target: 3,
8943                label: TOKEN_EOF,
8944            });
8945        atn
8946    }
8947
8948    fn complement_set_atn() -> Atn {
8949        let mut atn = Atn::new(AtnType::Parser, 1);
8950        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8951        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
8952        atn.set_rule_to_start_state(vec![0]);
8953        atn.set_rule_to_stop_state(vec![1]);
8954        let mut excluded = IntervalSet::new();
8955        excluded.add(1);
8956        atn.state_mut(0)
8957            .expect("state 0")
8958            .add_transition(Transition::NotSet {
8959                target: 1,
8960                set: excluded,
8961            });
8962        atn
8963    }
8964
8965    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
8966    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
8967    fn wildcard_then_eof_atn() -> Atn {
8968        let mut atn = Atn::new(AtnType::Parser, 1);
8969        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
8970        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
8971        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
8972        atn.set_rule_to_start_state(vec![0]);
8973        atn.set_rule_to_stop_state(vec![1]);
8974        atn.state_mut(0)
8975            .expect("state 0")
8976            .add_transition(Transition::Wildcard { target: 2 });
8977        atn.state_mut(2)
8978            .expect("state 2")
8979            .add_transition(Transition::Atom {
8980                target: 1,
8981                label: TOKEN_EOF,
8982            });
8983        atn
8984    }
8985
8986    #[test]
8987    fn parser_matches_token_and_reports_mismatch() {
8988        let source = Source {
8989            tokens: vec![
8990                CommonToken::new(1).with_text("x"),
8991                CommonToken::eof("parser-test", 1, 1, 1),
8992            ],
8993            index: 0,
8994        };
8995        let data = RecognizerData::new(
8996            "Mini.g4",
8997            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
8998        );
8999        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
9000        assert_eq!(
9001            parser.match_token(1).expect("token 1 should match").text(),
9002            "x"
9003        );
9004        assert!(parser.match_token(1).is_err());
9005    }
9006
9007    #[test]
9008    fn parser_matches_token_sets() {
9009        let mut parser = mini_parser(vec![
9010            CommonToken::new(1).with_text("x"),
9011            CommonToken::eof("parser-test", 1, 1, 1),
9012        ]);
9013
9014        assert_eq!(
9015            parser
9016                .match_set(&[(1, 1), (3, 4)])
9017                .expect("token set should match")
9018                .text(),
9019            "x"
9020        );
9021        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
9022    }
9023
9024    #[test]
9025    fn generated_rule_api_tracks_state_and_precedence() {
9026        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9027
9028        let context = parser.enter_rule(7, 2);
9029        assert_eq!(context.rule_index(), 2);
9030        assert_eq!(parser.state(), 7);
9031        assert_eq!(
9032            parser.rule_context_stack,
9033            vec![RuleContextFrame {
9034                rule_index: 2,
9035                invoking_state: 7
9036            }]
9037        );
9038
9039        let recursive = parser.enter_recursion_rule(11, 3, 4);
9040        assert_eq!(recursive.rule_index(), 3);
9041        assert!(parser.precpred(4));
9042        assert!(parser.precpred(5));
9043        assert!(!parser.precpred(3));
9044
9045        let next = parser.push_new_recursion_context(13, 3);
9046        assert_eq!(next.invoking_state(), 13);
9047        parser.unroll_recursion_context();
9048        assert_eq!(parser.precedence_stack, vec![0]);
9049        assert_eq!(
9050            parser.rule_context_stack,
9051            vec![RuleContextFrame {
9052                rule_index: 2,
9053                invoking_state: 7
9054            }]
9055        );
9056
9057        parser.exit_rule();
9058        assert!(parser.rule_context_stack.is_empty());
9059    }
9060
9061    #[test]
9062    fn parser_predicates_support_token_adjacency() {
9063        let mut parser = mini_parser(vec![
9064            CommonToken::new(1).with_text("=").with_span(0, 0),
9065            CommonToken::new(1).with_text(">").with_span(1, 1),
9066            CommonToken::eof("parser-test", 2, 1, 2),
9067        ]);
9068        parser.consume();
9069        parser.consume();
9070
9071        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
9072
9073        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
9074
9075        let mut parser = mini_parser(vec![
9076            CommonToken::new(1).with_text("=").with_span(0, 0),
9077            CommonToken::new(1)
9078                .with_text(" ")
9079                .with_channel(HIDDEN_CHANNEL)
9080                .with_span(1, 1),
9081            CommonToken::new(1).with_text(">").with_span(2, 2),
9082            CommonToken::eof("parser-test", 3, 1, 3),
9083        ]);
9084        parser.consume();
9085        parser.consume();
9086
9087        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
9088    }
9089
9090    #[test]
9091    fn parser_predicates_support_context_child_text_checks() {
9092        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9093        let mut context = ParserRuleContext::new(1, 0);
9094        let mut child_context = ParserRuleContext::new(2, 0);
9095        child_context.add_child(ParseTree::Terminal(TerminalNode::new(
9096            CommonToken::new(1).with_text("var"),
9097        )));
9098        context.add_child(ParseTree::Rule(RuleNode::new(child_context)));
9099        let predicates = [(
9100            1,
9101            0,
9102            ParserPredicate::ContextChildRuleTextNotEquals {
9103                rule_index: 2,
9104                text: "var",
9105            },
9106        )];
9107
9108        assert!(
9109            !parser.parser_semantic_predicate_matches_with_context_and_local(
9110                &predicates,
9111                1,
9112                0,
9113                &context,
9114                0,
9115            )
9116        );
9117    }
9118
9119    #[test]
9120    fn context_expected_symbols_walks_nullable_parent_contexts() {
9121        let atn = nested_nullable_context_atn();
9122        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9123        parser.rule_context_stack = vec![
9124            RuleContextFrame {
9125                rule_index: 0,
9126                invoking_state: 0,
9127            },
9128            RuleContextFrame {
9129                rule_index: 1,
9130                invoking_state: 1,
9131            },
9132            RuleContextFrame {
9133                rule_index: 2,
9134                invoking_state: 2,
9135            },
9136        ];
9137
9138        let expected = parser.context_expected_symbols(&atn);
9139
9140        assert!(expected.contains(&1));
9141        assert!(expected.contains(&TOKEN_EOF));
9142    }
9143
9144    #[test]
9145    fn prediction_context_reuses_cached_stack_until_rule_stack_changes() {
9146        let atn = nested_nullable_context_atn();
9147        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9148        parser.rule_context_stack = vec![
9149            RuleContextFrame {
9150                rule_index: 0,
9151                invoking_state: 0,
9152            },
9153            RuleContextFrame {
9154                rule_index: 1,
9155                invoking_state: 1,
9156            },
9157            RuleContextFrame {
9158                rule_index: 2,
9159                invoking_state: 2,
9160            },
9161        ];
9162
9163        let first = parser.prediction_context(&atn);
9164        let second = parser.prediction_context(&atn);
9165        assert!(Rc::ptr_eq(&first, &second));
9166
9167        parser.exit_rule();
9168        let after_pop = parser.prediction_context(&atn);
9169        assert!(!Rc::ptr_eq(&first, &after_pop));
9170    }
9171
9172    #[test]
9173    fn generated_match_token_recovers_missing_token_from_context_follow() {
9174        let atn = generated_match_recovery_atn();
9175        let data = RecognizerData::new(
9176            "Mini.g4",
9177            Vocabulary::new(
9178                [None, Some("'X'"), Some("'Y'")],
9179                [None, Some("X"), Some("Y")],
9180                [None::<&str>, None, None],
9181            ),
9182        );
9183        let mut parser = BaseParser::new(
9184            CommonTokenStream::new(Source {
9185                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
9186                index: 0,
9187            }),
9188            data,
9189        );
9190        parser.rule_context_stack = vec![
9191            RuleContextFrame {
9192                rule_index: 0,
9193                invoking_state: 0,
9194            },
9195            RuleContextFrame {
9196                rule_index: 1,
9197                invoking_state: 1,
9198            },
9199        ];
9200        assert_eq!(parser.number_of_syntax_errors(), 0);
9201
9202        let node = parser
9203            .match_token_recovering(2, 5, &atn)
9204            .expect("generated match should insert missing token");
9205
9206        assert_eq!(node.children().len(), 1);
9207        assert_eq!(node.children()[0].text(), "<missing 'Y'>");
9208        // Single-token insertion synthesizes a missing token and consumes nothing,
9209        // so no EOF terminal is consumed even though lookahead is EOF.
9210        assert!(!node.consumed_eof());
9211        assert_eq!(parser.la(1), TOKEN_EOF);
9212        assert_eq!(parser.number_of_syntax_errors(), 1);
9213        assert_eq!(
9214            parser.generated_parser_diagnostics,
9215            [ParserDiagnostic {
9216                line: 1,
9217                column: 3,
9218                message: "missing 'Y' at '<EOF>'".to_owned(),
9219            }]
9220        );
9221    }
9222
9223    #[test]
9224    fn generated_match_token_counts_single_token_deletion_recovery() {
9225        let atn = generated_match_recovery_atn();
9226        let data = RecognizerData::new(
9227            "Mini.g4",
9228            Vocabulary::new(
9229                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
9230                [None, Some("X"), Some("Y"), Some("Z")],
9231                [None::<&str>, None, None, None],
9232            ),
9233        );
9234        let mut parser = BaseParser::new(
9235            CommonTokenStream::new(Source {
9236                tokens: vec![
9237                    CommonToken::new(3).with_text("z"),
9238                    CommonToken::new(2).with_text("y"),
9239                    CommonToken::eof("parser-test", 3, 1, 3),
9240                ],
9241                index: 0,
9242            }),
9243            data,
9244        );
9245
9246        let node = parser
9247            .match_token_recovering(2, 5, &atn)
9248            .expect("generated match should delete the extraneous token");
9249
9250        assert_eq!(node.children().len(), 2);
9251        assert!(matches!(node.children()[0], ParseTree::Error(_)));
9252        assert_eq!(node.children()[0].text(), "z");
9253        assert_eq!(node.children()[1].text(), "y");
9254        assert_eq!(parser.number_of_syntax_errors(), 1);
9255    }
9256
9257    #[test]
9258    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
9259        let atn = generated_match_recovery_atn();
9260        let data = RecognizerData::new(
9261            "Mini.g4",
9262            Vocabulary::new(
9263                [None, Some("'X'"), Some("'Y'")],
9264                [None, Some("X"), Some("Y")],
9265                [None::<&str>, None, None],
9266            ),
9267        );
9268        let mut parser = BaseParser::new(
9269            CommonTokenStream::new(Source {
9270                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
9271                index: 0,
9272            }),
9273            data,
9274        );
9275        parser.rule_context_stack = vec![
9276            RuleContextFrame {
9277                rule_index: 0,
9278                invoking_state: 0,
9279            },
9280            RuleContextFrame {
9281                rule_index: 1,
9282                invoking_state: 1,
9283            },
9284        ];
9285        let marker = parser.generated_diagnostics_checkpoint();
9286
9287        let _ = parser
9288            .match_token_recovering(2, 5, &atn)
9289            .expect("generated match should insert missing token");
9290        assert_eq!(parser.number_of_syntax_errors(), 1);
9291
9292        parser.restore_generated_diagnostics(marker);
9293
9294        assert_eq!(parser.number_of_syntax_errors(), 0);
9295        assert!(parser.generated_parser_diagnostics.is_empty());
9296    }
9297
9298    #[test]
9299    fn generated_prediction_diagnostics_use_adaptive_context() {
9300        let atn = two_alt_decision_atn();
9301        let data = RecognizerData::new(
9302            "Mini.g4",
9303            Vocabulary::new(
9304                [None, Some("'x'"), Some("'y'")],
9305                [None, Some("X"), Some("Y")],
9306                [None::<&str>, None, None],
9307            ),
9308        )
9309        .with_rule_names(["s"]);
9310        let mut parser = BaseParser::new(
9311            CommonTokenStream::new(Source {
9312                tokens: vec![
9313                    CommonToken::new(1)
9314                        .with_text("x")
9315                        .with_position(1, 0)
9316                        .with_span(0, 0),
9317                    CommonToken::new(2)
9318                        .with_text("y")
9319                        .with_position(1, 2)
9320                        .with_span(1, 1),
9321                    CommonToken::eof("parser-test", 2, 1, 3),
9322                ],
9323                index: 0,
9324            }),
9325            data,
9326        );
9327        parser.set_report_diagnostic_errors(true);
9328
9329        parser.record_generated_prediction_diagnostic(
9330            &atn,
9331            1,
9332            &ParserAtnPrediction {
9333                alt: 1,
9334                requires_full_context: true,
9335                has_semantic_context: false,
9336                diagnostic: Some(ParserAtnPredictionDiagnostic {
9337                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
9338                    start_index: 0,
9339                    sll_stop_index: 1,
9340                    ll_stop_index: 0,
9341                    conflicting_alts: vec![1, 2],
9342                }),
9343            },
9344        );
9345        parser.record_generated_prediction_diagnostic(
9346            &atn,
9347            1,
9348            &ParserAtnPrediction {
9349                alt: 1,
9350                requires_full_context: true,
9351                has_semantic_context: false,
9352                diagnostic: Some(ParserAtnPredictionDiagnostic {
9353                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
9354                    start_index: 0,
9355                    sll_stop_index: 1,
9356                    ll_stop_index: 1,
9357                    conflicting_alts: vec![1, 2],
9358                }),
9359            },
9360        );
9361
9362        assert_eq!(
9363            parser.generated_parser_diagnostics,
9364            [
9365                ParserDiagnostic {
9366                    line: 1,
9367                    column: 2,
9368                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
9369                },
9370                ParserDiagnostic {
9371                    line: 1,
9372                    column: 0,
9373                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
9374                },
9375                ParserDiagnostic {
9376                    line: 1,
9377                    column: 2,
9378                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
9379                },
9380                ParserDiagnostic {
9381                    line: 1,
9382                    column: 2,
9383                    message: "reportAmbiguity d=0 (s): ambigAlts={1, 2}, input='xy'".to_owned(),
9384                },
9385            ]
9386        );
9387    }
9388
9389    #[test]
9390    fn generated_match_not_set_recovers_empty_complement_at_eof() {
9391        let atn = complement_set_atn();
9392        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9393        parser.rule_context_stack = vec![RuleContextFrame {
9394            rule_index: 0,
9395            invoking_state: 0,
9396        }];
9397
9398        let node = parser
9399            .match_not_set_recovering(&[(1, 1)], 1, 1, 1, &atn)
9400            .expect("empty complement should recover at EOF");
9401
9402        assert_eq!(node.children().len(), 1);
9403        // Recovery synthesizes a missing token without consuming EOF, so the
9404        // enclosing rule must not record EOF as its stop token.
9405        assert!(!node.consumed_eof());
9406        assert_eq!(parser.la(1), TOKEN_EOF);
9407        assert_eq!(
9408            parser.generated_parser_diagnostics,
9409            [ParserDiagnostic {
9410                line: 1,
9411                column: 1,
9412                message: "missing {} at '<EOF>'".to_owned(),
9413            }]
9414        );
9415    }
9416
9417    #[test]
9418    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
9419        // `start : . EOF ;` on empty input. The wildcard is modeled as an
9420        // empty-complement not-set; at EOF the follow state (the explicit EOF
9421        // match) expects EOF, so even in the start rule recovery must perform
9422        // single-token insertion (`<missing ...>`) rather than aborting — matching
9423        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
9424        let atn = wildcard_then_eof_atn();
9425        let data = RecognizerData::new(
9426            "Mini.g4",
9427            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
9428        );
9429        let mut parser = BaseParser::new(
9430            CommonTokenStream::new(Source {
9431                tokens: vec![CommonToken::eof("parser-test", 1, 1, 1)],
9432                index: 0,
9433            }),
9434            data,
9435        );
9436        parser.rule_context_stack = vec![RuleContextFrame {
9437            rule_index: 0,
9438            invoking_state: 0,
9439        }];
9440
9441        let node = parser
9442            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
9443            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
9444
9445        // A single `<missing ...>` error node is inserted; EOF is not consumed.
9446        assert_eq!(node.children().len(), 1);
9447        assert!(!node.consumed_eof());
9448        assert!(node.children()[0].text().starts_with("<missing"));
9449        assert_eq!(parser.la(1), TOKEN_EOF);
9450        assert_eq!(
9451            parser.generated_parser_diagnostics,
9452            [ParserDiagnostic {
9453                line: 1,
9454                column: 1,
9455                message: "missing 'x' at '<EOF>'".to_owned(),
9456            }]
9457        );
9458    }
9459
9460    #[test]
9461    fn generated_rule_recovery_consumes_to_parent_follow() {
9462        let atn = generated_match_recovery_atn();
9463        let data = RecognizerData::new(
9464            "Mini.g4",
9465            Vocabulary::new(
9466                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
9467                [None, Some("X"), Some("Y"), Some("Z")],
9468                [None::<&str>, None, None, None],
9469            ),
9470        );
9471        let mut parser = BaseParser::new(
9472            CommonTokenStream::new(Source {
9473                tokens: vec![
9474                    CommonToken::new(3).with_text("z"),
9475                    CommonToken::eof("parser-test", 1, 1, 1),
9476                ],
9477                index: 0,
9478            }),
9479            data,
9480        );
9481        let _parent = parser.enter_rule(0, 0);
9482        let marker = parser.push_invoking_state(1);
9483        let mut child = parser.enter_rule(4, 1);
9484        parser.discard_invoking_state(marker);
9485
9486        parser.recover_generated_rule(
9487            &mut child,
9488            &atn,
9489            AntlrError::ParserError {
9490                line: 1,
9491                column: 0,
9492                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
9493            },
9494        );
9495        let tree = parser.finish_rule(child, false);
9496
9497        assert_eq!(parser.la(1), TOKEN_EOF);
9498        assert_eq!(tree.to_string_tree(&["s", "a"]), "(a z)");
9499        assert_eq!(parser.number_of_syntax_errors(), 1);
9500        assert_eq!(
9501            parser.generated_parser_diagnostics,
9502            [ParserDiagnostic {
9503                line: 1,
9504                column: 0,
9505                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
9506            }]
9507        );
9508        parser.exit_rule();
9509    }
9510
9511    #[test]
9512    fn greedy_ll1_alt_handles_nullable_loop_exit() {
9513        let mut body_symbols = TokenBitSet::default();
9514        body_symbols.insert(1);
9515        let entry = DecisionLookahead {
9516            transitions: vec![
9517                TransitionLookSet {
9518                    symbols: body_symbols,
9519                    nullable: false,
9520                },
9521                TransitionLookSet {
9522                    symbols: TokenBitSet::default(),
9523                    nullable: true,
9524                },
9525            ],
9526        };
9527
9528        assert_eq!(ll1_unique_alt(&entry, 2), None);
9529        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
9530        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
9531        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
9532    }
9533
9534    #[test]
9535    fn single_outcome_memo_probe_selects_sparse_or_promote_mode() {
9536        let key = |state_number| FastRecognizeKey {
9537            state_number,
9538            stop_state: 10,
9539            index: state_number,
9540            rule_start_index: 0,
9541            decision_start_index: None,
9542            precedence: 0,
9543            recovery_symbols_id: 0,
9544            recovery_state: None,
9545        };
9546
9547        let mut sparse = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9548        for state_number in 0..(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT - 1) {
9549            assert!(sparse.should_memoize_single_outcome(&key(state_number)));
9550        }
9551        assert!(!sparse.should_memoize_single_outcome(&key(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT)));
9552        assert_eq!(
9553            sparse.single_outcome_memo_mode,
9554            SingleOutcomeMemoMode::Sparse
9555        );
9556
9557        let mut promote = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9558        let repeated = key(1);
9559        for _ in 0..=CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
9560            assert!(promote.should_memoize_single_outcome(&repeated));
9561        }
9562        assert_eq!(
9563            promote.single_outcome_memo_mode,
9564            SingleOutcomeMemoMode::Promote
9565        );
9566    }
9567
9568    #[test]
9569    fn clean_empty_multi_alt_outcomes_are_memoized() {
9570        let mut atn = Atn::new(AtnType::Parser, 2);
9571        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
9572        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
9573        atn.add_state(AtnState::new(2, AtnStateKind::RuleStop).with_rule_index(0));
9574        atn.set_rule_to_start_state(vec![0]);
9575        atn.set_rule_to_stop_state(vec![2]);
9576        atn.state_mut(0)
9577            .expect("state 0")
9578            .add_transition(Transition::Epsilon { target: 1 });
9579        atn.state_mut(1)
9580            .expect("state 1")
9581            .add_transition(Transition::Atom {
9582                target: 2,
9583                label: 1,
9584            });
9585        atn.state_mut(1)
9586            .expect("state 1")
9587            .add_transition(Transition::Atom {
9588                target: 2,
9589                label: 2,
9590            });
9591
9592        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
9593        parser.fast_recovery_enabled = false;
9594        let mut visiting = FxHashSet::default();
9595        let mut memo = FxHashMap::default();
9596        let mut expected = ExpectedTokens::default();
9597        let outcomes = parser.recognize_state_fast(
9598            &atn,
9599            FastRecognizeRequest {
9600                state_number: 1,
9601                stop_state: 2,
9602                index: 0,
9603                rule_start_index: 0,
9604                decision_start_index: None,
9605                precedence: 0,
9606                depth: 0,
9607                recovery_symbols: parser.empty_recovery_symbols(),
9608                recovery_state: None,
9609            },
9610            &mut visiting,
9611            &mut memo,
9612            &mut expected,
9613        );
9614
9615        assert!(outcomes.is_empty());
9616        assert_eq!(memo.len(), 1);
9617        assert!(memo.values().next().expect("memo entry").is_empty());
9618    }
9619
9620    #[test]
9621    fn wildcard_matches_non_eof_only() {
9622        let mut parser = mini_parser(vec![
9623            CommonToken::new(1).with_text("x"),
9624            CommonToken::eof("parser-test", 1, 1, 1),
9625        ]);
9626        assert_eq!(parser.match_wildcard().expect("wildcard").text(), "x");
9627        assert!(parser.match_wildcard().is_err());
9628    }
9629
9630    #[test]
9631    fn add_parse_child_records_match_even_without_tree_building() {
9632        // `sync_decision`'s "is the current context empty" flag must reflect real
9633        // matches, not parse-tree children: when `build_parse_trees(false)`,
9634        // `children` stays empty but `has_matched_child` must still flip so nested
9635        // recovery does not wrongly suppress single-token deletion.
9636        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9637        let token = CommonToken::new(1).with_text("x");
9638
9639        parser.set_build_parse_trees(false);
9640        let mut ctx = ParserRuleContext::new(0, 0);
9641        assert!(!ctx.has_matched_child());
9642        parser.add_parse_child(
9643            &mut ctx,
9644            ParseTree::Terminal(TerminalNode::new(token.clone())),
9645        );
9646        // Tree building is off, so no child is stored...
9647        assert!(ctx.children().is_empty());
9648        // ...but the match is recorded, so the context is no longer "empty".
9649        assert!(ctx.has_matched_child());
9650
9651        // With tree building on, the child is stored and the match is recorded.
9652        parser.set_build_parse_trees(true);
9653        let mut ctx = ParserRuleContext::new(0, 0);
9654        parser.add_parse_child(&mut ctx, ParseTree::Terminal(TerminalNode::new(token)));
9655        assert_eq!(ctx.children().len(), 1);
9656        assert!(ctx.has_matched_child());
9657    }
9658
9659    #[test]
9660    fn parser_interprets_simple_atn_rule() {
9661        let atn = token_then_eof_atn();
9662        let mut parser = mini_parser(vec![
9663            CommonToken::new(1).with_text("x"),
9664            CommonToken::eof("parser-test", 1, 1, 1),
9665        ]);
9666
9667        let tree = parser
9668            .parse_atn_rule(&atn, 0)
9669            .expect("artificial parser rule should parse");
9670        assert_eq!(tree.text(), "x<EOF>");
9671        assert_eq!(parser.number_of_syntax_errors(), 0);
9672        assert_eq!(
9673            tree.first_rule_stop(0)
9674                .expect("rule should stop at EOF")
9675                .token_type(),
9676            TOKEN_EOF
9677        );
9678
9679        let mut parser = mini_parser(vec![
9680            CommonToken::new(1).with_text("x"),
9681            CommonToken::eof("parser-test", 1, 1, 1),
9682        ]);
9683        let (tree, actions) = parser
9684            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
9685            .expect("runtime-option parser rule should parse");
9686        assert!(actions.is_empty());
9687        assert_eq!(
9688            tree.first_rule_stop(0)
9689                .expect("rule should stop at EOF")
9690                .token_type(),
9691            TOKEN_EOF
9692        );
9693    }
9694
9695    #[test]
9696    fn parser_exposes_buffered_token_stream_after_parse() {
9697        let atn = token_then_eof_atn();
9698        let mut parser = mini_parser(vec![
9699            CommonToken::new(1).with_text("x"),
9700            CommonToken::eof("parser-test", 1, 1, 1),
9701        ]);
9702
9703        let tree = parser
9704            .parse_atn_rule(&atn, 0)
9705            .expect("artificial parser rule should parse");
9706        assert_eq!(tree.text(), "x<EOF>");
9707
9708        let stream = parser.token_stream();
9709        let source_index_after_parse = stream.token_source().index;
9710        let buffered = stream.tokens();
9711        assert_eq!(buffered.len(), 2);
9712        assert_eq!(buffered[0].text(), Some("x"));
9713        assert_eq!(buffered[0].token_index(), 0);
9714        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
9715        assert_eq!(stream.token_source().index, source_index_after_parse);
9716
9717        let stream = parser.into_token_stream();
9718        assert_eq!(stream.token_source().index, source_index_after_parse);
9719        assert_eq!(stream.tokens()[0].text(), Some("x"));
9720        assert_eq!(stream.tokens()[1].token_type(), TOKEN_EOF);
9721    }
9722
9723    #[test]
9724    fn parser_syntax_error_count_tracks_interpreted_recovery() {
9725        let atn = token_then_eof_atn();
9726        let mut parser = mini_parser(vec![
9727            CommonToken::new(1).with_text("x"),
9728            CommonToken::new(2).with_text("y"),
9729            CommonToken::eof("parser-test", 2, 1, 2),
9730        ]);
9731
9732        let tree = parser
9733            .parse_atn_rule(&atn, 0)
9734            .expect("invalid token should recover into an error node");
9735
9736        assert_eq!(parser.number_of_syntax_errors(), 1);
9737        assert_eq!(
9738            tree.first_error_token()
9739                .expect("recovery should embed an error token")
9740                .text(),
9741            Some("y")
9742        );
9743    }
9744
9745    #[test]
9746    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
9747        let atn = token_then_eof_atn();
9748        let mut parser = mini_parser(vec![
9749            CommonToken::new(2).with_text("y"),
9750            CommonToken::eof("parser-test", 1, 1, 1),
9751        ]);
9752
9753        let error = parser
9754            .parse_atn_rule(&atn, 0)
9755            .expect_err("start-rule mismatch should remain a parser error");
9756
9757        assert_eq!(parser.number_of_syntax_errors(), 1);
9758        assert!(matches!(error, AntlrError::ParserError { .. }));
9759    }
9760
9761    #[test]
9762    fn adaptive_direct_rule_uses_simulator_decision() {
9763        let atn = two_alt_decision_atn();
9764        let mut simulator = ParserAtnSimulator::new(&atn);
9765        let mut parser = mini_parser(vec![
9766            CommonToken::new(2).with_text("y"),
9767            CommonToken::eof("parser-test", 1, 1, 1),
9768        ]);
9769
9770        let tree = parser
9771            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
9772            .expect("direct adaptive rule should parse");
9773
9774        assert_eq!(tree.text(), "y");
9775        assert_eq!(parser.input.index(), 1);
9776    }
9777
9778    #[test]
9779    fn adaptive_direct_rule_restores_input_on_fallback() {
9780        let atn = predicate_after_token_atn();
9781        let mut simulator = ParserAtnSimulator::new(&atn);
9782        let mut parser = mini_parser(vec![
9783            CommonToken::new(1).with_text("x"),
9784            CommonToken::new(2).with_text("y"),
9785            CommonToken::eof("parser-test", 2, 1, 2),
9786        ]);
9787
9788        let tree = parser
9789            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
9790            .expect("fallback recognizer should parse");
9791
9792        assert_eq!(tree.text(), "xy");
9793        assert_eq!(parser.input.index(), 2);
9794    }
9795
9796    #[test]
9797    fn parser_rule_start_skips_leading_hidden_tokens() {
9798        let atn = token_then_eof_atn();
9799        let mut parser = mini_parser(vec![
9800            CommonToken::new(99)
9801                .with_text(" ")
9802                .with_channel(HIDDEN_CHANNEL),
9803            CommonToken::new(1).with_text("x"),
9804            CommonToken::eof("parser-test", 2, 1, 2),
9805        ]);
9806
9807        let tree = parser
9808            .parse_atn_rule(&atn, 0)
9809            .expect("artificial parser rule should parse");
9810        let Some(ParseTree::Rule(rule)) = tree.first_rule(0) else {
9811            panic!("rule node should be present");
9812        };
9813        assert_eq!(
9814            rule.context()
9815                .start()
9816                .expect("rule should have a start token")
9817                .token_type(),
9818            1
9819        );
9820    }
9821
9822    #[test]
9823    fn parser_action_after_eof_stops_at_eof_token() {
9824        let atn = eof_then_action_atn();
9825        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
9826
9827        let (_, actions) = parser
9828            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
9829            .expect("EOF action rule should parse");
9830
9831        assert_eq!(actions.len(), 1);
9832        assert_eq!(actions[0].stop_index(), Some(0));
9833        assert_eq!(
9834            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
9835            ""
9836        );
9837    }
9838
9839    #[test]
9840    fn after_action_stop_uses_rule_context_stop_not_cursor() {
9841        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
9842        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
9843        // but the rule did not consume it. The @after stop must follow the rule
9844        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
9845        let mut id = CommonToken::new(1).with_text("x");
9846        id.set_token_index(0);
9847        let mut eof = CommonToken::eof("parser-test", 1, 1, 1);
9848        eof.set_token_index(1);
9849        let mut parser = mini_parser(vec![id.clone(), eof]);
9850        // Advance the cursor onto EOF, as it would be after `a` matched ID.
9851        parser.consume();
9852        assert_eq!(parser.la(1), TOKEN_EOF);
9853
9854        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
9855        // exactly what finish_rule(consumed_eof = false) records.
9856        let mut ctx = ParserRuleContext::new(0, 0);
9857        ctx.set_stop(id);
9858        let tree = ParseTree::Rule(RuleNode::new(ctx));
9859
9860        let current_index = parser.input.index();
9861        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
9862        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
9863        // ...but the tree-aware helper follows the rule context stop (ID).
9864        assert_eq!(
9865            parser.after_action_stop_index_for_tree(&tree, current_index),
9866            Some(0)
9867        );
9868    }
9869
9870    #[test]
9871    fn after_action_start_uses_rule_context_start_not_cursor() {
9872        // A rule that begins after leading hidden-channel tokens: the rule context
9873        // start (set by `enter_rule`) is the first visible token, not the raw cursor
9874        // that may still point at the hidden prefix. The @after start must follow
9875        // the context start so `$start`/`$text` excludes the hidden prefix.
9876        let parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
9877        let mut id = CommonToken::new(1).with_text("x");
9878        // The first visible token sits at stream index 2 (after two hidden tokens).
9879        id.set_token_index(2);
9880
9881        let mut ctx = ParserRuleContext::new(0, 0);
9882        ctx.set_start(id);
9883        let tree = ParseTree::Rule(RuleNode::new(ctx));
9884
9885        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
9886        // ...but the tree-aware helper follows the rule context start (index 2).
9887        assert_eq!(parser.after_action_start_index_for_tree(&tree, 0), 2);
9888
9889        // With no rule start recorded, it falls back to the provided index.
9890        let empty = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, 0)));
9891        assert_eq!(parser.after_action_start_index_for_tree(&empty, 7), 7);
9892    }
9893
9894    #[test]
9895    fn fast_outcome_selection_respects_sll_tie_order() {
9896        let first = FastRecognizeOutcome {
9897            index: 1,
9898            consumed_eof: false,
9899            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
9900                line: 1,
9901                column: 0,
9902                message: "mismatched input 'x'".to_owned(),
9903            }]),
9904            nodes: NodeList::new(),
9905        };
9906        let second = FastRecognizeOutcome {
9907            index: first.index,
9908            consumed_eof: first.consumed_eof,
9909            diagnostics: FastDiagnostics::new(),
9910            nodes: NodeList::new(),
9911        };
9912
9913        let selected = select_best_fast_outcome(
9914            [first.clone(), second.clone()].into_iter(),
9915            PredictionMode::Sll,
9916            None,
9917            |_| panic!("caller-follow token probe should not run"),
9918        )
9919        .expect("one outcome should be selected");
9920        assert_eq!(selected.diagnostics.len(), 1);
9921        let eof_second = FastRecognizeOutcome {
9922            index: second.index,
9923            consumed_eof: true,
9924            diagnostics: FastDiagnostics::new(),
9925            nodes: NodeList::new(),
9926        };
9927        let selected = select_best_fast_outcome(
9928            [first.clone(), eof_second].into_iter(),
9929            PredictionMode::Sll,
9930            None,
9931            |_| panic!("caller-follow token probe should not run"),
9932        )
9933        .expect("one outcome should be selected");
9934        assert!(!selected.consumed_eof);
9935        let selected = select_best_fast_outcome(
9936            [first, second].into_iter(),
9937            PredictionMode::Ll,
9938            None,
9939            |_| panic!("caller-follow token probe should not run"),
9940        )
9941        .expect("one outcome should be selected");
9942        assert!(selected.diagnostics.is_empty());
9943    }
9944
9945    #[test]
9946    fn fast_outcome_selection_prefers_generated_caller_follow() {
9947        let earlier = FastRecognizeOutcome {
9948            index: 7,
9949            consumed_eof: false,
9950            diagnostics: FastDiagnostics::new(),
9951            nodes: NodeList::new(),
9952        };
9953        let later = FastRecognizeOutcome {
9954            index: 8,
9955            consumed_eof: false,
9956            diagnostics: FastDiagnostics::new(),
9957            nodes: NodeList::new(),
9958        };
9959        let mut follow = TokenBitSet::default();
9960        follow.insert(5);
9961
9962        let selected = select_best_fast_outcome(
9963            [later.clone(), earlier.clone()].into_iter(),
9964            PredictionMode::Ll,
9965            Some(&follow),
9966            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
9967        )
9968        .expect("one outcome should be selected");
9969        assert_eq!(selected.index, 7);
9970
9971        let selected = select_best_fast_outcome(
9972            [later.clone(), earlier.clone()].into_iter(),
9973            PredictionMode::Ll,
9974            Some(&follow),
9975            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
9976        )
9977        .expect("one outcome should be selected");
9978        assert_eq!(selected.index, 8);
9979
9980        let indented_next_statement = FastRecognizeOutcome {
9981            index: 9,
9982            consumed_eof: false,
9983            diagnostics: FastDiagnostics::new(),
9984            nodes: NodeList::new(),
9985        };
9986        let selected = select_best_fast_outcome(
9987            [indented_next_statement, earlier.clone()].into_iter(),
9988            PredictionMode::Ll,
9989            Some(&follow),
9990            |index| {
9991                let is_boundary = index == 7;
9992                let is_boundary_gap = matches!(index, 7 | 8);
9993                (
9994                    if index == 7 { 5 } else { TOKEN_EOF },
9995                    is_boundary,
9996                    is_boundary_gap,
9997                )
9998            },
9999        )
10000        .expect("one outcome should be selected");
10001        assert_eq!(selected.index, 7);
10002
10003        let continuation = FastRecognizeOutcome {
10004            index: 10,
10005            consumed_eof: false,
10006            diagnostics: FastDiagnostics::new(),
10007            nodes: NodeList::new(),
10008        };
10009        let selected = select_best_fast_outcome(
10010            [continuation, earlier.clone()].into_iter(),
10011            PredictionMode::Ll,
10012            Some(&follow),
10013            |index| {
10014                let is_boundary = matches!(index, 7 | 9);
10015                (
10016                    if index == 7 { 5 } else { TOKEN_EOF },
10017                    is_boundary,
10018                    is_boundary,
10019                )
10020            },
10021        )
10022        .expect("one outcome should be selected");
10023        assert_eq!(selected.index, 10);
10024
10025        let selected = select_best_fast_outcome(
10026            [earlier, later].into_iter(),
10027            PredictionMode::Sll,
10028            Some(&follow),
10029            |_| panic!("caller-follow token probe should not run in SLL mode"),
10030        )
10031        .expect("one outcome should be selected");
10032        assert_eq!(selected.index, 8);
10033    }
10034
10035    #[test]
10036    fn caller_follow_boundary_text_requires_separator_shape() {
10037        assert!(is_caller_follow_boundary_text(";"));
10038        assert!(is_caller_follow_boundary_text("\n"));
10039        assert!(is_caller_follow_boundary_text("\r\n  "));
10040        assert!(is_caller_follow_boundary_text(";\n"));
10041        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
10042        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
10043        assert!(!is_caller_follow_boundary_text("identifier"));
10044        assert!(is_caller_follow_boundary_gap_text(" \t "));
10045        assert!(is_caller_follow_boundary_gap_text("\n  "));
10046        assert!(is_caller_follow_boundary_gap_text(";\t"));
10047        assert!(!is_caller_follow_boundary_gap_text(
10048            "\"\"\"line1\nline2\"\"\""
10049        ));
10050        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
10051    }
10052
10053    #[test]
10054    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
10055        let mut parser = mini_parser(vec![
10056            CommonToken::new(5).with_text("\n"),
10057            CommonToken::new(6)
10058                .with_text("// comment\n")
10059                .with_channel(HIDDEN_CHANNEL),
10060            CommonToken::new(1).with_text("x"),
10061            CommonToken::eof("parser-test", 1, 2, 0),
10062        ]);
10063
10064        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
10065        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
10066        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
10067    }
10068
10069    #[test]
10070    fn caller_follow_token_info_uses_stream_visible_channel() {
10071        let source = Source {
10072            tokens: vec![
10073                CommonToken::new(5).with_text("\n").with_channel(2),
10074                CommonToken::new(1).with_text("x").with_channel(2),
10075                CommonToken::new(6)
10076                    .with_text("// comment\n")
10077                    .with_channel(HIDDEN_CHANNEL),
10078                CommonToken::eof("parser-test", 1, 2, 0),
10079            ],
10080            index: 0,
10081        };
10082        let data = RecognizerData::new(
10083            "Mini.g4",
10084            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10085        );
10086        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
10087
10088        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
10089        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
10090        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
10091    }
10092
10093    #[test]
10094    fn reset_per_parse_caches_clears_state_expected_token_cache() {
10095        let atn = token_then_eof_atn();
10096        let mut parser = mini_parser(Vec::new());
10097
10098        let _ = parser.cached_state_expected_token_set(&atn, 0);
10099        assert!(!parser.state_expected_token_cache.is_empty());
10100
10101        parser.reset_per_parse_caches();
10102        assert!(parser.state_expected_token_cache.is_empty());
10103    }
10104
10105    #[test]
10106    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
10107        let source = Source {
10108            tokens: vec![
10109                CommonToken::new(1).with_text("x"),
10110                CommonToken::eof("parser-test", 1, 1, 1),
10111            ],
10112            index: 0,
10113        };
10114        let data = RecognizerData::new(
10115            "Mini.g4",
10116            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10117        );
10118        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
10119        let expected = ExpectedTokens {
10120            index: Some(0),
10121            symbols: BTreeSet::new(),
10122            no_viable: None,
10123        };
10124
10125        let (_, message) = parser.expected_error_message(0, 0, &expected);
10126
10127        assert_eq!(message, "mismatched input 'x'");
10128    }
10129
10130    #[test]
10131    fn eof_rule_stop_index_points_at_eof_token() {
10132        let source = Source {
10133            tokens: vec![
10134                CommonToken::new(1).with_text("x"),
10135                CommonToken::eof("parser-test", 1, 1, 1),
10136            ],
10137            index: 0,
10138        };
10139        let data = RecognizerData::new(
10140            "Mini.g4",
10141            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10142        );
10143        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
10144
10145        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
10146        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
10147    }
10148
10149    #[test]
10150    fn generated_parser_action_uses_current_rule_stop_boundary() {
10151        let mut parser = mini_parser(vec![
10152            CommonToken::new(1).with_text("x"),
10153            CommonToken::eof("parser-test", 1, 1, 1),
10154        ]);
10155
10156        parser.match_token(1).expect("token should match");
10157        let action = parser.parser_action_at_current(7, 0, 0, false);
10158        assert_eq!(action.source_state(), 7);
10159        assert_eq!(action.rule_index(), 0);
10160        assert_eq!(action.start_index(), 0);
10161        assert_eq!(action.stop_index(), Some(0));
10162
10163        parser.match_eof().expect("EOF should match");
10164        let action = parser.parser_action_at_current(8, 0, 0, true);
10165        assert_eq!(action.stop_index(), Some(1));
10166    }
10167
10168    #[test]
10169    fn folds_left_recursive_boundary_into_rule_node() {
10170        let nodes = fold_left_recursive_boundaries(vec![
10171            RecognizedNode::Token { index: 0 },
10172            RecognizedNode::LeftRecursiveBoundary { rule_index: 1 },
10173            RecognizedNode::Token { index: 1 },
10174        ]);
10175
10176        assert_eq!(
10177            nodes,
10178            vec![
10179                RecognizedNode::Rule {
10180                    rule_index: 1,
10181                    invoking_state: -1,
10182                    alt_number: 0,
10183                    start_index: 0,
10184                    stop_index: Some(0),
10185                    return_values: BTreeMap::new(),
10186                    children: vec![RecognizedNode::Token { index: 0 }],
10187                },
10188                RecognizedNode::Token { index: 1 },
10189            ]
10190        );
10191    }
10192
10193    #[test]
10194    fn outcome_ties_keep_later_non_recursive_alternative() {
10195        let first = RecognizeOutcome {
10196            index: 1,
10197            consumed_eof: false,
10198            alt_number: 0,
10199            member_values: BTreeMap::new(),
10200            return_values: BTreeMap::new(),
10201            diagnostics: Vec::new(),
10202            decisions: Vec::new(),
10203            actions: vec![ParserAction::new(1, 0, 0, None)],
10204            nodes: vec![RecognizedNode::Token { index: 0 }],
10205        };
10206        let second = RecognizeOutcome {
10207            actions: vec![ParserAction::new(2, 0, 0, None)],
10208            ..first.clone()
10209        };
10210
10211        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
10212            .expect("one outcome should be selected");
10213        assert_eq!(selected.actions[0].source_state(), 2);
10214    }
10215
10216    #[test]
10217    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
10218        let first = RecognizeOutcome {
10219            index: 1,
10220            consumed_eof: false,
10221            alt_number: 0,
10222            member_values: BTreeMap::new(),
10223            return_values: BTreeMap::new(),
10224            diagnostics: Vec::new(),
10225            decisions: Vec::new(),
10226            actions: vec![ParserAction::new(1, 0, 0, None)],
10227            nodes: vec![RecognizedNode::Token { index: 0 }],
10228        };
10229        let second = RecognizeOutcome {
10230            actions: vec![
10231                ParserAction::new(2, 0, 0, None),
10232                ParserAction::new(3, 0, 0, None),
10233            ],
10234            ..first.clone()
10235        };
10236
10237        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll)
10238            .expect("one outcome should be selected");
10239        assert_eq!(selected.actions.len(), 2);
10240    }
10241
10242    #[test]
10243    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
10244        let first = RecognizeOutcome {
10245            index: 7,
10246            consumed_eof: false,
10247            alt_number: 0,
10248            member_values: BTreeMap::new(),
10249            return_values: BTreeMap::new(),
10250            diagnostics: Vec::new(),
10251            decisions: vec![1, 0],
10252            actions: vec![
10253                ParserAction::new(23, 2, 2, Some(4)),
10254                ParserAction::new(23, 2, 0, Some(6)),
10255            ],
10256            nodes: vec![RecognizedNode::Token { index: 0 }],
10257        };
10258        let second = RecognizeOutcome {
10259            decisions: vec![0, 1],
10260            actions: vec![
10261                ParserAction::new(23, 2, 2, Some(6)),
10262                ParserAction::new(23, 2, 0, Some(6)),
10263            ],
10264            ..first.clone()
10265        };
10266
10267        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
10268            .expect("one outcome should be selected");
10269        assert_eq!(selected.actions[0].stop_index(), Some(6));
10270    }
10271
10272    #[test]
10273    fn outcome_ties_keep_first_recursive_tree_shape() {
10274        let recursive_nodes = vec![RecognizedNode::Rule {
10275            rule_index: 1,
10276            invoking_state: -1,
10277            alt_number: 0,
10278            start_index: 0,
10279            stop_index: Some(0),
10280            return_values: BTreeMap::new(),
10281            children: vec![RecognizedNode::Rule {
10282                rule_index: 1,
10283                invoking_state: -1,
10284                alt_number: 0,
10285                start_index: 0,
10286                stop_index: Some(0),
10287                return_values: BTreeMap::new(),
10288                children: vec![RecognizedNode::Token { index: 0 }],
10289            }],
10290        }];
10291        let first = RecognizeOutcome {
10292            index: 1,
10293            consumed_eof: false,
10294            alt_number: 0,
10295            member_values: BTreeMap::new(),
10296            return_values: BTreeMap::new(),
10297            diagnostics: Vec::new(),
10298            decisions: Vec::new(),
10299            actions: vec![ParserAction::new(1, 0, 0, None)],
10300            nodes: recursive_nodes.clone(),
10301        };
10302        let second = RecognizeOutcome {
10303            index: 1,
10304            consumed_eof: false,
10305            alt_number: 0,
10306            member_values: BTreeMap::new(),
10307            return_values: BTreeMap::new(),
10308            diagnostics: Vec::new(),
10309            decisions: Vec::new(),
10310            actions: vec![ParserAction::new(2, 0, 0, None)],
10311            nodes: recursive_nodes,
10312        };
10313
10314        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
10315            .expect("one outcome should be selected");
10316        assert_eq!(selected.actions[0].source_state(), 1);
10317    }
10318
10319    #[test]
10320    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
10321        let first_alt = RecognizeOutcome {
10322            index: 2,
10323            consumed_eof: true,
10324            alt_number: 0,
10325            member_values: BTreeMap::new(),
10326            return_values: BTreeMap::new(),
10327            diagnostics: vec![ParserDiagnostic {
10328                line: 1,
10329                column: 3,
10330                message: "missing 'Y' at '<EOF>'".to_owned(),
10331            }],
10332            decisions: vec![0],
10333            actions: vec![ParserAction::new(1, 0, 0, None)],
10334            nodes: vec![RecognizedNode::Token { index: 0 }],
10335        };
10336        let second_alt = RecognizeOutcome {
10337            diagnostics: Vec::new(),
10338            decisions: vec![1],
10339            actions: vec![ParserAction::new(2, 0, 0, None)],
10340            ..first_alt.clone()
10341        };
10342
10343        let selected =
10344            select_best_outcome([second_alt, first_alt].into_iter(), PredictionMode::Sll)
10345                .expect("one outcome should be selected");
10346        assert_eq!(selected.diagnostics.len(), 1);
10347        assert_eq!(selected.decisions, [0]);
10348    }
10349}