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