Skip to main content

antlr4_runtime/
parser.rs

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