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