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    /// Token at an absolute buffered index, including hidden/custom channels.
396    ///
397    /// Unlike [`Self::lt`], this does not apply the token stream's channel
398    /// filter and does not move its cursor. It is intended for semantic helpers
399    /// such as automatic-semicolon-insertion checks that inspect trivia
400    /// immediately before the current visible token.
401    pub fn token_at(&mut self, index: usize) -> Option<&CommonToken> {
402        self.input.get(index)
403    }
404
405    /// Current generated rule context, when a generated rule predicate supplied
406    /// one.
407    #[must_use]
408    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
409        self.context
410    }
411
412    /// Completed parse tree passed to an action hook, if the action is being
413    /// replayed after recognition.
414    #[must_use]
415    pub const fn tree(&self) -> Option<&'a ParseTree> {
416        self.tree
417    }
418
419    /// Integer local argument visible to this predicate coordinate.
420    #[must_use]
421    pub fn local_int_arg(&self) -> Option<i64> {
422        self.local_int_arg.map(|(_, value)| value)
423    }
424
425    /// Integer member value observed on the current speculative path.
426    #[must_use]
427    pub fn member_int(&self, member: usize) -> Option<i64> {
428        self.member_values.get(&member).copied()
429    }
430
431    /// Parser action event being replayed, when this context belongs to an
432    /// action hook.
433    #[must_use]
434    pub const fn action(&self) -> Option<ParserAction> {
435        self.action
436    }
437
438    /// Text covered by a parser action event.
439    ///
440    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
441    /// EOF the interval ends at the previous *visible* token, so trailing hidden
442    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
443    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
444    /// itself guards `start > stop`, so an empty interval yields `""`.
445    pub fn action_text(&mut self) -> String {
446        let Some(action) = self.action else {
447            return String::new();
448        };
449        let Some(stop) = action.stop_index() else {
450            return String::new();
451        };
452        let stop = if self
453            .input
454            .get(stop)
455            .is_some_and(|token| token.token_type() == TOKEN_EOF)
456        {
457            let Some(previous) = self.input.previous_visible_token_index(stop) else {
458                return String::new();
459            };
460            previous
461        } else {
462            stop
463        };
464        self.input.text(action.start_index(), stop)
465    }
466}
467
468/// User extension point for parser semantic predicates and actions that the
469/// metadata generator did not translate into built-in runtime metadata.
470///
471/// Returning `None`/`false` says "not handled", so the runtime falls through
472/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
473/// speculative prediction and must be replay-safe.
474pub trait SemanticHooks {
475    /// Whether this hook object may observe parser predicate transitions.
476    ///
477    /// Custom hooks default to conservative predicate handling so the fast
478    /// recognizer does not bypass a `sempred` implementation.
479    fn observes_parser_predicates(&self) -> bool {
480        true
481    }
482
483    fn sempred<S>(
484        &mut self,
485        ctx: &mut ParserSemCtx<'_, S>,
486        rule_index: usize,
487        pred_index: usize,
488    ) -> Option<bool>
489    where
490        S: TokenSource,
491    {
492        let _ = (ctx, rule_index, pred_index);
493        None
494    }
495
496    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
497    where
498        S: TokenSource,
499    {
500        let _ = (ctx, action);
501        false
502    }
503
504    fn lexer_sempred<I, F>(
505        &mut self,
506        ctx: &mut LexerSemCtx<'_, I, F>,
507        rule_index: usize,
508        pred_index: usize,
509    ) -> Option<bool>
510    where
511        I: CharStream,
512        F: TokenFactory,
513    {
514        let _ = (ctx, rule_index, pred_index);
515        None
516    }
517
518    /// Runs a lexer custom action on the committed lexing path. Returns whether
519    /// the hook handled the action.
520    ///
521    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
522    /// hook may change lexer state — [`LexerSemCtx::push_mode`],
523    /// [`LexerSemCtx::pop_mode`], [`LexerSemCtx::set_mode`] — just like the
524    /// closure-based `custom_action` API. (The speculative predicate context in
525    /// [`Self::lexer_sempred`] is a shared borrow, so those mutators are inert
526    /// there.)
527    fn lexer_action<I, F>(
528        &mut self,
529        ctx: &mut LexerSemCtx<'_, I, F>,
530        action: LexerCustomAction,
531    ) -> bool
532    where
533        I: CharStream,
534        F: TokenFactory,
535    {
536        let _ = (ctx, action);
537        false
538    }
539
540    /// Observes a token after committed lexer actions and portable commands
541    /// have run and the token has been emitted, immediately before it is
542    /// returned to the token stream.
543    ///
544    /// Hidden and custom-channel tokens are included. `skip` and intermediate
545    /// `more` matches do not produce callbacks.
546    fn lexer_token_emitted(&mut self, token: &CommonToken) {
547        let _ = token;
548    }
549}
550
551/// Default hook object used by parsers that do not need user-supplied
552/// semantics.
553#[derive(Clone, Copy, Debug, Default)]
554pub struct NoSemanticHooks;
555
556impl SemanticHooks for NoSemanticHooks {
557    fn observes_parser_predicates(&self) -> bool {
558        false
559    }
560}
561
562/// Parser semantic predicate rendered from a supported target template.
563///
564/// The metadata recognizer evaluates these at the token-stream index where the
565/// predicate transition is reached. Unsupported or absent predicate templates
566/// remain unconditional so existing generated parsers keep their previous
567/// behavior unless the generator opts into this table.
568#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
569pub enum ParserPredicate {
570    True,
571    False,
572    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
573    FalseWithMessage {
574        message: &'static str,
575    },
576    /// Target-template test helper that reports predicate evaluation before
577    /// returning the wrapped boolean value.
578    Invoke {
579        value: bool,
580    },
581    LookaheadTextEquals {
582        offset: isize,
583        text: &'static str,
584    },
585    LookaheadNotEquals {
586        offset: isize,
587        token_type: i32,
588    },
589    /// Checks that the last two consumed visible tokens were adjacent in the
590    /// token stream. Used by C# parser predicates for split operator tokens.
591    TokenPairAdjacent,
592    /// Checks a generated parser context child by rule index and text.
593    ///
594    /// If the child is absent the predicate succeeds, matching target helpers
595    /// that treat incomplete or non-matching contexts as non-restrictive.
596    ContextChildRuleTextNotEquals {
597        rule_index: usize,
598        text: &'static str,
599    },
600    /// Compares the current rule invocation's integer argument with a literal
601    /// value from a supported `ValEquals("$i", "...")` target template.
602    LocalIntEquals {
603        value: i64,
604    },
605    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
606    /// rule invocation's integer argument.
607    LocalIntLessOrEqual {
608        value: i64,
609    },
610    /// Compares a generated parser integer member modulo a literal value.
611    MemberModuloEquals {
612        member: usize,
613        modulus: i64,
614        value: i64,
615        equals: bool,
616    },
617    /// Compares a generated parser integer member with a literal value.
618    MemberEquals {
619        member: usize,
620        value: i64,
621        equals: bool,
622    },
623}
624
625impl ParserPredicate {
626    /// Lowers the legacy predicate metadata variant into `SemIR`.
627    ///
628    /// This is the compatibility adapter for generated parsers produced while
629    /// the runtime still emitted closed enum tables. Newer generated parsers
630    /// emit `SemIR` directly.
631    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
632        match self {
633            Self::True => ir.expr(PExpr::Bool(true)),
634            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
635            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
636            Self::LookaheadTextEquals { offset, text } => {
637                let token = ir.expr(PExpr::TokenText(offset));
638                let text = ir.intern(text);
639                let text = ir.expr(PExpr::Str(text));
640                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
641            }
642            Self::LookaheadNotEquals { offset, token_type } => {
643                let actual = ir.expr(PExpr::La(offset));
644                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
645                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
646            }
647            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
648            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
649                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
650                let expected = ir.intern(text);
651                let expected = ir.expr(PExpr::Str(expected));
652                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
653            }
654            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
655            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
656            Self::MemberModuloEquals {
657                member,
658                modulus,
659                value,
660                equals,
661            } => {
662                if modulus == 0 {
663                    return ir.expr(PExpr::Bool(false));
664                }
665                let member = ir.expr(PExpr::Member(member));
666                let modulus = ir.expr(PExpr::Int(modulus));
667                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
668                let expected = ir.expr(PExpr::Int(value));
669                ir.expr(PExpr::Cmp(
670                    if equals { CmpOp::Eq } else { CmpOp::Ne },
671                    actual,
672                    expected,
673                ))
674            }
675            Self::MemberEquals {
676                member,
677                value,
678                equals,
679            } => {
680                let actual = ir.expr(PExpr::Member(member));
681                let expected = ir.expr(PExpr::Int(value));
682                ir.expr(PExpr::Cmp(
683                    if equals { CmpOp::Eq } else { CmpOp::Ne },
684                    actual,
685                    expected,
686                ))
687            }
688        }
689    }
690
691    #[must_use]
692    pub const fn failure_message(self) -> Option<&'static str> {
693        match self {
694            Self::FalseWithMessage { message } => Some(message),
695            Self::True
696            | Self::False
697            | Self::Invoke { .. }
698            | Self::LookaheadTextEquals { .. }
699            | Self::LookaheadNotEquals { .. }
700            | Self::TokenPairAdjacent
701            | Self::ContextChildRuleTextNotEquals { .. }
702            | Self::LocalIntEquals { .. }
703            | Self::LocalIntLessOrEqual { .. }
704            | Self::MemberModuloEquals { .. }
705            | Self::MemberEquals { .. } => None,
706        }
707    }
708}
709
710fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
711    let local = ir.expr(PExpr::LocalArg);
712    let absent = ir.expr(PExpr::IsNull(local));
713    let expected = ir.expr(PExpr::Int(value));
714    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
715    ir.expr(PExpr::Or([absent, comparison].into()))
716}
717
718/// Policy for semantic predicate coordinates that have no runtime
719/// implementation.
720///
721/// ANTLR grammars may embed target-language predicates that the metadata
722/// generator could not translate into a [`ParserPredicate`] table entry. When
723/// recognition reaches such a coordinate the runtime cannot know the grammar
724/// author's intent, so the caller chooses how to proceed.
725///
726/// The default is [`Self::AssumeTrue`], matching the historical behavior of
727/// this runtime. That default is deprecated and will change to [`Self::Error`]
728/// in a future minor release; grammars relying on unconditional predicates
729/// should opt in explicitly.
730#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
731pub enum UnknownSemanticPolicy {
732    /// Treat the predicate as passing, as if it were absent from the grammar.
733    #[default]
734    AssumeTrue,
735    /// Treat the predicate as failing, removing the guarded alternative.
736    AssumeFalse,
737    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
738    /// coordinate that recognition evaluated.
739    Error,
740}
741
742/// Resolves a predicate coordinate that neither a translated table entry nor a
743/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
744///
745/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
746/// so the parse entry can surface every unresolved coordinate afterwards. Both
747/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
748/// funnel through here so a missing implementation is never silently coerced
749/// to a boolean (design goal G1: never silently mis-parse).
750fn apply_unknown_predicate_policy(
751    policy: UnknownSemanticPolicy,
752    rule_index: usize,
753    pred_index: usize,
754    hits: &mut Vec<(usize, usize)>,
755) -> bool {
756    match policy {
757        UnknownSemanticPolicy::AssumeTrue => true,
758        UnknownSemanticPolicy::AssumeFalse => false,
759        UnknownSemanticPolicy::Error => {
760            let coordinate = (rule_index, pred_index);
761            if !hits.contains(&coordinate) {
762                hits.push(coordinate);
763            }
764            false
765        }
766    }
767}
768
769/// Interval-set of expected token types, displayable through a vocabulary —
770/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
771/// generated test actions.
772#[derive(Clone, Debug, Eq, PartialEq)]
773pub struct ExpectedTokenSet {
774    symbols: BTreeSet<i32>,
775}
776
777impl ExpectedTokenSet {
778    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
779    #[must_use]
780    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
781        expected_symbols_display(&self.symbols, vocabulary)
782    }
783}
784
785/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
786///
787/// The first syntax error aborts the parse instead of recovering. Generated
788/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
789#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
790pub struct BailErrorStrategy;
791
792impl BailErrorStrategy {
793    #[must_use]
794    pub const fn new() -> Self {
795        Self
796    }
797}
798
799/// Prediction strategy requested by generated parser harnesses.
800#[derive(Clone, Copy, Debug, Eq, PartialEq)]
801pub enum PredictionMode {
802    /// Prefer the clean full-context outcome when alternatives reach the same
803    /// input position.
804    Ll,
805    /// Preserve SLL's first-viable alternative bias at a decision, even when a
806    /// later full-context alternative could avoid recovery.
807    Sll,
808    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
809    LlExactAmbigDetection,
810}
811
812/// Integer argument metadata for a generated parser rule invocation.
813///
814/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
815/// the generator records the rule-transition source state and the value that
816/// should be visible to semantic predicates inside the callee.
817#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
818pub struct ParserRuleArg {
819    /// ATN state containing the rule transition that receives this argument.
820    pub source_state: usize,
821    /// Callee rule index for the transition.
822    pub rule_index: usize,
823    /// Literal fallback value to expose in the callee.
824    pub value: i64,
825    /// Whether the callee should inherit the caller's current integer argument.
826    pub inherit_local: bool,
827}
828
829/// Integer member mutation attached to an ATN action transition.
830#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
831pub struct ParserMemberAction {
832    /// ATN state containing the action transition.
833    pub source_state: usize,
834    /// Generator-assigned integer member id.
835    pub member: usize,
836    /// Delta applied when the action is reached on one speculative path.
837    pub delta: i64,
838}
839
840/// Integer return-value assignment attached to an ATN action transition.
841///
842/// Generated parsers use this metadata when target actions assign a simple
843/// return field such as `$y=1000;`. The interpreter applies it while selecting
844/// the recognized path so the finished parse tree can answer later
845/// `$label.y` action templates.
846#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
847pub struct ParserReturnAction {
848    /// ATN state containing the action transition.
849    pub source_state: usize,
850    /// Rule index recorded by the serialized action transition.
851    pub rule_index: usize,
852    /// Return-field name as it appears in the grammar.
853    pub name: &'static str,
854    /// Literal integer value assigned by the action.
855    pub value: i64,
856}
857
858impl ParserMemberAction {
859    /// Lowers this speculative member mutation into a `SemIR` action.
860    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
861        let delta = ir.expr(PExpr::Int(self.delta));
862        ParserSemanticAction {
863            source_state: self.source_state,
864            rule_index: usize::MAX,
865            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
866            speculative: true,
867        }
868    }
869}
870
871impl ParserReturnAction {
872    /// Lowers this committed return-value assignment into a `SemIR` action.
873    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
874        let name = ir.intern(self.name);
875        let value = ir.expr(PExpr::Int(self.value));
876        ParserSemanticAction {
877            source_state: self.source_state,
878            rule_index: self.rule_index,
879            stmt: ir.stmt(AStmt::SetReturn(name, value)),
880            speculative: false,
881        }
882    }
883}
884
885/// Parser predicate coordinate lowered into [`SemIr`].
886#[derive(Clone, Copy, Debug, Eq, PartialEq)]
887pub struct ParserSemanticPredicate {
888    /// Serialized rule index that owns this predicate.
889    pub rule_index: usize,
890    /// Predicate index inside the owning rule.
891    pub pred_index: usize,
892    /// Root expression in the associated [`ParserSemantics::ir`] arena.
893    pub expr: ExprId,
894    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
895    pub failure_message: Option<&'static str>,
896}
897
898/// Parser action coordinate lowered into [`SemIr`].
899#[derive(Clone, Copy, Debug, Eq, PartialEq)]
900pub struct ParserSemanticAction {
901    /// ATN state containing the action transition.
902    pub source_state: usize,
903    /// Serialized rule index recorded by the action transition.
904    pub rule_index: usize,
905    /// Root statement in the associated [`ParserSemantics::ir`] arena.
906    pub stmt: StmtId,
907    /// Whether this action may run on speculative recognition paths.
908    pub speculative: bool,
909}
910
911/// Data-driven semantic tables emitted by generated parsers.
912///
913/// This is the runtime representation for issue #9's `SemIR` path. Existing
914/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
915/// remain accepted as deprecated adapters for generated code produced before
916/// this table existed.
917#[derive(Clone, Debug, Default, Eq, PartialEq)]
918pub struct ParserSemantics {
919    pub ir: SemIr,
920    pub predicates: Vec<ParserSemanticPredicate>,
921    pub actions: Vec<ParserSemanticAction>,
922}
923
924/// Optional generated-runtime metadata for metadata-driven parser execution.
925#[derive(Clone, Copy, Debug, Default)]
926pub struct ParserRuntimeOptions<'a> {
927    /// Rule indexes whose `@init` actions should be replayed.
928    pub init_action_rules: &'a [usize],
929    /// Whether generated parse-tree contexts should retain alternative numbers.
930    pub track_alt_numbers: bool,
931    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
932    pub predicates: &'a [(usize, usize, ParserPredicate)],
933    /// `SemIR` predicate/action table emitted by newer generated parsers.
934    pub semantics: Option<&'a ParserSemantics>,
935    /// Rule-call integer argument table keyed by ATN source state.
936    pub rule_args: &'a [ParserRuleArg],
937    /// Integer member mutations keyed by ATN action source state.
938    pub member_actions: &'a [ParserMemberAction],
939    /// Integer return assignments keyed by ATN action source state.
940    pub return_actions: &'a [ParserReturnAction],
941    /// How to evaluate semantic predicate coordinates absent from
942    /// `predicates`.
943    pub unknown_predicate_policy: UnknownSemanticPolicy,
944}
945
946pub trait Parser: Recognizer {
947    /// Reports whether generated parser rules should build parse-tree nodes
948    /// while recognizing input.
949    fn build_parse_trees(&self) -> bool;
950
951    /// Enables or disables parse-tree construction for subsequent rule calls.
952    fn set_build_parse_trees(&mut self, build: bool);
953
954    /// Returns the number of parser syntax errors recorded by committed parse
955    /// paths so far.
956    fn number_of_syntax_errors(&self) -> usize {
957        0
958    }
959
960    /// Reports whether prediction diagnostic-listener messages are emitted
961    /// during parser ATN recognition.
962    fn report_diagnostic_errors(&self) -> bool {
963        false
964    }
965
966    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
967    /// rule calls.
968    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
969
970    /// Reports the prediction strategy used when selecting among alternatives.
971    fn prediction_mode(&self) -> PredictionMode {
972        PredictionMode::Ll
973    }
974
975    /// Sets the prediction strategy for subsequent rule calls.
976    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
977}
978
979#[derive(Debug)]
980struct CachedPredictionContext {
981    version: usize,
982    atn_key: usize,
983    context: Rc<PredictionContext>,
984}
985
986#[derive(Debug)]
987struct LeftRecursiveCallerOverlap {
988    atn_key: SharedAtnCacheKey,
989    state_number: usize,
990    symbol: i32,
991    context: Rc<PredictionContext>,
992    overlaps: bool,
993}
994
995const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
996
997#[derive(Debug)]
998pub struct BaseParser<S, H = NoSemanticHooks> {
999    input: CommonTokenStream<S>,
1000    data: RecognizerData,
1001    semantic_hooks: H,
1002    build_parse_trees: bool,
1003    syntax_errors: usize,
1004    report_diagnostic_errors: bool,
1005    prediction_mode: PredictionMode,
1006    prediction_diagnostics: Vec<ParserDiagnostic>,
1007    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1008    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1009    generated_sync_expected: Option<TokenBitSet>,
1010    int_members: BTreeMap<usize, i64>,
1011    rule_context_stack: Vec<RuleContextFrame>,
1012    rule_context_version: usize,
1013    prediction_context_cache: Option<CachedPredictionContext>,
1014    left_recursive_caller_overlap_cache:
1015        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1016    pending_invoking_states: Vec<isize>,
1017    precedence_stack: Vec<i32>,
1018    /// Predicate side effects are observable in a few target-template tests;
1019    /// speculative recognition may revisit the same coordinate, so replay it
1020    /// once per parser instance.
1021    invoked_predicates: Vec<(usize, usize)>,
1022    /// Bail error strategy: the first syntax error aborts the parse instead of
1023    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1024    /// through `set_error_handler(BailErrorStrategy::new())`.
1025    bail_on_error: bool,
1026    /// How to evaluate predicate coordinates missing from the active
1027    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1028    unknown_predicate_policy: UnknownSemanticPolicy,
1029    /// Unknown predicate coordinates evaluated by the current parse, recorded
1030    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1031    unknown_predicate_hits: Vec<(usize, usize)>,
1032    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1033    /// that no hook handled, recorded so a generated `hook`/error-disposed
1034    /// action fails loud instead of being silently dropped. Keyed by
1035    /// `(rule_index, source_state)`.
1036    unhandled_action_hits: Vec<(usize, usize)>,
1037    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1038    /// hot rule-transition checks to a vector lookup after the first visit
1039    /// while the thread-local shared ATN cache still owns the cross-parse
1040    /// computed value.
1041    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1042    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1043    /// epsilon-reachable consuming transition and shows up as a hot loop in
1044    /// `next_recovery_context` and recovery diagnostics on long inputs.
1045    /// Keying on `state_number` and sharing the result through `Rc` removes
1046    /// repeated DFS plus per-call `BTreeSet` allocations.
1047    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1048    /// Same expected-symbol cache as a bitset for generated parser sync.
1049    /// Successful parses only need `contains` and union; keeping that path out
1050    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1051    /// check and defers deterministic formatting to diagnostics.
1052    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1053    /// Per-state cache for whether a return state can finish its owning rule
1054    /// without consuming more input. Generated-parser sync uses this to walk
1055    /// parent prediction contexts for nullable exits without paying repeated
1056    /// epsilon-closure searches on every loop or optional decision.
1057    rule_stop_reach_cache: Vec<Option<bool>>,
1058    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1059    /// threads the same epsilon-recovery context through hundreds of follow
1060    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1061    /// reference bump and lets the memo key hash by pointer.
1062    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1063    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1064    /// touch a given decision state still pay no upfront cost; once cached,
1065    /// the recognizer prunes alternatives whose look-1 cannot accept the
1066    /// current lookahead, letting common SLL decisions reduce to a single
1067    /// transition walk instead of a full speculative fan-out.
1068    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1069    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1070    /// Each multi-trans visit asks "given this decision state and this
1071    /// lookahead token, which alt do I commit to?" Hitting this cache
1072    /// turns the question into a hashmap probe instead of re-scanning
1073    /// the decision's per-transition FIRST sets every visit.
1074    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1075    /// Per-parse cache for whether an ATN state can reach itself without
1076    /// consuming input. Only those states need the recursive recognizer's
1077    /// `(state, token-index)` cycle guard.
1078    empty_cycle_cache: Vec<Option<bool>>,
1079    /// Probe state for deciding whether clean-pass one-outcome memo entries
1080    /// are worth storing for the current parse.
1081    single_outcome_memo_mode: SingleOutcomeMemoMode,
1082    single_outcome_probe_seen: FxHashSet<FastRecognizeKey>,
1083    single_outcome_probe_samples: usize,
1084    single_outcome_probe_repeats: usize,
1085    /// Empty recovery-symbols singleton used as the default at rule entry and
1086    /// after token consumption.
1087    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1088    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1089    /// prefilter trims speculative rule calls whose called rule cannot
1090    /// match the current lookahead, but it also bypasses single-token
1091    /// insertion / deletion recovery that ANTLR runs at the rule's first
1092    /// consuming transition. `parse_atn_rule` flips this off and retries
1093    /// when the first pass produces no clean outcome so the runtime can
1094    /// repair inputs the reference parser would have repaired.
1095    fast_first_set_prefilter: bool,
1096    /// Whether the fast recognizer should explore parser error-recovery paths.
1097    /// Public rule parsing starts with this disabled for the common valid-input
1098    /// path and enables it only for the retry that needs ANTLR-style repairs.
1099    fast_recovery_enabled: bool,
1100    /// Whether the fast recognizer should record terminal-token nodes while
1101    /// speculating. Clean valid-input parsing can reconstruct terminals from
1102    /// selected rule spans after recognition, avoiding many speculative `Rc`
1103    /// nodes that are thrown away with losing paths.
1104    fast_token_nodes_enabled: bool,
1105}
1106
1107/// Rollback marker for speculative generated parser paths.
1108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1109pub struct GeneratedDiagnosticsCheckpoint {
1110    diagnostics_len: usize,
1111    syntax_errors: usize,
1112}
1113
1114#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1115struct RuleContextFrame {
1116    rule_index: usize,
1117    invoking_state: isize,
1118}
1119
1120#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1121struct RecognizeOutcome {
1122    index: usize,
1123    consumed_eof: bool,
1124    alt_number: usize,
1125    member_values: BTreeMap<usize, i64>,
1126    return_values: BTreeMap<String, i64>,
1127    diagnostics: Vec<ParserDiagnostic>,
1128    decisions: Vec<usize>,
1129    actions: Vec<ParserAction>,
1130    nodes: Vec<RecognizedNode>,
1131}
1132
1133#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1134enum RecognizedNode {
1135    Token {
1136        index: usize,
1137    },
1138    ErrorToken {
1139        index: usize,
1140    },
1141    MissingToken {
1142        token_type: i32,
1143        at_index: usize,
1144        text: String,
1145    },
1146    Rule {
1147        rule_index: usize,
1148        invoking_state: isize,
1149        alt_number: usize,
1150        start_index: usize,
1151        stop_index: Option<usize>,
1152        return_values: BTreeMap<String, i64>,
1153        children: Vec<Self>,
1154    },
1155    LeftRecursiveBoundary {
1156        rule_index: usize,
1157    },
1158}
1159
1160#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1161struct FastRecognizeOutcome {
1162    index: usize,
1163    consumed_eof: bool,
1164    diagnostics: FastDiagnostics,
1165    /// Speculative parse-tree fragment built up as the recognizer climbs.
1166    /// The list is held as a persistent cons-list of `Rc`-wrapped nodes so
1167    /// prepending while chaining recognition outcomes is `O(1)` and cloning
1168    /// an outcome (memo lookup, dedup, or when fanning a child's tree out
1169    /// to every follow outcome) only bumps a reference count rather than
1170    /// deep-copying. On left-recursive grammars the unfolded list can carry
1171    /// thousands of nodes per speculative path; without the persistent-list
1172    /// shape recognition becomes super-linear in path length.
1173    nodes: NodeList,
1174}
1175
1176#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
1177#[allow(clippy::box_collection)]
1178struct FastDiagnostics(Option<Box<Vec<ParserDiagnostic>>>);
1179
1180impl FastDiagnostics {
1181    const fn new() -> Self {
1182        Self(None)
1183    }
1184
1185    #[cfg(test)]
1186    fn from_vec(diagnostics: Vec<ParserDiagnostic>) -> Self {
1187        if diagnostics.is_empty() {
1188            Self::new()
1189        } else {
1190            Self(Some(Box::new(diagnostics)))
1191        }
1192    }
1193
1194    fn is_empty(&self) -> bool {
1195        self.0
1196            .as_ref()
1197            .is_none_or(|diagnostics| diagnostics.is_empty())
1198    }
1199
1200    fn as_slice(&self) -> &[ParserDiagnostic] {
1201        self.0.as_deref().map_or(&[], Vec::as_slice)
1202    }
1203
1204    fn insert(&mut self, index: usize, diagnostic: ParserDiagnostic) {
1205        self.0
1206            .get_or_insert_with(Box::default)
1207            .insert(index, diagnostic);
1208    }
1209
1210    fn append(&mut self, other: &mut Self) {
1211        if other.is_empty() {
1212            return;
1213        }
1214        self.0
1215            .get_or_insert_with(Box::default)
1216            .append(other.0.get_or_insert_with(Box::default));
1217        if other.is_empty() {
1218            other.0 = None;
1219        }
1220    }
1221}
1222
1223impl std::ops::Deref for FastDiagnostics {
1224    type Target = [ParserDiagnostic];
1225
1226    fn deref(&self) -> &Self::Target {
1227        self.as_slice()
1228    }
1229}
1230
1231/// Persistent cons-list of fast-recognizer nodes. The list keeps nodes in the
1232/// same head-first order as the original `Vec<FastRecognizedNode>` they
1233/// replaced. Shared tails across speculative outcomes amortize the cost of
1234/// chaining a child rule's nodes onto every follow outcome.
1235///
1236/// `One` is an inline single-element variant: most outcomes carry only one
1237/// node (a single token or a single rule wrapper), so storing that node
1238/// directly avoids allocating an `Rc<NodeList>` tail wrapper.
1239#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
1240enum NodeList {
1241    #[default]
1242    Empty,
1243    One(Rc<FastRecognizedNode>),
1244    Cons {
1245        head: Rc<FastRecognizedNode>,
1246        tail: Rc<Self>,
1247    },
1248}
1249
1250impl NodeList {
1251    /// Creates an empty list.
1252    const fn new() -> Self {
1253        Self::Empty
1254    }
1255
1256    /// Prepends `node` and returns the new list. Both shared tails and the
1257    /// new head are reference-counted so this is `O(1)`.
1258    fn cons(self, node: Rc<FastRecognizedNode>) -> Self {
1259        match self {
1260            Self::Empty => Self::One(node),
1261            existing @ (Self::One(_) | Self::Cons { .. }) => Self::Cons {
1262                head: node,
1263                tail: Rc::new(existing),
1264            },
1265        }
1266    }
1267
1268    /// In-place prepend that takes ownership of `self` via [`std::mem::take`]
1269    /// so existing call sites can keep using `&mut` access.
1270    fn prepend(&mut self, node: Rc<FastRecognizedNode>) {
1271        let owned = std::mem::take(self);
1272        *self = owned.cons(node);
1273    }
1274
1275    /// Materializes the list into a `Vec` in head-first order. Used at the
1276    /// boundaries that need random-access traversal (the public rule entry
1277    /// when building the final parse tree, and
1278    /// `fold_fast_left_recursive_boundaries`).
1279    fn to_vec(&self) -> Vec<Rc<FastRecognizedNode>> {
1280        let mut out = Vec::new();
1281        let mut cursor = self;
1282        loop {
1283            match cursor {
1284                Self::Empty => break,
1285                Self::One(node) => {
1286                    out.push(Rc::clone(node));
1287                    break;
1288                }
1289                Self::Cons { head, tail } => {
1290                    out.push(Rc::clone(head));
1291                    cursor = tail.as_ref();
1292                }
1293            }
1294        }
1295        out
1296    }
1297
1298    const fn iter(&self) -> NodeListIter<'_> {
1299        NodeListIter { cursor: self }
1300    }
1301
1302    fn len(&self) -> usize {
1303        self.iter().count()
1304    }
1305
1306    fn has_left_recursive_boundary(&self) -> bool {
1307        self.iter()
1308            .any(|node| fast_node_has_left_recursive_boundary(node.as_ref()))
1309    }
1310
1311    fn has_explicit_token_node(&self) -> bool {
1312        self.iter().any(|node| {
1313            matches!(
1314                node.as_ref(),
1315                FastRecognizedNode::Token { .. }
1316                    | FastRecognizedNode::ErrorToken { .. }
1317                    | FastRecognizedNode::MissingToken { .. }
1318            )
1319        })
1320    }
1321
1322    /// Builds a list from an already ordered vector.
1323    fn from_vec(nodes: Vec<Rc<FastRecognizedNode>>) -> Self {
1324        let mut list = Self::new();
1325        for node in nodes.into_iter().rev() {
1326            list.prepend(node);
1327        }
1328        list
1329    }
1330}
1331
1332struct NodeListIter<'a> {
1333    cursor: &'a NodeList,
1334}
1335
1336impl<'a> Iterator for NodeListIter<'a> {
1337    type Item = &'a Rc<FastRecognizedNode>;
1338
1339    fn next(&mut self) -> Option<Self::Item> {
1340        match self.cursor {
1341            NodeList::Empty => None,
1342            NodeList::One(node) => {
1343                self.cursor = &NodeList::Empty;
1344                Some(node)
1345            }
1346            NodeList::Cons { head, tail } => {
1347                self.cursor = tail.as_ref();
1348                Some(head)
1349            }
1350        }
1351    }
1352}
1353
1354/// Minimal parse-tree fragment retained by the fast recognizer so the public
1355/// rule entry can build nested rule contexts without paying for
1356/// action/decision bookkeeping.
1357#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1358enum FastRecognizedNode {
1359    Token {
1360        index: usize,
1361    },
1362    ErrorToken {
1363        index: usize,
1364    },
1365    MissingToken {
1366        token_type: i32,
1367        at_index: usize,
1368        text: String,
1369    },
1370    Rule {
1371        rule_index: usize,
1372        invoking_state: isize,
1373        start_index: usize,
1374        stop_index: Option<usize>,
1375        children: NodeList,
1376    },
1377    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1378    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1379    /// public rule entry hands the tree to the caller.
1380    LeftRecursiveBoundary {
1381        rule_index: usize,
1382    },
1383}
1384
1385#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1386struct ParserDiagnostic {
1387    line: usize,
1388    column: usize,
1389    message: String,
1390}
1391
1392#[derive(Clone, Debug, Default, Eq, PartialEq)]
1393struct ExpectedTokens {
1394    index: Option<usize>,
1395    symbols: BTreeSet<i32>,
1396    no_viable: Option<NoViableAlternative>,
1397}
1398
1399#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1400struct NoViableAlternative {
1401    start_index: usize,
1402    error_index: usize,
1403}
1404
1405impl ExpectedTokens {
1406    /// Records the expected symbols for the farthest token index reached by any
1407    /// failed ATN path.
1408    fn record_transition(&mut self, index: usize, transition: &Transition, max_token_type: i32) {
1409        let symbols = transition_expected_symbols(transition, max_token_type);
1410        match self.index {
1411            Some(current) if index < current => {}
1412            Some(current) if index == current => self.symbols.extend(symbols),
1413            _ => {
1414                self.index = Some(index);
1415                self.symbols = symbols;
1416            }
1417        }
1418    }
1419
1420    /// Records an ambiguous decision that failed after consuming a shared
1421    /// prefix, which ANTLR reports as `no viable alternative`.
1422    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
1423        match self.no_viable {
1424            Some(current) if error_index < current.error_index => {}
1425            _ => {
1426                self.no_viable = Some(NoViableAlternative {
1427                    start_index,
1428                    error_index,
1429                });
1430            }
1431        }
1432    }
1433}
1434
1435/// Compact token-type set for parser-internal FIRST/lookahead caches.
1436///
1437/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
1438/// but the hot recognizer path mostly needs `contains` and set union over
1439/// small token ids. A bitset avoids tree traversal and per-symbol allocation
1440/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
1441#[derive(Clone, Debug, Default, Eq, PartialEq)]
1442struct TokenBitSet {
1443    words: Vec<u64>,
1444}
1445
1446impl TokenBitSet {
1447    fn insert(&mut self, symbol: i32) {
1448        let Some(slot) = token_bit_slot(symbol) else {
1449            return;
1450        };
1451        let word = slot / u64::BITS as usize;
1452        if word >= self.words.len() {
1453            self.words.resize(word + 1, 0);
1454        }
1455        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
1456    }
1457
1458    fn extend_range(&mut self, start: i32, stop: i32) {
1459        let (start, stop) = if start <= stop {
1460            (start, stop)
1461        } else {
1462            (stop, start)
1463        };
1464        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
1465            self.insert(TOKEN_EOF);
1466        }
1467        let positive_start = start.max(1);
1468        if positive_start > stop {
1469            return;
1470        }
1471        let Some(start_slot) = token_bit_slot(positive_start) else {
1472            return;
1473        };
1474        let Some(stop_slot) = token_bit_slot(stop) else {
1475            return;
1476        };
1477        self.extend_slot_range(start_slot, stop_slot);
1478    }
1479
1480    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
1481        if start_slot > stop_slot {
1482            return;
1483        }
1484        let start_word = start_slot / u64::BITS as usize;
1485        let stop_word = stop_slot / u64::BITS as usize;
1486        if stop_word >= self.words.len() {
1487            self.words.resize(stop_word + 1, 0);
1488        }
1489        let start_offset = start_slot % u64::BITS as usize;
1490        let stop_offset = stop_slot % u64::BITS as usize;
1491        if start_word == stop_word {
1492            self.words[start_word] |=
1493                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
1494            return;
1495        }
1496        self.words[start_word] |= !0_u64 << start_offset;
1497        for word in &mut self.words[(start_word + 1)..stop_word] {
1498            *word = !0_u64;
1499        }
1500        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
1501    }
1502
1503    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
1504        for symbol in symbols {
1505            self.insert(symbol);
1506        }
1507    }
1508
1509    fn extend_from(&mut self, other: &Self) {
1510        if other.words.len() > self.words.len() {
1511            self.words.resize(other.words.len(), 0);
1512        }
1513        for (left, right) in self.words.iter_mut().zip(&other.words) {
1514            *left |= *right;
1515        }
1516    }
1517
1518    fn contains(&self, symbol: i32) -> bool {
1519        let Some(slot) = token_bit_slot(symbol) else {
1520            return false;
1521        };
1522        let word = slot / u64::BITS as usize;
1523        self.words
1524            .get(word)
1525            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
1526    }
1527
1528    fn is_empty(&self) -> bool {
1529        self.words.iter().all(|word| *word == 0)
1530    }
1531
1532    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
1533        for (word_index, word) in self.words.iter().copied().enumerate() {
1534            let mut bits = word;
1535            while bits != 0 {
1536                let bit = bits.trailing_zeros() as usize;
1537                if let Some(symbol) = token_bit_symbol(word_index * u64::BITS as usize + bit) {
1538                    target.insert(symbol);
1539                }
1540                bits &= bits - 1;
1541            }
1542        }
1543    }
1544
1545    fn to_btree_set(&self) -> BTreeSet<i32> {
1546        let mut out = BTreeSet::new();
1547        self.extend_btree_set(&mut out);
1548        out
1549    }
1550}
1551
1552fn token_bit_slot(symbol: i32) -> Option<usize> {
1553    if symbol == TOKEN_EOF {
1554        Some(0)
1555    } else if symbol > 0 {
1556        usize::try_from(symbol).ok()
1557    } else {
1558        None
1559    }
1560}
1561
1562fn token_bit_symbol(slot: usize) -> Option<i32> {
1563    if slot == 0 {
1564        Some(TOKEN_EOF)
1565    } else {
1566        i32::try_from(slot).ok()
1567    }
1568}
1569
1570/// Converts one consuming transition into the token types that would satisfy it
1571/// for diagnostic reporting.
1572fn transition_expected_symbols(transition: &Transition, max_token_type: i32) -> BTreeSet<i32> {
1573    let mut symbols = BTreeSet::new();
1574    match transition {
1575        Transition::Atom { label, .. } => {
1576            symbols.insert(*label);
1577        }
1578        Transition::Range { start, stop, .. } => {
1579            symbols.extend(*start..=*stop);
1580        }
1581        Transition::Set { set, .. } => {
1582            for (start, stop) in set.ranges() {
1583                symbols.extend(*start..=*stop);
1584            }
1585        }
1586        Transition::NotSet { set, .. } => {
1587            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
1588        }
1589        Transition::Wildcard { .. } => {
1590            symbols.extend(1..=max_token_type);
1591        }
1592        Transition::Epsilon { .. }
1593        | Transition::Rule { .. }
1594        | Transition::Predicate { .. }
1595        | Transition::Action { .. }
1596        | Transition::Precedence { .. } => {}
1597    }
1598    symbols
1599}
1600
1601fn transition_expected_token_set(transition: &Transition, max_token_type: i32) -> TokenBitSet {
1602    let mut symbols = TokenBitSet::default();
1603    match transition {
1604        Transition::Atom { label, .. } => {
1605            symbols.insert(*label);
1606        }
1607        Transition::Range { start, stop, .. } => {
1608            symbols.extend_range(*start, *stop);
1609        }
1610        Transition::Set { set, .. } => {
1611            for (start, stop) in set.ranges() {
1612                symbols.extend_range(*start, *stop);
1613            }
1614        }
1615        Transition::NotSet { set, .. } => {
1616            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
1617        }
1618        Transition::Wildcard { .. } => {
1619            symbols.extend_range(1, max_token_type);
1620        }
1621        Transition::Epsilon { .. }
1622        | Transition::Rule { .. }
1623        | Transition::Predicate { .. }
1624        | Transition::Action { .. }
1625        | Transition::Precedence { .. } => {}
1626    }
1627    symbols
1628}
1629
1630/// Returns the consuming-token expectations reachable from an ATN state through
1631/// epsilon transitions. Recovery diagnostics need this closure so alternatives
1632/// and loop exits report the same expectation set ANTLR users see.
1633fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
1634    let mut symbols = BTreeSet::new();
1635    let mut stack = vec![state_number];
1636    let mut visited = BTreeSet::new();
1637    while let Some(current) = stack.pop() {
1638        if !visited.insert(current) {
1639            continue;
1640        }
1641        let Some(state) = atn.state(current) else {
1642            continue;
1643        };
1644        for transition in &state.transitions {
1645            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
1646            if transition_symbols.is_empty() {
1647                if transition.is_epsilon() {
1648                    stack.push(transition.target());
1649                }
1650            } else {
1651                symbols.extend(transition_symbols);
1652            }
1653        }
1654    }
1655    symbols
1656}
1657
1658fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
1659    let mut symbols = TokenBitSet::default();
1660    let mut stack = vec![state_number];
1661    let mut visited = BTreeSet::new();
1662    while let Some(current) = stack.pop() {
1663        if !visited.insert(current) {
1664            continue;
1665        }
1666        let Some(state) = atn.state(current) else {
1667            continue;
1668        };
1669        for transition in &state.transitions {
1670            let transition_symbols =
1671                transition_expected_token_set(transition, atn.max_token_type());
1672            if transition_symbols.is_empty() {
1673                if transition.is_epsilon() {
1674                    stack.push(transition.target());
1675                }
1676            } else {
1677                symbols.extend_from(&transition_symbols);
1678            }
1679        }
1680    }
1681    symbols
1682}
1683
1684fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
1685    let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
1686        return false;
1687    };
1688    let Some(&stop_state) = atn.rule_to_stop_state().get(rule_index) else {
1689        return false;
1690    };
1691    epsilon_reaches_state(atn, state_number, stop_state)
1692}
1693
1694fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
1695    let mut stack = vec![start];
1696    let mut visited = BTreeSet::new();
1697    while let Some(current) = stack.pop() {
1698        if current == target {
1699            return true;
1700        }
1701        if !visited.insert(current) {
1702            continue;
1703        }
1704        let Some(state) = atn.state(current) else {
1705            continue;
1706        };
1707        stack.extend(
1708            state
1709                .transitions
1710                .iter()
1711                .filter(|transition| transition.is_epsilon())
1712                .map(Transition::target),
1713        );
1714    }
1715    false
1716}
1717
1718/// FIRST set for a rule entry plus whether the rule is nullable.
1719///
1720/// Walks epsilon, predicate, action, and rule-call transitions until it finds
1721/// a consuming transition or reaches the rule's stop state. Used by the fast
1722/// recognizer to skip rule alternatives whose first-consumed token cannot
1723/// possibly match the current lookahead.
1724#[derive(Clone, Debug, Default, Eq, PartialEq)]
1725struct FirstSet {
1726    symbols: TokenBitSet,
1727    nullable: bool,
1728}
1729
1730/// Per-parser cache of FIRST sets computed during recognition. The fast path
1731/// consults this on every speculative `Transition::Rule` encounter, so the
1732/// computation must amortize across all of those calls — the FIRST set is a
1733/// pure function of the ATN, not of the input position. Cached entries are
1734/// shared via `Rc` so the recognizer never deep-copies the underlying
1735/// `BTreeSet<i32>`.
1736type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
1737
1738// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
1739// and decision-lookahead entries are purely functions of the grammar's
1740// ATN, so caching across parses lets repeated parsing of the same grammar
1741// (the common case for a CLI tool or language server) avoid redoing the
1742// closure work. Generated parsers hand us a `&'static Atn` whose address
1743// is stable, which is what we hash on.
1744type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
1745
1746#[derive(Debug, Default)]
1747struct LeftRecursiveOperatorLookahead {
1748    unconditional_symbols: TokenBitSet,
1749    predicate_dependent_symbols: TokenBitSet,
1750}
1751
1752#[derive(Default)]
1753struct SharedAtnCache {
1754    first_set: FirstSetCache,
1755    decision_lookahead: DecisionLookaheadCache,
1756    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
1757    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
1758    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
1759    rule_stop_reach: FxHashMap<usize, bool>,
1760    observable_action_transitions: Option<bool>,
1761    predicate_transitions: Option<bool>,
1762}
1763
1764thread_local! {
1765    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
1766        RefCell::new(FxHashMap::default());
1767}
1768
1769/// Compound key for `SHARED_ATN_CACHES`.
1770///
1771/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
1772/// pointer identifies one grammar for the program's lifetime. For the
1773/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
1774/// the secondary fields below catch the pointer collision: a new grammar
1775/// would need to match all of `(states ptr, states len, max_token_type)` to
1776/// be mistaken for the dropped one. That combination changing under us
1777/// without a rebuild is implausible enough to treat as a bug; bundling them
1778/// into the key is otherwise a few extra bytes per lookup.
1779#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1780struct SharedAtnCacheKey {
1781    atn: usize,
1782    states: usize,
1783    state_count: usize,
1784    max_token_type: i32,
1785}
1786
1787impl SharedAtnCacheKey {
1788    fn for_atn(atn: &Atn) -> Self {
1789        Self {
1790            atn: std::ptr::from_ref::<Atn>(atn) as usize,
1791            states: atn.states().as_ptr() as usize,
1792            state_count: atn.states().len(),
1793            max_token_type: atn.max_token_type(),
1794        }
1795    }
1796}
1797
1798fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
1799    SHARED_ATN_CACHES.with(|cell| {
1800        let key = SharedAtnCacheKey::for_atn(atn);
1801        let mut map = cell.borrow_mut();
1802        let cache = map.entry(key).or_default();
1803        f(&mut cache.first_set)
1804    })
1805}
1806
1807fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
1808    SHARED_ATN_CACHES.with(|cell| {
1809        let key = SharedAtnCacheKey::for_atn(atn);
1810        let mut map = cell.borrow_mut();
1811        let cache = map.entry(key).or_default();
1812        f(cache)
1813    })
1814}
1815
1816/// Per-decision-state cached look-1 sets for each outgoing transition.
1817///
1818/// At a multi-alternative state, the recognizer would otherwise speculatively
1819/// walk every alternative even when only one can possibly accept the current
1820/// lookahead. Caching the look-1 set per transition lets us prune the
1821/// non-viable transitions before recursing — the same SLL prediction trick
1822/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
1823/// filter rather than a full DFA.
1824#[derive(Debug, Default)]
1825struct DecisionLookahead {
1826    transitions: Vec<TransitionLookSet>,
1827}
1828
1829/// Look-1 information for one outgoing transition.
1830///
1831/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
1832/// can reach the rule stop without consuming a token (e.g. an empty alt).
1833/// Nullable transitions cannot be pruned: they may still be the right path
1834/// when the lookahead consumes nothing further inside the current rule.
1835#[derive(Clone, Debug, Default)]
1836struct TransitionLookSet {
1837    symbols: TokenBitSet,
1838    nullable: bool,
1839}
1840
1841/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
1842/// rarely-touched fields keeps the recursive helpers below the function-arity
1843/// lint and lets every nested call thread the same cache and cycle guards.
1844struct FirstSetCtx<'a> {
1845    cache: &'a mut FirstSetCache,
1846    in_progress: BTreeSet<(usize, usize)>,
1847    hit_cycle: bool,
1848}
1849
1850/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
1851/// shared cache and tolerating recursive nullable rule chains. Mutually
1852/// recursive rules cannot stack-overflow because callers in flight are tracked
1853/// in `ctx.in_progress`; revisits return without recursing, and the partial
1854/// result is cached only when no cycle was detected during its computation.
1855///
1856/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
1857/// rule-call probes only pay a reference bump.
1858fn rule_first_set(
1859    atn: &Atn,
1860    target: usize,
1861    rule_stop_state: usize,
1862    cache: &mut FirstSetCache,
1863) -> Rc<FirstSet> {
1864    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
1865        return Rc::clone(cached);
1866    }
1867    let mut ctx = FirstSetCtx {
1868        cache,
1869        in_progress: BTreeSet::new(),
1870        hit_cycle: false,
1871    };
1872    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
1873}
1874
1875fn rule_first_set_cached(
1876    atn: &Atn,
1877    target: usize,
1878    rule_stop_state: usize,
1879    ctx: &mut FirstSetCtx<'_>,
1880) -> Rc<FirstSet> {
1881    let key = (target, rule_stop_state);
1882    if let Some(cached) = ctx.cache.get(&key) {
1883        return Rc::clone(cached);
1884    }
1885    if !ctx.in_progress.insert(key) {
1886        // Cycle: a caller above is already computing this entry. Return an
1887        // empty FIRST set; that caller's traversal supplies the contributions
1888        // from the rule's other alternatives.
1889        return Rc::new(FirstSet::default());
1890    }
1891    let saved_hit_cycle = ctx.hit_cycle;
1892    ctx.hit_cycle = false;
1893    let mut first = FirstSet::default();
1894    let mut visited = BTreeSet::new();
1895    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
1896    ctx.in_progress.remove(&key);
1897    let entry = Rc::new(first);
1898    if !ctx.hit_cycle {
1899        ctx.cache.insert(key, Rc::clone(&entry));
1900    }
1901    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
1902    entry
1903}
1904
1905/// Returns the look-1 set for traversing `transition` while still inside the
1906/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
1907/// prunes transitions whose look-1 cannot accept the current lookahead.
1908fn transition_first_set(
1909    atn: &Atn,
1910    transition: &Transition,
1911    rule_stop_state: usize,
1912    cache: &mut FirstSetCache,
1913) -> TransitionLookSet {
1914    match transition {
1915        Transition::Atom { label, .. } => {
1916            let mut symbols = TokenBitSet::default();
1917            symbols.insert(*label);
1918            TransitionLookSet {
1919                symbols,
1920                nullable: false,
1921            }
1922        }
1923        Transition::Range { start, stop, .. } => {
1924            let mut symbols = TokenBitSet::default();
1925            symbols.extend_range(*start, *stop);
1926            TransitionLookSet {
1927                symbols,
1928                nullable: false,
1929            }
1930        }
1931        Transition::Set { set, .. } => {
1932            let mut symbols = TokenBitSet::default();
1933            for (start, stop) in set.ranges() {
1934                symbols.extend_range(*start, *stop);
1935            }
1936            TransitionLookSet {
1937                symbols,
1938                nullable: false,
1939            }
1940        }
1941        Transition::NotSet { set, .. } => {
1942            let max = atn.max_token_type();
1943            let mut symbols = TokenBitSet::default();
1944            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
1945            TransitionLookSet {
1946                symbols,
1947                nullable: false,
1948            }
1949        }
1950        Transition::Wildcard { .. } => {
1951            let mut symbols = TokenBitSet::default();
1952            symbols.extend_range(1, atn.max_token_type());
1953            TransitionLookSet {
1954                symbols,
1955                nullable: false,
1956            }
1957        }
1958        Transition::Epsilon { target }
1959        | Transition::Action { target, .. }
1960        | Transition::Predicate { target, .. }
1961        | Transition::Precedence { target, .. } => {
1962            // Walk the closure starting at `target` until a consuming transition
1963            // is reached or the rule stop state is hit.
1964            let first = rule_first_set(atn, *target, rule_stop_state, cache);
1965            TransitionLookSet {
1966                symbols: first.symbols.clone(),
1967                nullable: first.nullable,
1968            }
1969        }
1970        Transition::Rule {
1971            target,
1972            rule_index,
1973            follow_state,
1974            ..
1975        } => {
1976            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
1977                return TransitionLookSet::default();
1978            };
1979            let child = rule_first_set(atn, *target, child_stop, cache);
1980            let mut symbols = child.symbols.clone();
1981            let nullable = if child.nullable {
1982                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
1983                symbols.extend_from(&follow.symbols);
1984                follow.nullable
1985            } else {
1986                false
1987            };
1988            TransitionLookSet { symbols, nullable }
1989        }
1990    }
1991}
1992
1993/// Reports whether `transition` can be pruned at a multi-alt state because
1994/// its cached look-1 cannot accept the current lookahead.
1995///
1996/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
1997/// Rule/Precedence) so consuming transitions still reach the
1998/// `matches`+recovery path that surfaces single-token deletion / insertion
1999/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
2000/// transition is pruned, its FIRST set is folded into `expected` so failed
2001/// parses produce the same `mismatched input ... expecting ...` diagnostic
2002/// the no-prefilter baseline would emit.
2003/// Returns the unique alt index (0-based) when `symbol` falls into exactly
2004/// one transition's FIRST set and no transition is nullable. Used as an
2005/// LL(1) commit point: when prediction is unambiguous from the lookahead
2006/// alone, the recursive recognizer can skip every other alt without paying
2007/// for the per-transition filter probe.
2008///
2009/// `None` signals the caller to fall back to per-transition lookahead
2010/// filtering. Returning `Some` for an alt whose transition cannot actually
2011/// match would prune the only viable parse path; this is why we require
2012/// strict disjointness *and* no nullable transitions in the decision.
2013fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
2014    let mut chosen: Option<usize> = None;
2015    for (index, transition) in entry.transitions.iter().enumerate() {
2016        if transition.nullable {
2017            return None;
2018        }
2019        if transition.symbols.contains(symbol) {
2020            if chosen.is_some() {
2021                return None;
2022            }
2023            chosen = Some(index);
2024        }
2025    }
2026    chosen
2027}
2028
2029/// Returns the unique greedy alt index (0-based) selected by the current
2030/// lookahead.
2031///
2032/// The shortcut is intentionally conservative around nullable exits. If the
2033/// current symbol can start a consuming alternative and an empty alternative is
2034/// also present, one-token lookahead is not enough to know whether the symbol
2035/// belongs to the current construct or to its caller's follow set. `None`
2036/// signals the caller to fall back to adaptive prediction.
2037fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
2038    let mut matching_non_nullable_alt = None;
2039    let mut nullable_alt = None;
2040    for (index, transition) in entry.transitions.iter().enumerate() {
2041        if transition.nullable {
2042            if nullable_alt.is_some() {
2043                return None;
2044            }
2045            nullable_alt = Some(index);
2046        }
2047        if transition.symbols.contains(symbol) {
2048            if transition.nullable {
2049                continue;
2050            }
2051            if matching_non_nullable_alt.is_some() {
2052                return None;
2053            }
2054            matching_non_nullable_alt = Some(index);
2055        }
2056    }
2057    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
2058        return None;
2059    }
2060    if non_greedy {
2061        nullable_alt.or(matching_non_nullable_alt)
2062    } else {
2063        matching_non_nullable_alt.or(nullable_alt)
2064    }
2065}
2066
2067fn should_skip_via_lookahead(
2068    transition: &Transition,
2069    transition_index: usize,
2070    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
2071    index: usize,
2072    record_expected: bool,
2073    expected: &mut ExpectedTokens,
2074) -> bool {
2075    let prune_non_consuming = matches!(
2076        transition,
2077        Transition::Epsilon { .. }
2078            | Transition::Action { .. }
2079            | Transition::Predicate { .. }
2080            | Transition::Rule { .. }
2081            | Transition::Precedence { .. }
2082    );
2083    if !prune_non_consuming {
2084        return false;
2085    }
2086    let Some((symbol, entry)) = lookahead_filter else {
2087        return false;
2088    };
2089    let Some(set) = entry.transitions.get(transition_index) else {
2090        return false;
2091    };
2092    if set.symbols.contains(*symbol) || set.nullable {
2093        return false;
2094    }
2095    if record_expected && !set.symbols.is_empty() {
2096        record_pruned_transition_expected(set, index, expected);
2097    }
2098    true
2099}
2100
2101fn should_skip_rule_via_first_set(
2102    first: &FirstSet,
2103    symbol: i32,
2104    record_expected: bool,
2105    index: usize,
2106    expected: &mut ExpectedTokens,
2107) -> bool {
2108    if first.nullable || first.symbols.contains(symbol) {
2109        return false;
2110    }
2111    if record_expected && !first.symbols.is_empty() {
2112        record_token_bit_expected(&first.symbols, index, expected);
2113    }
2114    true
2115}
2116
2117fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
2118    match expected.index {
2119        Some(current) if index < current => {}
2120        Some(current) if index == current => {
2121            symbols.extend_btree_set(&mut expected.symbols);
2122        }
2123        _ => {
2124            expected.index = Some(index);
2125            expected.symbols = symbols.to_btree_set();
2126        }
2127    }
2128}
2129
2130/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
2131fn record_pruned_transition_expected(
2132    set: &TransitionLookSet,
2133    index: usize,
2134    expected: &mut ExpectedTokens,
2135) {
2136    match expected.index {
2137        Some(current) if index < current => {}
2138        Some(current) if index == current => {
2139            set.symbols.extend_btree_set(&mut expected.symbols);
2140        }
2141        _ => {
2142            expected.index = Some(index);
2143            expected.symbols = set.symbols.to_btree_set();
2144        }
2145    }
2146}
2147
2148fn rule_first_set_inner(
2149    atn: &Atn,
2150    state_number: usize,
2151    rule_stop_state: usize,
2152    ctx: &mut FirstSetCtx<'_>,
2153    visited: &mut BTreeSet<usize>,
2154    first: &mut FirstSet,
2155) {
2156    if !visited.insert(state_number) {
2157        return;
2158    }
2159    if state_number == rule_stop_state {
2160        first.nullable = true;
2161        return;
2162    }
2163    let Some(state) = atn.state(state_number) else {
2164        return;
2165    };
2166    for transition in &state.transitions {
2167        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2168        if !transition_symbols.is_empty() {
2169            first.symbols.extend_iter(transition_symbols);
2170            continue;
2171        }
2172        match transition {
2173            Transition::Epsilon { target }
2174            | Transition::Action { target, .. }
2175            | Transition::Predicate { target, .. }
2176            | Transition::Precedence { target, .. } => {
2177                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
2178            }
2179            Transition::Rule {
2180                target,
2181                rule_index,
2182                follow_state,
2183                ..
2184            } => {
2185                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
2186                    continue;
2187                };
2188                let child_key = (*target, child_stop);
2189                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
2190                    ctx.hit_cycle = true;
2191                }
2192                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
2193                first.symbols.extend_from(&child.symbols);
2194                if child.nullable {
2195                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
2196                }
2197            }
2198            Transition::Atom { .. }
2199            | Transition::Range { .. }
2200            | Transition::Set { .. }
2201            | Transition::NotSet { .. }
2202            | Transition::Wildcard { .. } => {}
2203        }
2204    }
2205}
2206
2207/// Returns token types that can resume parsing from `state_number` after a
2208/// failed child rule, following rule calls as well as epsilon transitions.
2209fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
2210    let mut symbols = BTreeSet::new();
2211    state_sync_symbols_inner(
2212        atn,
2213        state_number,
2214        stop_state,
2215        &mut BTreeSet::new(),
2216        &mut symbols,
2217    );
2218    symbols
2219}
2220
2221/// Walks epsilon-like continuations from a parent follow state until it finds
2222/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
2223fn state_sync_symbols_inner(
2224    atn: &Atn,
2225    state_number: usize,
2226    stop_state: usize,
2227    visited: &mut BTreeSet<usize>,
2228    symbols: &mut BTreeSet<i32>,
2229) {
2230    if !visited.insert(state_number) {
2231        return;
2232    }
2233    if state_number == stop_state {
2234        symbols.insert(TOKEN_EOF);
2235        return;
2236    }
2237    let Some(state) = atn.state(state_number) else {
2238        return;
2239    };
2240    for transition in &state.transitions {
2241        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2242        if transition_symbols.is_empty() {
2243            match transition {
2244                Transition::Rule { target, .. }
2245                | Transition::Epsilon { target }
2246                | Transition::Action { target, .. }
2247                | Transition::Predicate { target, .. }
2248                | Transition::Precedence { target, .. } => {
2249                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
2250                }
2251                Transition::Atom { .. }
2252                | Transition::Range { .. }
2253                | Transition::Set { .. }
2254                | Transition::NotSet { .. }
2255                | Transition::Wildcard { .. } => {}
2256            }
2257        } else {
2258            symbols.extend(transition_symbols);
2259        }
2260    }
2261}
2262
2263#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
2264enum OperatorSymbolReachability {
2265    #[default]
2266    None,
2267    PredicateDependent,
2268    Unconditional,
2269}
2270
2271#[derive(Clone, Copy)]
2272struct OperatorReachabilityRequest {
2273    symbol: i32,
2274    precedence: i32,
2275    predicate_dependent: bool,
2276}
2277
2278struct NullablePrecedenceCtx {
2279    cache: FxHashMap<(usize, usize, i32, bool), bool>,
2280    in_progress: BTreeSet<(usize, usize, i32, bool)>,
2281    hit_cycle: bool,
2282}
2283
2284fn state_is_nullable_with_precedence(
2285    atn: &Atn,
2286    state_number: usize,
2287    stop_state_number: usize,
2288    precedence: i32,
2289    allow_predicates: bool,
2290    ctx: &mut NullablePrecedenceCtx,
2291) -> bool {
2292    let saved_hit_cycle = ctx.hit_cycle;
2293    ctx.hit_cycle = false;
2294    let nullable = state_is_nullable_with_precedence_cached(
2295        atn,
2296        state_number,
2297        stop_state_number,
2298        precedence,
2299        allow_predicates,
2300        ctx,
2301    );
2302    ctx.hit_cycle = saved_hit_cycle;
2303    nullable
2304}
2305
2306fn state_is_nullable_with_precedence_cached(
2307    atn: &Atn,
2308    state_number: usize,
2309    stop_state_number: usize,
2310    precedence: i32,
2311    allow_predicates: bool,
2312    ctx: &mut NullablePrecedenceCtx,
2313) -> bool {
2314    if state_number == stop_state_number {
2315        return true;
2316    }
2317    let key = (
2318        state_number,
2319        stop_state_number,
2320        precedence,
2321        allow_predicates,
2322    );
2323    if let Some(cached) = ctx.cache.get(&key) {
2324        return *cached;
2325    }
2326    if !ctx.in_progress.insert(key) {
2327        ctx.hit_cycle = true;
2328        return false;
2329    }
2330    let saved_hit_cycle = ctx.hit_cycle;
2331    ctx.hit_cycle = false;
2332    let nullable = atn.state(state_number).is_some_and(|state| {
2333        state.transitions.iter().any(|transition| match transition {
2334            Transition::Rule {
2335                target,
2336                rule_index,
2337                follow_state,
2338                precedence: rule_precedence,
2339            } => {
2340                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
2341                    return false;
2342                };
2343                state_is_nullable_with_precedence_cached(
2344                    atn,
2345                    *target,
2346                    child_stop,
2347                    *rule_precedence,
2348                    allow_predicates,
2349                    ctx,
2350                ) && state_is_nullable_with_precedence_cached(
2351                    atn,
2352                    *follow_state,
2353                    stop_state_number,
2354                    precedence,
2355                    allow_predicates,
2356                    ctx,
2357                )
2358            }
2359            Transition::Epsilon { target } | Transition::Action { target, .. } => {
2360                state_is_nullable_with_precedence_cached(
2361                    atn,
2362                    *target,
2363                    stop_state_number,
2364                    precedence,
2365                    allow_predicates,
2366                    ctx,
2367                )
2368            }
2369            Transition::Predicate { target, .. } if allow_predicates => {
2370                state_is_nullable_with_precedence_cached(
2371                    atn,
2372                    *target,
2373                    stop_state_number,
2374                    precedence,
2375                    allow_predicates,
2376                    ctx,
2377                )
2378            }
2379            Transition::Precedence {
2380                target,
2381                precedence: transition_precedence,
2382            } if *transition_precedence >= precedence => state_is_nullable_with_precedence_cached(
2383                atn,
2384                *target,
2385                stop_state_number,
2386                precedence,
2387                allow_predicates,
2388                ctx,
2389            ),
2390            Transition::Atom { .. }
2391            | Transition::Range { .. }
2392            | Transition::Set { .. }
2393            | Transition::NotSet { .. }
2394            | Transition::Wildcard { .. }
2395            | Transition::Predicate { .. }
2396            | Transition::Precedence { .. } => false,
2397        })
2398    });
2399    ctx.in_progress.remove(&key);
2400    if !ctx.hit_cycle {
2401        ctx.cache.insert(key, nullable);
2402    }
2403    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
2404    nullable
2405}
2406
2407fn state_can_reach_symbol_with_precedence(
2408    atn: &Atn,
2409    state_number: usize,
2410    request: OperatorReachabilityRequest,
2411    nullable_ctx: &mut NullablePrecedenceCtx,
2412    visited: &mut BTreeSet<(usize, i32, bool)>,
2413) -> OperatorSymbolReachability {
2414    if !visited.insert((
2415        state_number,
2416        request.precedence,
2417        request.predicate_dependent,
2418    )) {
2419        return OperatorSymbolReachability::None;
2420    }
2421    let Some(state) = atn.state(state_number) else {
2422        return OperatorSymbolReachability::None;
2423    };
2424    let mut reachability = OperatorSymbolReachability::None;
2425    for transition in &state.transitions {
2426        if transition.matches(request.symbol, 1, atn.max_token_type()) {
2427            if request.predicate_dependent {
2428                reachability = OperatorSymbolReachability::PredicateDependent;
2429                continue;
2430            }
2431            return OperatorSymbolReachability::Unconditional;
2432        }
2433        let transition_reachability = match transition {
2434            Transition::Rule {
2435                target,
2436                rule_index,
2437                follow_state,
2438                precedence: rule_precedence,
2439            } => {
2440                let mut result = state_can_reach_symbol_with_precedence(
2441                    atn,
2442                    *target,
2443                    OperatorReachabilityRequest {
2444                        precedence: *rule_precedence,
2445                        ..request
2446                    },
2447                    nullable_ctx,
2448                    visited,
2449                );
2450                if result == OperatorSymbolReachability::Unconditional {
2451                    return result;
2452                }
2453                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
2454                    continue;
2455                };
2456                if state_is_nullable_with_precedence(
2457                    atn,
2458                    *target,
2459                    child_stop,
2460                    *rule_precedence,
2461                    true,
2462                    nullable_ctx,
2463                ) {
2464                    let child_predicate_dependent = request.predicate_dependent
2465                        || !state_is_nullable_with_precedence(
2466                            atn,
2467                            *target,
2468                            child_stop,
2469                            *rule_precedence,
2470                            false,
2471                            nullable_ctx,
2472                        );
2473                    result = result.max(state_can_reach_symbol_with_precedence(
2474                        atn,
2475                        *follow_state,
2476                        OperatorReachabilityRequest {
2477                            predicate_dependent: child_predicate_dependent,
2478                            ..request
2479                        },
2480                        nullable_ctx,
2481                        visited,
2482                    ));
2483                }
2484                result
2485            }
2486            Transition::Epsilon { target }
2487            | Transition::Action { target, .. }
2488            | Transition::Precedence { target, .. } => {
2489                if matches!(
2490                    transition,
2491                    Transition::Precedence {
2492                        precedence: transition_precedence,
2493                        ..
2494                    } if *transition_precedence < request.precedence
2495                ) {
2496                    continue;
2497                }
2498                state_can_reach_symbol_with_precedence(atn, *target, request, nullable_ctx, visited)
2499            }
2500            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
2501                atn,
2502                *target,
2503                OperatorReachabilityRequest {
2504                    predicate_dependent: true,
2505                    ..request
2506                },
2507                nullable_ctx,
2508                visited,
2509            ),
2510            Transition::Atom { .. }
2511            | Transition::Range { .. }
2512            | Transition::Set { .. }
2513            | Transition::NotSet { .. }
2514            | Transition::Wildcard { .. } => OperatorSymbolReachability::None,
2515        };
2516        if transition_reachability == OperatorSymbolReachability::Unconditional {
2517            return transition_reachability;
2518        }
2519        reachability = reachability.max(transition_reachability);
2520    }
2521    reachability
2522}
2523
2524fn left_recursive_operator_lookahead(
2525    atn: &Atn,
2526    state_number: usize,
2527    precedence: i32,
2528) -> LeftRecursiveOperatorLookahead {
2529    let Some(state) = atn.state(state_number) else {
2530        return LeftRecursiveOperatorLookahead::default();
2531    };
2532    let mut lookahead = LeftRecursiveOperatorLookahead::default();
2533    let mut nullable_ctx = NullablePrecedenceCtx {
2534        cache: FxHashMap::default(),
2535        in_progress: BTreeSet::new(),
2536        hit_cycle: false,
2537    };
2538    for transition in &state.transitions {
2539        let target = transition.target();
2540        if atn
2541            .state(target)
2542            .is_some_and(|state| state.kind == AtnStateKind::LoopEnd)
2543        {
2544            continue;
2545        }
2546        for symbol in 1..=atn.max_token_type() {
2547            match state_can_reach_symbol_with_precedence(
2548                atn,
2549                target,
2550                OperatorReachabilityRequest {
2551                    symbol,
2552                    precedence,
2553                    predicate_dependent: false,
2554                },
2555                &mut nullable_ctx,
2556                &mut BTreeSet::new(),
2557            ) {
2558                OperatorSymbolReachability::Unconditional => {
2559                    lookahead.unconditional_symbols.insert(symbol);
2560                }
2561                OperatorSymbolReachability::PredicateDependent => {
2562                    lookahead.predicate_dependent_symbols.insert(symbol);
2563                }
2564                OperatorSymbolReachability::None => {}
2565            }
2566        }
2567    }
2568    lookahead
2569}
2570
2571#[derive(Debug, Default)]
2572struct StateBeforeStopLookahead {
2573    symbols: TokenBitSet,
2574    reaches_context_boundary: bool,
2575}
2576
2577fn state_before_stop_lookahead(
2578    atn: &Atn,
2579    state_number: usize,
2580    stop_state_number: usize,
2581) -> Rc<StateBeforeStopLookahead> {
2582    with_shared_atn_caches(atn, |cache| {
2583        let key = (state_number, stop_state_number);
2584        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
2585            return Rc::clone(cached);
2586        }
2587        let mut lookahead = StateBeforeStopLookahead::default();
2588        state_before_stop_lookahead_inner(
2589            atn,
2590            state_number,
2591            stop_state_number,
2592            &mut BTreeSet::new(),
2593            &mut cache.first_set,
2594            &mut lookahead,
2595        );
2596        let lookahead = Rc::new(lookahead);
2597        cache
2598            .state_before_stop_lookahead
2599            .insert(key, Rc::clone(&lookahead));
2600        lookahead
2601    })
2602}
2603
2604fn state_before_stop_lookahead_inner(
2605    atn: &Atn,
2606    state_number: usize,
2607    stop_state_number: usize,
2608    visited: &mut BTreeSet<usize>,
2609    first_set_cache: &mut FirstSetCache,
2610    lookahead: &mut StateBeforeStopLookahead,
2611) {
2612    if state_number == stop_state_number {
2613        lookahead.reaches_context_boundary = true;
2614        return;
2615    }
2616    if !visited.insert(state_number) {
2617        return;
2618    }
2619    let Some(state) = atn.state(state_number) else {
2620        return;
2621    };
2622    if state.kind == AtnStateKind::RuleStop {
2623        lookahead.reaches_context_boundary = true;
2624        return;
2625    }
2626    for transition in &state.transitions {
2627        match transition {
2628            Transition::Epsilon { target }
2629            | Transition::Action { target, .. }
2630            | Transition::Predicate { target, .. }
2631            | Transition::Precedence { target, .. } => {
2632                state_before_stop_lookahead_inner(
2633                    atn,
2634                    *target,
2635                    stop_state_number,
2636                    visited,
2637                    first_set_cache,
2638                    lookahead,
2639                );
2640            }
2641            Transition::Rule {
2642                target,
2643                rule_index,
2644                follow_state,
2645                ..
2646            } => {
2647                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
2648                    continue;
2649                };
2650                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
2651                lookahead.symbols.extend_from(&child.symbols);
2652                if child.nullable {
2653                    state_before_stop_lookahead_inner(
2654                        atn,
2655                        *follow_state,
2656                        stop_state_number,
2657                        visited,
2658                        first_set_cache,
2659                        lookahead,
2660                    );
2661                }
2662            }
2663            Transition::Atom { .. }
2664            | Transition::Range { .. }
2665            | Transition::Set { .. }
2666            | Transition::NotSet { .. }
2667            | Transition::Wildcard { .. } => {
2668                lookahead.symbols.extend_iter(transition_expected_symbols(
2669                    transition,
2670                    atn.max_token_type(),
2671                ));
2672            }
2673        }
2674    }
2675}
2676
2677fn context_can_match_symbol_before_state(
2678    atn: &Atn,
2679    context: &PredictionContext,
2680    stop_state_number: usize,
2681    symbol: i32,
2682) -> bool {
2683    (0..context.len()).any(|index| {
2684        context.return_state(index).is_some_and(|return_state| {
2685            if return_state == EMPTY_RETURN_STATE {
2686                return false;
2687            }
2688            let parent = context
2689                .parent(index)
2690                .unwrap_or_else(PredictionContext::empty);
2691            let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
2692            lookahead.symbols.contains(symbol)
2693                || (lookahead.reaches_context_boundary
2694                    && context_can_match_symbol_before_state(
2695                        atn,
2696                        &parent,
2697                        stop_state_number,
2698                        symbol,
2699                    ))
2700        })
2701    })
2702}
2703
2704/// Carries recovery expectations and their restart state through epsilon-only
2705/// paths. ANTLR can report and repair at the decision state even when the
2706/// failed consuming transition is nested under block or loop epsilon edges.
2707fn next_recovery_context(
2708    atn: &Atn,
2709    state: &AtnState,
2710    inherited: &BTreeSet<i32>,
2711    inherited_state: Option<usize>,
2712) -> (BTreeSet<i32>, Option<usize>) {
2713    let state_symbols = state_expected_symbols(atn, state.state_number);
2714    if state.transitions.len() > 1 && !state_symbols.is_empty() {
2715        let mut symbols = state_symbols;
2716        symbols.extend(inherited.iter().copied());
2717        return (symbols, Some(state.state_number));
2718    }
2719    (inherited.clone(), inherited_state)
2720}
2721
2722fn recovery_expected_symbols(
2723    atn: &Atn,
2724    state_number: usize,
2725    inherited: &BTreeSet<i32>,
2726) -> BTreeSet<i32> {
2727    let mut symbols = state_expected_symbols(atn, state_number);
2728    symbols.extend(inherited.iter().copied());
2729    symbols
2730}
2731
2732/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
2733/// parser's cached state-expected-symbols sets and the inherited `Rc`
2734/// without copying when the state cannot widen recovery.
2735fn fast_next_recovery_context<S, H>(
2736    parser: &mut BaseParser<S, H>,
2737    atn: &Atn,
2738    state: &AtnState,
2739    inherited: &Rc<BTreeSet<i32>>,
2740    inherited_state: Option<usize>,
2741) -> (Rc<BTreeSet<i32>>, Option<usize>)
2742where
2743    S: TokenSource,
2744    H: SemanticHooks,
2745{
2746    if state.transitions.len() <= 1 {
2747        return (Rc::clone(inherited), inherited_state);
2748    }
2749    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number);
2750    if state_symbols.is_empty() {
2751        return (Rc::clone(inherited), inherited_state);
2752    }
2753    if inherited.is_empty() {
2754        return (state_symbols, Some(state.state_number));
2755    }
2756    if Rc::ptr_eq(&state_symbols, inherited) {
2757        return (state_symbols, Some(state.state_number));
2758    }
2759    let mut combined = (*state_symbols).clone();
2760    combined.extend(inherited.iter().copied());
2761    (
2762        parser.intern_recovery_symbols(combined),
2763        Some(state.state_number),
2764    )
2765}
2766
2767/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
2768/// cached state-expected-symbols and avoids cloning when no widening is
2769/// needed.
2770fn fast_recovery_expected_symbols<S, H>(
2771    parser: &mut BaseParser<S, H>,
2772    atn: &Atn,
2773    state_number: usize,
2774    inherited: &Rc<BTreeSet<i32>>,
2775) -> Rc<BTreeSet<i32>>
2776where
2777    S: TokenSource,
2778    H: SemanticHooks,
2779{
2780    let cached = parser.cached_state_expected_symbols(atn, state_number);
2781    if inherited.is_empty() {
2782        return cached;
2783    }
2784    if cached.is_empty() {
2785        return Rc::clone(inherited);
2786    }
2787    if Rc::ptr_eq(&cached, inherited) {
2788        return cached;
2789    }
2790    let mut combined = (*cached).clone();
2791    combined.extend(inherited.iter().copied());
2792    parser.intern_recovery_symbols(combined)
2793}
2794
2795struct ParserTableSemCtx<'a> {
2796    member_values: &'a mut BTreeMap<usize, i64>,
2797    return_values: &'a mut BTreeMap<String, i64>,
2798}
2799
2800impl semir::PredContext for ParserTableSemCtx<'_> {
2801    fn la(&mut self, _offset: isize) -> i64 {
2802        i64::from(TOKEN_EOF)
2803    }
2804
2805    fn token_text(&mut self, _offset: isize) -> Option<&str> {
2806        None
2807    }
2808
2809    fn token_index_adjacent(&mut self) -> bool {
2810        false
2811    }
2812
2813    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
2814        None
2815    }
2816
2817    fn member(&self, member: usize) -> Option<i64> {
2818        Some(self.member_values.get(&member).copied().unwrap_or_default())
2819    }
2820
2821    fn local_arg(&self) -> Option<i64> {
2822        None
2823    }
2824
2825    fn column(&self) -> Option<i64> {
2826        None
2827    }
2828
2829    fn token_start_column(&self) -> Option<i64> {
2830        None
2831    }
2832
2833    fn token_text_so_far(&self) -> Option<String> {
2834        None
2835    }
2836
2837    fn hook(&mut self, _hook: HookId) -> bool {
2838        false
2839    }
2840}
2841
2842impl semir::ActContext for ParserTableSemCtx<'_> {
2843    fn set_member(&mut self, member: usize, value: i64) {
2844        self.member_values.insert(member, value);
2845    }
2846
2847    fn set_return(&mut self, name: &str, value: i64) {
2848        self.return_values.insert(name.to_owned(), value);
2849    }
2850
2851    fn action_hook(&mut self, _hook: HookId) {}
2852}
2853
2854/// Applies generated integer-member side effects to one speculative path.
2855fn apply_member_actions(
2856    source_state: usize,
2857    actions: &[ParserMemberAction],
2858    semantics: Option<&ParserSemantics>,
2859    values: &mut BTreeMap<usize, i64>,
2860) {
2861    for action in actions
2862        .iter()
2863        .filter(|action| action.source_state == source_state)
2864    {
2865        *values.entry(action.member).or_default() += action.delta;
2866    }
2867    let Some(semantics) = semantics else {
2868        return;
2869    };
2870    let mut return_values = BTreeMap::new();
2871    let mut ctx = ParserTableSemCtx {
2872        member_values: values,
2873        return_values: &mut return_values,
2874    };
2875    for action in semantics
2876        .actions
2877        .iter()
2878        .filter(|action| action.source_state == source_state && action.speculative)
2879    {
2880        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
2881    }
2882}
2883
2884/// Returns the speculative member state after replaying one ATN action state.
2885fn member_values_after_action(
2886    source_state: usize,
2887    actions: &[ParserMemberAction],
2888    semantics: Option<&ParserSemantics>,
2889    values: &BTreeMap<usize, i64>,
2890) -> BTreeMap<usize, i64> {
2891    let mut values = values.clone();
2892    apply_member_actions(source_state, actions, semantics, &mut values);
2893    values
2894}
2895
2896/// Returns the speculative rule-return state after replaying one ATN action.
2897fn return_values_after_action(
2898    source_state: usize,
2899    rule_index: usize,
2900    actions: &[ParserReturnAction],
2901    semantics: Option<&ParserSemantics>,
2902    values: &BTreeMap<String, i64>,
2903) -> BTreeMap<String, i64> {
2904    let mut values = values.clone();
2905    for action in actions
2906        .iter()
2907        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
2908    {
2909        values.insert(action.name.to_owned(), action.value);
2910    }
2911    if let Some(semantics) = semantics {
2912        let mut member_values = BTreeMap::new();
2913        let mut ctx = ParserTableSemCtx {
2914            member_values: &mut member_values,
2915            return_values: &mut values,
2916        };
2917        for action in semantics.actions.iter().filter(|action| {
2918            action.source_state == source_state
2919                && action.rule_index == rule_index
2920                && !action.speculative
2921        }) {
2922            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
2923        }
2924    }
2925    values
2926}
2927
2928/// Resolves the integer argument visible to a child rule invocation.
2929fn rule_local_int_arg(
2930    rule_args: &[ParserRuleArg],
2931    source_state: usize,
2932    rule_index: usize,
2933    local_int_arg: Option<(usize, i64)>,
2934) -> Option<(usize, i64)> {
2935    rule_args
2936        .iter()
2937        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
2938        .map(|arg| {
2939            let value = if arg.inherit_local {
2940                local_int_arg.map_or(arg.value, |(_, value)| value)
2941            } else {
2942                arg.value
2943            };
2944            (rule_index, value)
2945        })
2946}
2947
2948/// Builds the terminal recognition outcome for a path that reached its stop
2949/// state.
2950fn stop_outcome(
2951    index: usize,
2952    consumed_eof: bool,
2953    rule_alt_number: usize,
2954    member_values: BTreeMap<usize, i64>,
2955    return_values: BTreeMap<String, i64>,
2956) -> Vec<RecognizeOutcome> {
2957    vec![RecognizeOutcome {
2958        index,
2959        consumed_eof,
2960        alt_number: rule_alt_number,
2961        member_values,
2962        return_values,
2963        diagnostics: Vec::new(),
2964        decisions: Vec::new(),
2965        actions: Vec::new(),
2966        nodes: Vec::new(),
2967    }]
2968}
2969
2970fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
2971    with_shared_atn_caches(atn, |cache| {
2972        *cache.observable_action_transitions.get_or_insert_with(|| {
2973            atn.states().iter().any(|state| {
2974                state.transitions.iter().any(|transition| {
2975                    matches!(
2976                        transition,
2977                        Transition::Action {
2978                            action_index: Some(_),
2979                            ..
2980                        }
2981                    )
2982                })
2983            })
2984        })
2985    })
2986}
2987
2988fn atn_has_predicate_transitions(atn: &Atn) -> bool {
2989    with_shared_atn_caches(atn, |cache| {
2990        *cache.predicate_transitions.get_or_insert_with(|| {
2991            atn.states().iter().any(|state| {
2992                state
2993                    .transitions
2994                    .iter()
2995                    .any(|transition| matches!(transition, Transition::Predicate { .. }))
2996            })
2997        })
2998    })
2999}
3000
3001#[derive(Clone, Debug, Eq, PartialEq)]
3002struct RecognizeRequest<'a> {
3003    state_number: usize,
3004    stop_state: usize,
3005    index: usize,
3006    rule_start_index: usize,
3007    decision_start_index: Option<usize>,
3008    init_action_rules: &'a BTreeSet<usize>,
3009    predicates: &'a [(usize, usize, ParserPredicate)],
3010    semantics: Option<&'a ParserSemantics>,
3011    rule_args: &'a [ParserRuleArg],
3012    member_actions: &'a [ParserMemberAction],
3013    return_actions: &'a [ParserReturnAction],
3014    local_int_arg: Option<(usize, i64)>,
3015    member_values: BTreeMap<usize, i64>,
3016    return_values: BTreeMap<String, i64>,
3017    rule_alt_number: usize,
3018    track_alt_numbers: bool,
3019    consumed_eof: bool,
3020    /// Current left-recursive precedence threshold, matching ANTLR's
3021    /// `precpred(_ctx, k)` check for generated precedence rules.
3022    precedence: i32,
3023    depth: usize,
3024    recovery_symbols: BTreeSet<i32>,
3025    recovery_state: Option<usize>,
3026}
3027
3028#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3029struct RecognizeKey {
3030    state_number: usize,
3031    stop_state: usize,
3032    index: usize,
3033    rule_start_index: usize,
3034    decision_start_index: Option<usize>,
3035    local_int_arg: Option<(usize, i64)>,
3036    member_values: BTreeMap<usize, i64>,
3037    return_values: BTreeMap<String, i64>,
3038    rule_alt_number: usize,
3039    track_alt_numbers: bool,
3040    consumed_eof: bool,
3041    precedence: i32,
3042    recovery_symbols: BTreeSet<i32>,
3043    recovery_state: Option<usize>,
3044}
3045
3046#[derive(Clone, Debug, Eq, PartialEq)]
3047struct EpsilonActionStep {
3048    source_state: usize,
3049    target: usize,
3050    action_rule_index: Option<usize>,
3051    left_recursive_boundary: Option<usize>,
3052    decision: Option<usize>,
3053    decision_start_index: Option<usize>,
3054    alt_number: usize,
3055    recovery_symbols: BTreeSet<i32>,
3056    recovery_state: Option<usize>,
3057}
3058
3059struct RecognizeScratch<'a> {
3060    visiting: &'a mut BTreeSet<RecognizeKey>,
3061    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3062    expected: &'a mut ExpectedTokens,
3063}
3064
3065#[derive(Clone, Debug, Eq, PartialEq)]
3066struct FastRecognizeRequest {
3067    state_number: usize,
3068    stop_state: usize,
3069    index: usize,
3070    rule_start_index: usize,
3071    decision_start_index: Option<usize>,
3072    precedence: i32,
3073    depth: usize,
3074    recovery_symbols: Rc<BTreeSet<i32>>,
3075    recovery_state: Option<usize>,
3076}
3077
3078#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3079struct FastRecognizeTopRequest {
3080    start_state: usize,
3081    stop_state: usize,
3082    start_index: usize,
3083    precedence: i32,
3084    caller_follow_state: Option<usize>,
3085}
3086
3087/// Memo key for the fast recognizer. `recovery_symbols` must come from
3088/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
3089/// key, so equal sets share one allocation and the key can store that
3090/// allocation's address instead of cloning an `Rc` and walking the full
3091/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
3092/// into distinct cache coordinates.
3093#[derive(Clone, Debug)]
3094struct FastRecognizeKey {
3095    state_number: usize,
3096    stop_state: usize,
3097    index: usize,
3098    rule_start_index: usize,
3099    decision_start_index: Option<usize>,
3100    precedence: i32,
3101    recovery_symbols_id: usize,
3102    recovery_state: Option<usize>,
3103}
3104
3105impl PartialEq for FastRecognizeKey {
3106    fn eq(&self, other: &Self) -> bool {
3107        if self.state_number != other.state_number
3108            || self.stop_state != other.stop_state
3109            || self.index != other.index
3110            || self.rule_start_index != other.rule_start_index
3111            || self.decision_start_index != other.decision_start_index
3112            || self.precedence != other.precedence
3113            || self.recovery_state != other.recovery_state
3114            || self.recovery_symbols_id != other.recovery_symbols_id
3115        {
3116            return false;
3117        }
3118        true
3119    }
3120}
3121
3122impl Eq for FastRecognizeKey {}
3123
3124impl Hash for FastRecognizeKey {
3125    fn hash<H: Hasher>(&self, hasher: &mut H) {
3126        self.state_number.hash(hasher);
3127        self.stop_state.hash(hasher);
3128        self.index.hash(hasher);
3129        self.rule_start_index.hash(hasher);
3130        self.decision_start_index.hash(hasher);
3131        self.precedence.hash(hasher);
3132        self.recovery_state.hash(hasher);
3133        self.recovery_symbols_id.hash(hasher);
3134    }
3135}
3136
3137struct FastRecoveryRequest<'a, 'b> {
3138    atn: &'a Atn,
3139    transition: &'a Transition,
3140    expected_symbols: Rc<BTreeSet<i32>>,
3141    target: usize,
3142    request: FastRecognizeRequest,
3143    visiting: &'b mut FxHashSet<FastRecognizeKey>,
3144    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
3145    expected: &'b mut ExpectedTokens,
3146}
3147
3148struct FastCurrentTokenDeletionRequest<'a, 'b> {
3149    atn: &'a Atn,
3150    expected_symbols: Rc<BTreeSet<i32>>,
3151    request: FastRecognizeRequest,
3152    visiting: &'b mut FxHashSet<FastRecognizeKey>,
3153    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
3154    expected: &'b mut ExpectedTokens,
3155}
3156
3157#[derive(Clone, Copy)]
3158struct FastChildRuleFailureRecoveryRequest<'a> {
3159    atn: &'a Atn,
3160    rule_index: usize,
3161    start_index: usize,
3162    follow_state: usize,
3163    stop_state: usize,
3164    expected: &'a ExpectedTokens,
3165}
3166
3167struct RecoveryRequest<'a, 'b> {
3168    atn: &'a Atn,
3169    transition: &'a Transition,
3170    expected_symbols: BTreeSet<i32>,
3171    target: usize,
3172    request: RecognizeRequest<'a>,
3173    visiting: &'b mut BTreeSet<RecognizeKey>,
3174    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3175    expected: &'b mut ExpectedTokens,
3176}
3177
3178struct CurrentTokenDeletionRequest<'a, 'b> {
3179    atn: &'a Atn,
3180    expected_symbols: BTreeSet<i32>,
3181    request: RecognizeRequest<'a>,
3182    visiting: &'b mut BTreeSet<RecognizeKey>,
3183    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
3184    expected: &'b mut ExpectedTokens,
3185}
3186
3187/// Carries the state needed after the normal token-recovery strategies fail
3188/// for a consuming transition.
3189struct ConsumingFailureFallback<'a> {
3190    atn: &'a Atn,
3191    target: usize,
3192    request: RecognizeRequest<'a>,
3193    symbol: i32,
3194    expected_symbols: BTreeSet<i32>,
3195    decision_start_index: Option<usize>,
3196    decision: Option<usize>,
3197}
3198
3199/// Captures the parent-rule context needed when a called rule fails before it
3200/// can produce a normal outcome.
3201struct ChildRuleFailureRecovery<'a> {
3202    atn: &'a Atn,
3203    rule_index: usize,
3204    start_index: usize,
3205    follow_state: usize,
3206    stop_state: usize,
3207    member_values: BTreeMap<usize, i64>,
3208    expected: &'a ExpectedTokens,
3209}
3210
3211/// Bundles the context needed to evaluate one semantic predicate transition.
3212#[derive(Clone, Copy, Debug)]
3213struct PredicateEval<'a> {
3214    index: usize,
3215    rule_index: usize,
3216    pred_index: usize,
3217    predicates: &'a [(usize, usize, ParserPredicate)],
3218    semantics: Option<&'a ParserSemantics>,
3219    context: Option<&'a ParserRuleContext>,
3220    local_int_arg: Option<(usize, i64)>,
3221    member_values: &'a BTreeMap<usize, i64>,
3222}
3223
3224#[derive(Clone, Copy, Debug)]
3225struct ParserSemanticHookRequest<'a> {
3226    index: usize,
3227    rule_index: usize,
3228    pred_index: usize,
3229    context: Option<&'a ParserRuleContext>,
3230    local_int_arg: Option<(usize, i64)>,
3231    member_values: &'a BTreeMap<usize, i64>,
3232}
3233
3234/// Predicate-evaluation context over the recognizer's speculative state.
3235///
3236/// This sits in the prediction hot loop, so everything is borrowed: member
3237/// state read-only from the current speculative path and the rule name
3238/// straight from recognizer metadata. Predicates are pure by construction
3239/// ([`semir::PExpr`] has no mutating node); statement execution uses
3240/// [`ParserTableSemCtx`] (speculative member/return replay) and
3241/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
3242struct ParserSemIrCtx<'a, S, H>
3243where
3244    S: TokenSource,
3245    H: SemanticHooks,
3246{
3247    input: &'a mut CommonTokenStream<S>,
3248    semantic_hooks: &'a mut H,
3249    rule_index: usize,
3250    coordinate_index: usize,
3251    rule_name: Option<&'a str>,
3252    context: Option<&'a ParserRuleContext>,
3253    local_int_arg: Option<(usize, i64)>,
3254    member_values: &'a BTreeMap<usize, i64>,
3255    invoked_predicates: &'a mut Vec<(usize, usize)>,
3256    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
3257    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
3258    /// table path instead of coercing the miss to `false`.
3259    unknown_predicate_policy: UnknownSemanticPolicy,
3260    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
3261}
3262
3263impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
3264where
3265    S: TokenSource,
3266    H: SemanticHooks,
3267{
3268    fn la(&mut self, offset: isize) -> i64 {
3269        i64::from(self.input.la(offset))
3270    }
3271
3272    fn token_text(&mut self, offset: isize) -> Option<&str> {
3273        self.input.lt(offset).and_then(Token::text)
3274    }
3275
3276    fn token_index_adjacent(&mut self) -> bool {
3277        let Some(first) = self.input.lt(-2).map(Token::token_index) else {
3278            return false;
3279        };
3280        let Some(second) = self.input.lt(-1).map(Token::token_index) else {
3281            return false;
3282        };
3283        first + 1 == second
3284    }
3285
3286    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
3287        self.context.and_then(|context| {
3288            context.children().iter().find_map(|child| match child {
3289                ParseTree::Rule(rule) if rule.context().rule_index() == rule_index => {
3290                    Some(child.text())
3291                }
3292                ParseTree::Rule(_) | ParseTree::Terminal(_) | ParseTree::Error(_) => None,
3293            })
3294        })
3295    }
3296
3297    fn member(&self, member: usize) -> Option<i64> {
3298        Some(self.member_values.get(&member).copied().unwrap_or_default())
3299    }
3300
3301    fn local_arg(&self) -> Option<i64> {
3302        self.local_int_arg.map(|(_, value)| value)
3303    }
3304
3305    fn column(&self) -> Option<i64> {
3306        None
3307    }
3308
3309    fn token_start_column(&self) -> Option<i64> {
3310        None
3311    }
3312
3313    fn token_text_so_far(&self) -> Option<String> {
3314        None
3315    }
3316
3317    fn hook(&mut self, _hook: HookId) -> bool {
3318        let mut ctx = ParserSemCtx {
3319            input: &mut *self.input,
3320            rule_index: self.rule_index,
3321            coordinate_index: self.coordinate_index,
3322            rule_name: self.rule_name.map(str::to_owned),
3323            context: self.context,
3324            tree: None,
3325            local_int_arg: self.local_int_arg,
3326            member_values: self.member_values,
3327            action: None,
3328        };
3329        match self
3330            .semantic_hooks
3331            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
3332        {
3333            Some(result) => result,
3334            // No hook answered this coordinate: fall through to the configured
3335            // policy instead of silently rejecting the alternative, matching the
3336            // legacy table path's dispatch chain (hook → policy).
3337            None => apply_unknown_predicate_policy(
3338                self.unknown_predicate_policy,
3339                self.rule_index,
3340                self.coordinate_index,
3341                self.unknown_predicate_hits,
3342            ),
3343        }
3344    }
3345
3346    fn trace_bool(&mut self, value: bool) -> bool {
3347        let key = (self.rule_index, self.coordinate_index);
3348        if !self.invoked_predicates.contains(&key) {
3349            self.invoked_predicates.push(key);
3350            use std::io::Write as _;
3351            let mut stdout = std::io::stdout().lock();
3352            let _ = writeln!(stdout, "eval={value}");
3353        }
3354        value
3355    }
3356}
3357
3358/// Captures predicate-failure recovery metadata for fail-option predicates.
3359struct PredicateFailureRecovery<'a> {
3360    rule_index: usize,
3361    index: usize,
3362    message: &'a str,
3363    member_values: BTreeMap<usize, i64>,
3364    return_values: BTreeMap<String, i64>,
3365    rule_alt_number: usize,
3366}
3367
3368#[derive(Debug)]
3369enum DirectAdaptiveParseControl {
3370    Fallback(DirectAdaptiveFallback),
3371}
3372
3373#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3374enum DirectAdaptiveFallback {
3375    Action,
3376    InvalidAlt,
3377    LeftRecursiveBoundary,
3378    MissingAtn,
3379    NoTransition,
3380    Predicate,
3381    Prediction,
3382    Precedence,
3383    RuleStop,
3384    SemanticContext,
3385    StepLimit,
3386    TokenMismatch,
3387    UnknownDecision,
3388}
3389
3390type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
3391
3392struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
3393where
3394    S: TokenSource,
3395    H: SemanticHooks,
3396{
3397    parser: &'sim mut BaseParser<S, H>,
3398    atn: &'atn Atn,
3399    simulator: &'sim mut ParserAtnSimulator<'atn>,
3400    decision_by_state: Vec<Option<usize>>,
3401    steps: usize,
3402}
3403
3404/// Outcome of a generated token / set / not-set match that may recover.
3405///
3406/// Generated parsers append `children` to the current rule context. `consumed_eof`
3407/// reports whether the match actually consumed a real EOF terminal — it is true
3408/// only on a successful match (or single-token deletion that lands on EOF), and
3409/// always false on single-token insertion, which synthesizes a missing token and
3410/// consumes nothing. Generated code feeds this into `finish_rule`'s
3411/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
3412/// truly matched, matching ANTLR's `matchedEOF` semantics.
3413#[derive(Clone, Debug, Eq, PartialEq)]
3414pub struct GeneratedMatch {
3415    children: GeneratedMatchChildren,
3416    consumed_eof: bool,
3417}
3418
3419#[derive(Clone, Debug, Eq, PartialEq)]
3420enum GeneratedMatchChildren {
3421    One(ParseTree),
3422    Many(Vec<ParseTree>),
3423}
3424
3425struct GeneratedMatchChildrenIntoIter {
3426    one: Option<ParseTree>,
3427    many: Option<std::vec::IntoIter<ParseTree>>,
3428}
3429
3430impl Iterator for GeneratedMatchChildrenIntoIter {
3431    type Item = ParseTree;
3432
3433    fn next(&mut self) -> Option<Self::Item> {
3434        self.one
3435            .take()
3436            .or_else(|| self.many.as_mut().and_then(Iterator::next))
3437    }
3438}
3439
3440impl GeneratedMatch {
3441    /// Parse-tree children produced by the match (the matched terminal, an
3442    /// error node plus deleted-then-matched terminal, or a single missing-token
3443    /// error node).
3444    #[must_use]
3445    pub fn children(&self) -> &[ParseTree] {
3446        match &self.children {
3447            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
3448            GeneratedMatchChildren::Many(children) => children,
3449        }
3450    }
3451
3452    /// Consumes the result, returning the children for appending to the rule
3453    /// context.
3454    #[must_use]
3455    pub fn into_children(self) -> Vec<ParseTree> {
3456        match self.children {
3457            GeneratedMatchChildren::One(child) => vec![child],
3458            GeneratedMatchChildren::Many(children) => children,
3459        }
3460    }
3461
3462    /// Consumes the match without allocating for the common single-child case.
3463    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
3464        match self.children {
3465            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
3466                one: Some(child),
3467                many: None,
3468            },
3469            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
3470                one: None,
3471                many: Some(children.into_iter()),
3472            },
3473        }
3474    }
3475
3476    /// Whether a real EOF terminal was consumed by this match.
3477    #[must_use]
3478    pub const fn consumed_eof(&self) -> bool {
3479        self.consumed_eof
3480    }
3481}
3482
3483impl<S> BaseParser<S, NoSemanticHooks>
3484where
3485    S: TokenSource,
3486{
3487    /// Creates a parser base over a buffered token stream and recognizer
3488    /// metadata.
3489    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
3490        Self::with_semantic_hooks(input, data, NoSemanticHooks)
3491    }
3492}
3493
3494impl<S, H> BaseParser<S, H>
3495where
3496    S: TokenSource,
3497    H: SemanticHooks,
3498{
3499    /// Creates a parser base with caller-owned semantic hooks.
3500    pub fn with_semantic_hooks(
3501        input: CommonTokenStream<S>,
3502        data: RecognizerData,
3503        semantic_hooks: H,
3504    ) -> Self {
3505        Self {
3506            input,
3507            data,
3508            semantic_hooks,
3509            build_parse_trees: true,
3510            syntax_errors: 0,
3511            report_diagnostic_errors: false,
3512            prediction_mode: PredictionMode::Ll,
3513            prediction_diagnostics: Vec::new(),
3514            reported_prediction_diagnostics: BTreeSet::new(),
3515            generated_parser_diagnostics: Vec::new(),
3516            generated_sync_expected: None,
3517            int_members: BTreeMap::new(),
3518            rule_context_stack: Vec::new(),
3519            rule_context_version: 0,
3520            prediction_context_cache: None,
3521            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
3522            pending_invoking_states: Vec::new(),
3523            precedence_stack: vec![0],
3524            invoked_predicates: Vec::new(),
3525            bail_on_error: false,
3526            unknown_predicate_policy: UnknownSemanticPolicy::default(),
3527            unknown_predicate_hits: Vec::new(),
3528            unhandled_action_hits: Vec::new(),
3529            rule_first_set_cache: Vec::new(),
3530            state_expected_cache: FxHashMap::default(),
3531            state_expected_token_cache: FxHashMap::default(),
3532            rule_stop_reach_cache: Vec::new(),
3533            recovery_symbols_intern: FxHashMap::default(),
3534            decision_lookahead_cache: FxHashMap::default(),
3535            ll1_decision_cache: FxHashMap::default(),
3536            empty_cycle_cache: Vec::new(),
3537            single_outcome_memo_mode: SingleOutcomeMemoMode::Probe,
3538            single_outcome_probe_seen: FxHashSet::default(),
3539            single_outcome_probe_samples: 0,
3540            single_outcome_probe_repeats: 0,
3541            empty_recovery_symbols: Rc::new(BTreeSet::new()),
3542            fast_first_set_prefilter: true,
3543            fast_recovery_enabled: true,
3544            fast_token_nodes_enabled: true,
3545        }
3546    }
3547
3548    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
3549        &mut self.input
3550    }
3551
3552    /// Installs the policy for predicate coordinates that no translated table
3553    /// entry or user hook resolves.
3554    ///
3555    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
3556    /// but generated recursive-descent rules evaluate predicates directly
3557    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
3558    /// going through those options. Generated parser constructors call this so
3559    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
3560    /// the field at its `AssumeTrue` default and silently accepting an
3561    /// unimplemented hook predicate.
3562    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
3563        self.unknown_predicate_policy = policy;
3564    }
3565
3566    /// Reports any unknown predicate coordinate the generated-direct path
3567    /// recorded under [`UnknownSemanticPolicy::Error`], as an
3568    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
3569    /// after a rule completes so the fail-loud policy surfaces on the
3570    /// generated path the same way the interpreter entry surfaces it.
3571    #[must_use]
3572    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
3573        let error = self.unknown_semantic_error();
3574        self.unknown_predicate_hits.clear();
3575        self.unhandled_action_hits.clear();
3576        error
3577    }
3578
3579    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
3580    ///
3581    /// Generated parsers call this at the true top-level entry so a parser
3582    /// reused after a fail-loud (or recovered) parse starts clean, without
3583    /// clearing hits mid-parse where a generated parent still needs a child's
3584    /// recorded coordinate to survive to the top-level boundary.
3585    pub fn reset_unknown_semantic_hits(&mut self) {
3586        self.unknown_predicate_hits.clear();
3587        self.unhandled_action_hits.clear();
3588    }
3589
3590    /// Returns the token stream owned by this parser.
3591    #[must_use]
3592    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
3593        &self.input
3594    }
3595
3596    /// Consumes this parser and returns its token stream.
3597    #[must_use]
3598    pub fn into_token_stream(self) -> CommonTokenStream<S> {
3599        self.input
3600    }
3601
3602    /// Returns the number of parser syntax errors recorded by committed parse
3603    /// paths so far.
3604    pub const fn number_of_syntax_errors(&self) -> usize {
3605        self.syntax_errors
3606    }
3607
3608    /// Records a syntax error that generated parser code returns as fatal before
3609    /// it can recover into the current rule context.
3610    pub const fn record_generated_syntax_error(&mut self) {
3611        self.record_syntax_errors(1);
3612    }
3613
3614    const fn record_syntax_errors(&mut self, count: usize) {
3615        self.syntax_errors = self.syntax_errors.saturating_add(count);
3616    }
3617
3618    /// Emits diagnostics buffered by the token stream while generated parser
3619    /// code was fetching lexer tokens directly.
3620    pub fn report_token_source_errors(&mut self) {
3621        report_token_source_errors(&self.input.drain_source_errors());
3622    }
3623
3624    /// Captures generated-parser diagnostics and syntax-error count before a
3625    /// speculative generated rule path.
3626    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
3627        GeneratedDiagnosticsCheckpoint {
3628            diagnostics_len: self.generated_parser_diagnostics.len(),
3629            syntax_errors: self.syntax_errors,
3630        }
3631    }
3632
3633    /// Restores generated-parser diagnostics after a speculative rule path failed.
3634    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
3635        self.generated_parser_diagnostics
3636            .truncate(marker.diagnostics_len);
3637        self.syntax_errors = marker.syntax_errors;
3638        self.generated_sync_expected = None;
3639    }
3640
3641    /// Emits diagnostics recorded by committed generated parser recovery.
3642    pub fn report_generated_parser_diagnostics(&mut self) {
3643        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
3644        let token_errors = self.input.drain_source_errors();
3645        report_generated_diagnostics(&parser_diagnostics, &token_errors);
3646    }
3647
3648    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
3649    /// decision code.
3650    pub fn record_generated_ambiguity_diagnostic(
3651        &mut self,
3652        atn: &Atn,
3653        state_number: usize,
3654        start_index: usize,
3655        stop_index: usize,
3656        alts: &[usize],
3657    ) {
3658        if !self.report_diagnostic_errors || alts.len() < 2 {
3659            return;
3660        }
3661        let Some(decision) = atn
3662            .decision_to_state()
3663            .iter()
3664            .position(|candidate| *candidate == state_number)
3665        else {
3666            return;
3667        };
3668        let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
3669            return;
3670        };
3671        let rule_name = self
3672            .rule_names()
3673            .get(rule_index)
3674            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
3675        let input = display_input_text(&self.input.text(start_index, stop_index));
3676        let alts = alts
3677            .iter()
3678            .map(usize::to_string)
3679            .collect::<Vec<_>>()
3680            .join(", ");
3681        let key = (decision, start_index, format!("{alts}:{input}"));
3682        if !self.reported_prediction_diagnostics.insert(key) {
3683            return;
3684        }
3685        let start_token = self.token_at(start_index);
3686        let stop_token = self.token_at(stop_index);
3687        self.generated_parser_diagnostics.push(diagnostic_for_token(
3688            start_token.as_ref(),
3689            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
3690        ));
3691        self.generated_parser_diagnostics.push(diagnostic_for_token(
3692            stop_token.as_ref(),
3693            format!(
3694                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
3695            ),
3696        ));
3697    }
3698
3699    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
3700    /// parser calls to the adaptive simulator.
3701    pub fn record_generated_prediction_diagnostic(
3702        &mut self,
3703        atn: &Atn,
3704        state_number: usize,
3705        prediction: &ParserAtnPrediction,
3706    ) {
3707        let Some(diagnostic) = &prediction.diagnostic else {
3708            return;
3709        };
3710        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
3711            return;
3712        }
3713        let Some(decision) = atn
3714            .decision_to_state()
3715            .iter()
3716            .position(|candidate| *candidate == state_number)
3717        else {
3718            return;
3719        };
3720        let Some(rule_index) = atn.state(state_number).and_then(|state| state.rule_index) else {
3721            return;
3722        };
3723        let rule_name = self
3724            .rule_names()
3725            .get(rule_index)
3726            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
3727        let attempt_input = display_input_text(
3728            &self
3729                .input
3730                .text(diagnostic.start_index, diagnostic.sll_stop_index),
3731        );
3732        let result_input = display_input_text(
3733            &self
3734                .input
3735                .text(diagnostic.start_index, diagnostic.ll_stop_index),
3736        );
3737        let alts = diagnostic
3738            .conflicting_alts
3739            .iter()
3740            .map(usize::to_string)
3741            .collect::<Vec<_>>()
3742            .join(", ");
3743        let key = (
3744            decision,
3745            diagnostic.start_index,
3746            format!(
3747                "{:?}:{alts}:{attempt_input}:{result_input}",
3748                diagnostic.kind
3749            ),
3750        );
3751        if !self.reported_prediction_diagnostics.insert(key) {
3752            return;
3753        }
3754        let attempt_token = self.token_at(diagnostic.sll_stop_index);
3755        self.generated_parser_diagnostics.push(diagnostic_for_token(
3756            attempt_token.as_ref(),
3757            format!(
3758                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
3759            ),
3760        ));
3761        let result_token = self.token_at(diagnostic.ll_stop_index);
3762        let message = match diagnostic.kind {
3763            ParserAtnPredictionDiagnosticKind::Ambiguity => {
3764                // Java's DiagnosticErrorListener is exactOnly by default:
3765                // non-exact ambiguities (default LL mode stopping at the
3766                // first resolvable conflict) report the attempt above but
3767                // suppress the ambiguity line itself.
3768                if !diagnostic.exact {
3769                    return;
3770                }
3771                format!(
3772                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
3773                )
3774            }
3775            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
3776                format!(
3777                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
3778                )
3779            }
3780        };
3781        self.generated_parser_diagnostics
3782            .push(diagnostic_for_token(result_token.as_ref(), message));
3783    }
3784
3785    pub fn la(&mut self, offset: isize) -> i32 {
3786        self.input.la_token(offset)
3787    }
3788
3789    pub fn consume(&mut self) {
3790        IntStream::consume(&mut self.input);
3791    }
3792
3793    /// Sets a generated integer member value used by target-template tests.
3794    pub fn set_int_member(&mut self, member: usize, value: i64) {
3795        self.int_members.insert(member, value);
3796    }
3797
3798    /// Reads a generated integer member value.
3799    pub fn int_member(&self, member: usize) -> Option<i64> {
3800        self.int_members.get(&member).copied()
3801    }
3802
3803    /// Captures generated integer members before speculative generated parser
3804    /// execution.
3805    pub fn int_members_checkpoint(&self) -> BTreeMap<usize, i64> {
3806        self.int_members.clone()
3807    }
3808
3809    /// Restores generated integer members after generated parser fallback.
3810    pub fn restore_int_members(&mut self, members: BTreeMap<usize, i64>) {
3811        self.int_members = members;
3812    }
3813
3814    /// Adds `delta` to a generated integer member and returns the new value.
3815    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
3816        let value = self.int_members.entry(member).or_default();
3817        *value += delta;
3818        *value
3819    }
3820
3821    /// Matches and consumes the current token when it has the expected token
3822    /// type.
3823    ///
3824    /// On success the consumed token is wrapped as a terminal parse-tree node.
3825    /// On mismatch the error carries vocabulary display names so diagnostics are
3826    /// stable across literal and symbolic token naming.
3827    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
3828        let current = self
3829            .input
3830            .lt_ref(1)
3831            .ok_or_else(|| AntlrError::ParserError {
3832                line: 0,
3833                column: 0,
3834                message: "missing current token".to_owned(),
3835            })?;
3836        if current.token_type() == token_type {
3837            self.consume();
3838            Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
3839        } else {
3840            Err(AntlrError::MismatchedInput {
3841                expected: self.vocabulary().display_name(token_type),
3842                found: self.vocabulary().display_name(current.token_type()),
3843            })
3844        }
3845    }
3846
3847    /// Matches a token from generated recursive-descent code, including ANTLR's
3848    /// single-token insertion recovery when the active rule context can legally
3849    /// continue at the current input symbol.
3850    pub fn match_token_recovering(
3851        &mut self,
3852        token_type: i32,
3853        follow_state: usize,
3854        atn: &Atn,
3855    ) -> Result<GeneratedMatch, AntlrError> {
3856        let current = self
3857            .input
3858            .lt_ref(1)
3859            .ok_or_else(|| AntlrError::ParserError {
3860                line: 0,
3861                column: 0,
3862                message: "missing current token".to_owned(),
3863            })?;
3864        if current.token_type() == token_type {
3865            self.generated_sync_expected = None;
3866            let consumed_eof = current.token_type() == TOKEN_EOF;
3867            self.consume();
3868            return Ok(GeneratedMatch {
3869                children: GeneratedMatchChildren::One(ParseTree::Terminal(TerminalNode::from_ref(
3870                    current,
3871                ))),
3872                consumed_eof,
3873            });
3874        }
3875        let mut expected_symbols = BTreeSet::new();
3876        expected_symbols.insert(token_type);
3877        self.recover_generated_match(
3878            current.as_ref().clone(),
3879            &expected_symbols,
3880            follow_state,
3881            atn,
3882            |symbol| symbol == token_type,
3883        )
3884    }
3885
3886    pub fn match_set_recovering(
3887        &mut self,
3888        intervals: &[(i32, i32)],
3889        follow_state: usize,
3890        atn: &Atn,
3891    ) -> Result<GeneratedMatch, AntlrError> {
3892        let current = self
3893            .input
3894            .lt_ref(1)
3895            .ok_or_else(|| AntlrError::ParserError {
3896                line: 0,
3897                column: 0,
3898                message: "missing current token".to_owned(),
3899            })?;
3900        if interval_set_contains(intervals, current.token_type()) {
3901            self.generated_sync_expected = None;
3902            let consumed_eof = current.token_type() == TOKEN_EOF;
3903            self.consume();
3904            return Ok(GeneratedMatch {
3905                children: GeneratedMatchChildren::One(ParseTree::Terminal(TerminalNode::from_ref(
3906                    current,
3907                ))),
3908                consumed_eof,
3909            });
3910        }
3911        let expected_symbols = interval_symbols(intervals);
3912        self.recover_generated_match(
3913            current.as_ref().clone(),
3914            &expected_symbols,
3915            follow_state,
3916            atn,
3917            |symbol| interval_set_contains(intervals, symbol),
3918        )
3919    }
3920
3921    pub fn match_not_set_recovering(
3922        &mut self,
3923        intervals: &[(i32, i32)],
3924        min_vocabulary: i32,
3925        max_vocabulary: i32,
3926        follow_state: usize,
3927        atn: &Atn,
3928    ) -> Result<GeneratedMatch, AntlrError> {
3929        let current = self
3930            .input
3931            .lt_ref(1)
3932            .ok_or_else(|| AntlrError::ParserError {
3933                line: 0,
3934                column: 0,
3935                message: "missing current token".to_owned(),
3936            })?;
3937        if (min_vocabulary..=max_vocabulary).contains(&current.token_type())
3938            && !interval_set_contains(intervals, current.token_type())
3939        {
3940            self.generated_sync_expected = None;
3941            let consumed_eof = current.token_type() == TOKEN_EOF;
3942            self.consume();
3943            return Ok(GeneratedMatch {
3944                children: GeneratedMatchChildren::One(ParseTree::Terminal(TerminalNode::from_ref(
3945                    current,
3946                ))),
3947                consumed_eof,
3948            });
3949        }
3950        let expected_symbols =
3951            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
3952        self.recover_generated_match(
3953            current.as_ref().clone(),
3954            &expected_symbols,
3955            follow_state,
3956            atn,
3957            |symbol| {
3958                (min_vocabulary..=max_vocabulary).contains(&symbol)
3959                    && !interval_set_contains(intervals, symbol)
3960            },
3961        )
3962    }
3963
3964    fn recover_generated_match(
3965        &mut self,
3966        current: CommonToken,
3967        expected_symbols: &BTreeSet<i32>,
3968        follow_state: usize,
3969        atn: &Atn,
3970        matches: impl Fn(i32) -> bool,
3971    ) -> Result<GeneratedMatch, AntlrError> {
3972        let expected_display = self.expected_symbols_display(expected_symbols);
3973        if self.bail_on_error {
3974            return Err(AntlrError::ParserError {
3975                line: current.line(),
3976                column: current.column(),
3977                message: format!(
3978                    "mismatched input {} expecting {expected_display}",
3979                    token_input_display(&current)
3980                ),
3981            });
3982        }
3983        if current.token_type() != TOKEN_EOF
3984            && let Some(next) = self.input.lt(2).cloned()
3985            && matches(next.token_type())
3986        {
3987            let message = format!(
3988                "extraneous input {} expecting {expected_display}",
3989                token_input_display(&current)
3990            );
3991            self.push_generated_parser_diagnostic(diagnostic_for_token(Some(&current), message));
3992            self.record_syntax_errors(1);
3993            self.generated_sync_expected = None;
3994            // Single-token deletion: skip `current`, then accept `next`. The
3995            // accepted token can be EOF only if it is a real EOF terminal.
3996            let consumed_eof = next.token_type() == TOKEN_EOF;
3997            self.consume();
3998            self.consume();
3999            return Ok(GeneratedMatch {
4000                children: GeneratedMatchChildren::Many(vec![
4001                    ParseTree::Error(ErrorNode::new(current)),
4002                    ParseTree::Terminal(TerminalNode::new(next)),
4003                ]),
4004                consumed_eof,
4005            });
4006        }
4007        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
4008        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
4009        // *after* the current element can consume the current symbol. At EOF that
4010        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
4011        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
4012        // when EOF merely leaks in from the empty enclosing context (as in
4013        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
4014        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
4015        // so consult the follow state's OWN expected set for the explicit case.
4016        let follow_explicitly_expects_eof = current.token_type() == TOKEN_EOF
4017            && self
4018                .cached_state_expected_symbols(atn, follow_state)
4019                .contains(&TOKEN_EOF);
4020        if follow_symbols.contains(&current.token_type())
4021            && (current.token_type() != TOKEN_EOF
4022                || self.rule_context_stack.len() > 1
4023                || expected_symbols.is_empty()
4024                || follow_explicitly_expects_eof)
4025        {
4026            let message = format!(
4027                "missing {expected_display} at {}",
4028                token_input_display(&current)
4029            );
4030            self.push_generated_parser_diagnostic(diagnostic_for_token(Some(&current), message));
4031            self.record_syntax_errors(1);
4032            self.generated_sync_expected = None;
4033            let token_type = expected_symbols.iter().next().copied().unwrap_or(TOKEN_EOF);
4034            let mut missing_symbol = BTreeSet::new();
4035            missing_symbol.insert(token_type);
4036            let missing_display = self.expected_symbols_display(&missing_symbol);
4037            let token = CommonToken::new(token_type)
4038                .with_text(format!("<missing {missing_display}>"))
4039                .with_span(usize::MAX, usize::MAX)
4040                .with_position(current.line(), current.column());
4041            // Single-token insertion synthesizes a missing token and consumes
4042            // nothing, so no EOF terminal is consumed even when the lookahead is
4043            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
4044            // from recording EOF as the rule stop on this recovery path.
4045            return Ok(GeneratedMatch {
4046                children: GeneratedMatchChildren::One(ParseTree::Error(ErrorNode::new(token))),
4047                consumed_eof: false,
4048            });
4049        }
4050        let mismatch_expected = self.generated_sync_expected.take().map_or_else(
4051            || expected_symbols.clone(),
4052            |symbols| symbols.to_btree_set(),
4053        );
4054        let mismatch_expected_display = self.expected_symbols_display(&mismatch_expected);
4055        Err(AntlrError::ParserError {
4056            line: current.line(),
4057            column: current.column(),
4058            message: format!(
4059                "mismatched input {} expecting {mismatch_expected_display}",
4060                token_input_display(&current)
4061            ),
4062        })
4063    }
4064
4065    fn generated_recovery_follow_symbols(
4066        &mut self,
4067        atn: &Atn,
4068        follow_state: usize,
4069    ) -> BTreeSet<i32> {
4070        let mut follow = self
4071            .cached_state_expected_symbols(atn, follow_state)
4072            .as_ref()
4073            .clone();
4074        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
4075            follow.extend(self.context_expected_symbols(atn));
4076        }
4077        follow
4078    }
4079
4080    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
4081        self.match_token(TOKEN_EOF)
4082    }
4083
4084    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
4085        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
4086    }
4087
4088    pub fn match_not_set(
4089        &mut self,
4090        intervals: &[(i32, i32)],
4091        min_vocabulary: i32,
4092        max_vocabulary: i32,
4093    ) -> Result<ParseTree, AntlrError> {
4094        self.match_interval_condition(intervals, |symbol| {
4095            (min_vocabulary..=max_vocabulary).contains(&symbol)
4096                && !interval_set_contains(intervals, symbol)
4097        })
4098    }
4099
4100    fn match_interval_condition(
4101        &mut self,
4102        intervals: &[(i32, i32)],
4103        matches: impl FnOnce(i32) -> bool,
4104    ) -> Result<ParseTree, AntlrError> {
4105        let current = self
4106            .input
4107            .lt_ref(1)
4108            .ok_or_else(|| AntlrError::ParserError {
4109                line: 0,
4110                column: 0,
4111                message: "missing current token".to_owned(),
4112            })?;
4113        if matches(current.token_type()) {
4114            self.consume();
4115            Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
4116        } else {
4117            Err(AntlrError::MismatchedInput {
4118                expected: self.interval_display(intervals),
4119                found: self.vocabulary().display_name(current.token_type()),
4120            })
4121        }
4122    }
4123
4124    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
4125        let values = intervals
4126            .iter()
4127            .map(|(start, stop)| {
4128                if start == stop {
4129                    self.vocabulary().display_name(*start)
4130                } else {
4131                    format!(
4132                        "{}..{}",
4133                        self.vocabulary().display_name(*start),
4134                        self.vocabulary().display_name(*stop)
4135                    )
4136                }
4137            })
4138            .collect::<Vec<_>>()
4139            .join(", ");
4140        format!("{{{values}}}")
4141    }
4142
4143    pub const fn rule_node(&self, context: ParserRuleContext) -> ParseTree {
4144        ParseTree::Rule(RuleNode::new(context))
4145    }
4146
4147    /// Enters a generated parser rule and returns the context object the
4148    /// generated method should populate.
4149    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
4150        self.set_state(state);
4151        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
4152        self.rule_context_stack.push(RuleContextFrame {
4153            rule_index,
4154            invoking_state,
4155        });
4156        self.invalidate_prediction_context_cache();
4157        let start_index = self.current_visible_index();
4158        let mut context = ParserRuleContext::new(rule_index, invoking_state);
4159        if let Some(token) = self.token_ref_at(start_index) {
4160            context.set_start_ref(token);
4161        }
4162        context
4163    }
4164
4165    /// Records the ATN source state for the next generated rule invocation.
4166    ///
4167    /// ANTLR's full-context prediction reconstructs caller follow states from
4168    /// each active rule context's invoking state. Generated Rust rule methods are
4169    /// plain functions, so the caller supplies that ATN state just before making a
4170    /// rule call; `enter_rule` consumes it when the callee starts.
4171    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
4172        let marker = self.pending_invoking_states.len();
4173        self.pending_invoking_states.push(invoking_state);
4174        marker
4175    }
4176
4177    /// Discards an invoking-state marker if the callee did not consume it.
4178    pub fn discard_invoking_state(&mut self, marker: usize) {
4179        self.pending_invoking_states.truncate(marker);
4180    }
4181
4182    /// Exits the current generated parser rule.
4183    pub fn exit_rule(&mut self) {
4184        self.rule_context_stack.pop();
4185        self.invalidate_prediction_context_cache();
4186    }
4187
4188    /// Converts the active generated-parser rule stack into an ANTLR prediction
4189    /// context for full-context adaptive prediction.
4190    pub fn prediction_context(&mut self, atn: &Atn) -> Rc<PredictionContext> {
4191        let atn_ptr: *const Atn = atn;
4192        let atn_key = atn_ptr as usize;
4193        if let Some(cached) = &self.prediction_context_cache
4194            && cached.version == self.rule_context_version
4195            && cached.atn_key == atn_key
4196        {
4197            return Rc::clone(&cached.context);
4198        }
4199        let mut context = PredictionContext::empty();
4200        for frame in self.rule_context_stack.iter().skip(1) {
4201            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
4202                continue;
4203            };
4204            let Some(Transition::Rule { follow_state, .. }) = atn
4205                .state(state_number)
4206                .and_then(|state| state.transitions.first())
4207            else {
4208                continue;
4209            };
4210            context = PredictionContext::singleton(context, *follow_state);
4211        }
4212        self.prediction_context_cache = Some(CachedPredictionContext {
4213            version: self.rule_context_version,
4214            atn_key,
4215            context: Rc::clone(&context),
4216        });
4217        context
4218    }
4219
4220    fn invalidate_prediction_context_cache(&mut self) {
4221        self.rule_context_version = self.rule_context_version.wrapping_add(1);
4222        self.prediction_context_cache = None;
4223    }
4224
4225    /// Adds a generated parser child only when parse-tree construction is
4226    /// enabled. The match is recorded on the context either way (via `add_child`,
4227    /// or `note_matched_child` when trees are off) so generated recovery can tell
4228    /// whether the rule has matched anything yet without depending on `children`.
4229    pub fn add_parse_child(&self, context: &mut ParserRuleContext, child: ParseTree) {
4230        if self.build_parse_trees {
4231            context.add_child(child);
4232        } else {
4233            context.note_matched_child();
4234        }
4235    }
4236
4237    /// Finishes a generated parser rule and returns its parse-tree node.
4238    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
4239        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
4240        if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
4241            context.set_stop_ref(token);
4242        }
4243        self.exit_rule();
4244        self.rule_node(context)
4245    }
4246
4247    /// Recovers a generated rule catch block after a committed mismatch.
4248    ///
4249    /// ANTLR's generated parsers catch recognition errors inside each rule,
4250    /// report the original error, then consume unexpected tokens until the
4251    /// caller's recovery set can resume. Tokens consumed during recovery become
4252    /// error nodes in the current rule context.
4253    pub fn recover_generated_rule(
4254        &mut self,
4255        context: &mut ParserRuleContext,
4256        atn: &Atn,
4257        error: AntlrError,
4258    ) {
4259        let diagnostic = self.generated_rule_error_diagnostic(error);
4260        self.push_generated_parser_diagnostic(diagnostic);
4261        self.generated_sync_expected = None;
4262        let recovery_symbols = self.context_expected_symbols(atn);
4263        loop {
4264            let symbol = self.la(1);
4265            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
4266                break;
4267            }
4268            let Some(token) = self.input.lt(1).cloned() else {
4269                break;
4270            };
4271            self.consume();
4272            self.add_parse_child(context, ParseTree::Error(ErrorNode::new(token)));
4273        }
4274        self.record_syntax_errors(1);
4275    }
4276
4277    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
4278        if self
4279            .generated_parser_diagnostics
4280            .iter()
4281            .any(|existing| existing == &diagnostic)
4282        {
4283            return;
4284        }
4285        self.generated_parser_diagnostics.push(diagnostic);
4286    }
4287
4288    fn generated_rule_error_diagnostic(&mut self, error: AntlrError) -> ParserDiagnostic {
4289        match error {
4290            AntlrError::ParserError {
4291                line,
4292                column,
4293                message,
4294            } => ParserDiagnostic {
4295                line,
4296                column,
4297                message,
4298            },
4299            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
4300                self.input.lt(1),
4301                format!("mismatched input {found} expecting {expected}"),
4302            ),
4303            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
4304                self.input.lt(1),
4305                format!("no viable alternative at input {input}"),
4306            ),
4307            AntlrError::LexerError {
4308                line,
4309                column,
4310                message,
4311            } => ParserDiagnostic {
4312                line,
4313                column,
4314                message,
4315            },
4316            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
4317        }
4318    }
4319
4320    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
4321    pub fn finish_recursion_rule(
4322        &mut self,
4323        mut context: ParserRuleContext,
4324        consumed_eof: bool,
4325    ) -> ParseTree {
4326        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
4327        if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
4328            context.set_stop_ref(token);
4329        }
4330        self.unroll_recursion_context();
4331        self.rule_node(context)
4332    }
4333
4334    /// Enters a generated left-recursive rule at `precedence`.
4335    pub fn enter_recursion_rule(
4336        &mut self,
4337        state: isize,
4338        rule_index: usize,
4339        precedence: i32,
4340    ) -> ParserRuleContext {
4341        self.precedence_stack.push(precedence);
4342        self.enter_rule(state, rule_index)
4343    }
4344
4345    /// Replaces the current context while expanding a left-recursive rule.
4346    pub fn push_new_recursion_context(
4347        &mut self,
4348        state: isize,
4349        rule_index: usize,
4350    ) -> ParserRuleContext {
4351        self.set_state(state);
4352        ParserRuleContext::new(rule_index, state)
4353    }
4354
4355    /// Wraps the previous left-recursive context before parsing the next
4356    /// recursive operator alternative.
4357    pub fn push_new_recursion_context_with_previous(
4358        &mut self,
4359        state: isize,
4360        rule_index: usize,
4361        current: &mut ParserRuleContext,
4362    ) {
4363        self.set_state(state);
4364        if let Some(stop) = self
4365            .rule_stop_token_index(self.input.index(), false)
4366            .and_then(|index| self.token_ref_at(index))
4367        {
4368            current.set_stop_ref(stop);
4369        }
4370        let invoking_state = current.invoking_state();
4371        let start = current.start_ref();
4372        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
4373        if let Some(start) = start {
4374            replacement.set_start_ref(start);
4375        }
4376        let previous = std::mem::replace(current, replacement);
4377        if self.build_parse_trees {
4378            current.add_child(self.rule_node(previous));
4379        }
4380    }
4381
4382    /// Leaves a generated left-recursive rule.
4383    pub fn unroll_recursion_context(&mut self) {
4384        if self.precedence_stack.len() > 1 {
4385            self.precedence_stack.pop();
4386        }
4387        self.exit_rule();
4388    }
4389
4390    /// Predicts a generated left-recursive loop from one-token lookahead.
4391    ///
4392    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
4393    /// `None` means caller overlap or an unresolved semantic predicate requires
4394    /// full-context adaptive prediction.
4395    pub fn left_recursive_loop_enter_prediction(
4396        &mut self,
4397        atn: &Atn,
4398        state_number: usize,
4399        precedence: i32,
4400    ) -> Option<bool> {
4401        let symbol = self.la(1);
4402        if symbol == TOKEN_EOF {
4403            return Some(false);
4404        }
4405        let operator_lookahead = with_shared_atn_caches(atn, |cache| {
4406            let key = (state_number, precedence);
4407            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
4408                return Rc::clone(cached);
4409            }
4410            let lookahead = Rc::new(left_recursive_operator_lookahead(
4411                atn,
4412                state_number,
4413                precedence,
4414            ));
4415            cache
4416                .left_recursive_operator_lookahead
4417                .insert(key, Rc::clone(&lookahead));
4418            lookahead
4419        });
4420        if !operator_lookahead.unconditional_symbols.contains(symbol) {
4421            if operator_lookahead
4422                .predicate_dependent_symbols
4423                .contains(symbol)
4424            {
4425                return None;
4426            }
4427            return Some(false);
4428        }
4429        let context = self.prediction_context(atn);
4430        let atn_key = SharedAtnCacheKey::for_atn(atn);
4431        let cached_overlap = self
4432            .left_recursive_caller_overlap_cache
4433            .iter()
4434            .flatten()
4435            .find(|entry| {
4436                entry.atn_key == atn_key
4437                    && entry.state_number == state_number
4438                    && entry.symbol == symbol
4439                    && entry.context == context
4440            })
4441            .map(|entry| entry.overlaps);
4442        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
4443            let overlaps =
4444                context_can_match_symbol_before_state(atn, &context, state_number, symbol);
4445            if let Some(slot) = self
4446                .left_recursive_caller_overlap_cache
4447                .iter_mut()
4448                .find(|slot| slot.is_none())
4449            {
4450                *slot = Some(LeftRecursiveCallerOverlap {
4451                    atn_key,
4452                    state_number,
4453                    symbol,
4454                    context: Rc::clone(&context),
4455                    overlaps,
4456                });
4457            }
4458            overlaps
4459        });
4460        if caller_overlaps {
4461            return None;
4462        }
4463        Some(true)
4464    }
4465
4466    /// Checks whether a generated left-recursive loop can unambiguously enter
4467    /// its operator alternative from one-token lookahead.
4468    pub fn left_recursive_loop_enter_matches(
4469        &mut self,
4470        atn: &Atn,
4471        state_number: usize,
4472        precedence: i32,
4473    ) -> bool {
4474        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
4475    }
4476
4477    /// Implements generated `precpred(_ctx, k)` checks.
4478    pub fn precpred(&self, precedence: i32) -> bool {
4479        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
4480    }
4481
4482    /// Evaluates a generated parser semantic predicate at the current input
4483    /// position.
4484    pub fn parser_semantic_predicate_matches(
4485        &mut self,
4486        predicates: &[(usize, usize, ParserPredicate)],
4487        rule_index: usize,
4488        pred_index: usize,
4489    ) -> bool {
4490        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
4491    }
4492
4493    /// Evaluates a generated parser semantic predicate with the current integer
4494    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
4495    pub fn parser_semantic_predicate_matches_with_local(
4496        &mut self,
4497        predicates: &[(usize, usize, ParserPredicate)],
4498        rule_index: usize,
4499        pred_index: usize,
4500        local_int_arg: i32,
4501    ) -> bool {
4502        self.parser_semantic_predicate_matches_inner(
4503            predicates,
4504            rule_index,
4505            pred_index,
4506            Some((rule_index, i64::from(local_int_arg))),
4507        )
4508    }
4509
4510    fn parser_semantic_predicate_matches_inner(
4511        &mut self,
4512        predicates: &[(usize, usize, ParserPredicate)],
4513        rule_index: usize,
4514        pred_index: usize,
4515        local_int_arg: Option<(usize, i64)>,
4516    ) -> bool {
4517        let index = self.input.index();
4518        let member_values = self.int_members.clone();
4519        self.parser_predicate_matches(PredicateEval {
4520            index,
4521            rule_index,
4522            pred_index,
4523            predicates,
4524            semantics: None,
4525            context: None,
4526            local_int_arg,
4527            member_values: &member_values,
4528        })
4529    }
4530
4531    /// Evaluates a generated parser semantic predicate with access to the
4532    /// current generated rule context.
4533    pub fn parser_semantic_predicate_matches_with_context_and_local(
4534        &mut self,
4535        predicates: &[(usize, usize, ParserPredicate)],
4536        rule_index: usize,
4537        pred_index: usize,
4538        context: &ParserRuleContext,
4539        local_int_arg: i32,
4540    ) -> bool {
4541        let index = self.input.index();
4542        let member_values = self.int_members.clone();
4543        self.parser_predicate_matches(PredicateEval {
4544            index,
4545            rule_index,
4546            pred_index,
4547            predicates,
4548            semantics: None,
4549            context: Some(context),
4550            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
4551            member_values: &member_values,
4552        })
4553    }
4554
4555    /// Evaluates a generated `SemIR` parser predicate with access to the current
4556    /// generated rule context.
4557    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
4558        &mut self,
4559        semantics: &ParserSemantics,
4560        rule_index: usize,
4561        pred_index: usize,
4562        context: &ParserRuleContext,
4563        local_int_arg: i32,
4564    ) -> bool {
4565        let index = self.input.index();
4566        let member_values = self.int_members.clone();
4567        self.parser_predicate_matches(PredicateEval {
4568            index,
4569            rule_index,
4570            pred_index,
4571            predicates: &[],
4572            semantics: Some(semantics),
4573            context: Some(context),
4574            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
4575            member_values: &member_values,
4576        })
4577    }
4578
4579    /// Returns a generated fail-option message for a parser semantic
4580    /// predicate coordinate.
4581    pub fn parser_semantic_predicate_failure_message(
4582        &self,
4583        rule_index: usize,
4584        pred_index: usize,
4585        predicates: &[(usize, usize, ParserPredicate)],
4586    ) -> Option<&'static str> {
4587        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
4588    }
4589
4590    /// Matches any non-EOF token.
4591    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
4592        let current = self
4593            .input
4594            .lt_ref(1)
4595            .ok_or_else(|| AntlrError::ParserError {
4596                line: 0,
4597                column: 0,
4598                message: "missing current token".to_owned(),
4599            })?;
4600        if current.token_type() == TOKEN_EOF {
4601            return Err(AntlrError::MismatchedInput {
4602                expected: "wildcard".to_owned(),
4603                found: self.vocabulary().display_name(TOKEN_EOF),
4604            });
4605        }
4606        self.consume();
4607        Ok(ParseTree::Terminal(TerminalNode::from_ref(current)))
4608    }
4609
4610    /// Generated parser synchronization hook. The current interpreter owns
4611    /// recovery; direct generated methods can call this as a no-op until the
4612    /// generated recovery strategy is expanded.
4613    #[allow(clippy::unnecessary_wraps)]
4614    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
4615        self.set_state(state);
4616        Ok(())
4617    }
4618
4619    /// Synchronizes a generated parser decision against the ATN lookahead set.
4620    ///
4621    /// ANTLR generated parsers call the error strategy before optional and loop
4622    /// decisions. When the current token cannot start any alternative, follow a
4623    /// nullable exit, or be deleted before a later synchronization token, the
4624    /// generated Rust method reports that decision-level mismatch instead of
4625    /// descending into a child rule that cannot start at the current token.
4626    pub fn sync_decision(
4627        &mut self,
4628        atn: &Atn,
4629        state_number: usize,
4630        current_context_empty: bool,
4631        loop_back: bool,
4632    ) -> Result<Vec<ParseTree>, AntlrError> {
4633        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
4634        self.generated_sync_expected = None;
4635        let Some(state) = atn.state(state_number) else {
4636            return Ok(Vec::new());
4637        };
4638        let Some(rule_index) = state.rule_index else {
4639            return Ok(Vec::new());
4640        };
4641        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index).copied() else {
4642            return Ok(Vec::new());
4643        };
4644        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
4645        let symbol = self.la(1);
4646        let mut has_expected_symbols = false;
4647        let mut nullable = false;
4648        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
4649        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
4650        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
4651        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
4652        // and worth deleting; an implicit-follow EOF means the loop should simply
4653        // exit and leave the token for the (absent) caller — matching ANTLR, which
4654        // exits the loop via prediction rather than consuming up to a synthetic EOF.
4655        let mut explicit_eof_expected = false;
4656        for transition in &entry.transitions {
4657            if transition.symbols.contains(symbol) {
4658                return Ok(Vec::new());
4659            }
4660            has_expected_symbols |= !transition.symbols.is_empty();
4661            nullable |= transition.nullable;
4662            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
4663        }
4664        // Happy path: a nullable decision exits when the symbol is in the
4665        // rule-stack follow set. Answer the membership question with an
4666        // early-exit walk; the full union below is only needed for the
4667        // mismatch/deletion diagnostics.
4668        if nullable && self.context_expected_contains(atn, symbol) {
4669            return Ok(Vec::new());
4670        }
4671        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
4672        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
4673            return Ok(Vec::new());
4674        }
4675        let mut expected = TokenBitSet::default();
4676        for transition in &entry.transitions {
4677            expected.extend_from(&transition.symbols);
4678        }
4679        if let Some(context_expected) = context_expected {
4680            expected.extend_from(&context_expected);
4681        }
4682        let can_delete_in_place =
4683            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
4684        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
4685        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
4686        // least one iteration) does `consumeUntil` the follow set — multi-token
4687        // deletion, one error per skipped token across iterations; a loop ENTRY
4688        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
4689        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
4690        // unexpected token only when LA(2) is expected, otherwise report a mismatch
4691        // and leave recovery to the rule.
4692        //
4693        // The generated loop always presents the loop-ENTRY state to this method on
4694        // every pass, so `state.kind` cannot distinguish entry from back; the caller
4695        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
4696        // an iteration has been taken, and true on a `+` loop's first sync since its
4697        // mandatory first element is iteration 1). Treating a loop entry as a
4698        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
4699        // `c`s, which ANTLR rejects with `mismatched input`).
4700        let loop_sync = loop_back;
4701        if symbol != TOKEN_EOF && can_delete_in_place {
4702            let mut cursor = self.input.index();
4703            let mut skipped = Vec::new();
4704            loop {
4705                let current = self.token_type_at(cursor);
4706                if current == TOKEN_EOF {
4707                    break;
4708                }
4709                skipped.push(cursor);
4710                let next = self.consume_index(cursor, current);
4711                if next == cursor {
4712                    break;
4713                }
4714                let next_symbol = self.token_type_at(next);
4715                // Stop (and delete the skipped tokens as error nodes) when the next
4716                // token is a real expected continuation. EOF counts only when it is
4717                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
4718                // genuinely extraneous and the generated EOF match consumes the real
4719                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
4720                // rule-follow) does NOT count — the loop must exit and leave the
4721                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
4722                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
4723                    explicit_eof_expected
4724                } else {
4725                    expected.contains(next_symbol)
4726                };
4727                if next_is_expected_stop {
4728                    let current_token = self.input.lt(1).cloned();
4729                    let expected_symbols = expected.to_btree_set();
4730                    let message = format!(
4731                        "extraneous input {} expecting {}",
4732                        current_token
4733                            .as_ref()
4734                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
4735                        self.expected_symbols_display(&expected_symbols)
4736                    );
4737                    self.push_generated_parser_diagnostic(diagnostic_for_token(
4738                        current_token.as_ref(),
4739                        message,
4740                    ));
4741                    self.record_syntax_errors(1);
4742                    let mut children = Vec::with_capacity(skipped.len());
4743                    for index in skipped {
4744                        if let Some(token) = self.token_at(index) {
4745                            self.consume();
4746                            children.push(ParseTree::Error(ErrorNode::new(token)));
4747                        }
4748                    }
4749                    return Ok(children);
4750                }
4751                // A non-loop block entry deletes at most one token (single-token
4752                // deletion): if LA(2) is not expected, stop scanning so the mismatch
4753                // is reported at the first token instead of skipping ahead.
4754                if !loop_sync {
4755                    break;
4756                }
4757                cursor = next;
4758            }
4759        }
4760        if nullable {
4761            self.generated_sync_expected = Some(expected);
4762            return Ok(Vec::new());
4763        }
4764        let current = self.input.lt(1).cloned();
4765        let expected_symbols = expected.to_btree_set();
4766        Err(AntlrError::ParserError {
4767            line: current.as_ref().map(Token::line).unwrap_or_default(),
4768            column: current.as_ref().map(Token::column).unwrap_or_default(),
4769            message: format!(
4770                "mismatched input {} expecting {}",
4771                current
4772                    .as_ref()
4773                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
4774                self.expected_symbols_display(&expected_symbols)
4775            ),
4776        })
4777    }
4778
4779    /// Returns a generated-parser prediction when one token of lookahead
4780    /// uniquely selects an alternative for `state_number`.
4781    ///
4782    /// This mirrors the interpreter's LL(1) commit point and lets generated
4783    /// recursive-descent methods avoid invoking the adaptive simulator for
4784    /// simple optional/block/loop decisions.
4785    pub fn ll1_decision_prediction(
4786        &mut self,
4787        atn: &Atn,
4788        state_number: usize,
4789    ) -> Option<ParserAtnPrediction> {
4790        let state = atn.state(state_number)?;
4791        if state.precedence_rule_decision {
4792            return None;
4793        }
4794        let rule_stop = state
4795            .rule_index
4796            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index).copied())?;
4797        let symbol = self.la(1);
4798        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
4799        ll1_greedy_alt(&entry, symbol, state.non_greedy).map(|alt| ParserAtnPrediction {
4800            alt: alt + 1,
4801            requires_full_context: false,
4802            has_semantic_context: false,
4803            diagnostic: None,
4804        })
4805    }
4806
4807    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
4808        let context = self.prediction_context(atn);
4809        let mut expected = BTreeSet::new();
4810        self.collect_context_expected_symbols(atn, &context, &mut expected);
4811        expected
4812    }
4813
4814    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
4815        let context = self.prediction_context(atn);
4816        let mut expected = TokenBitSet::default();
4817        self.collect_context_expected_token_set(atn, &context, &mut expected);
4818        expected
4819    }
4820
4821    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
4822    /// without materializing the union.
4823    ///
4824    /// This walks the rule-invocation stack directly, innermost frame first —
4825    /// the same frames, in the same order, with the same rule-stop gating as
4826    /// `collect_context_expected_token_set` over `prediction_context(atn)`
4827    /// (whose chain head is the innermost frame's follow state). The nullable
4828    /// exit in `sync_decision` asks only this membership question, and on
4829    /// valid input the innermost frame answers it, so the early exit replaces
4830    /// an O(stack-depth) set union per loop/optional exit with one probe.
4831    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
4832        for index in (1..self.rule_context_stack.len()).rev() {
4833            let invoking_state = self.rule_context_stack[index].invoking_state;
4834            let Ok(state_number) = usize::try_from(invoking_state) else {
4835                continue;
4836            };
4837            let Some(Transition::Rule { follow_state, .. }) = atn
4838                .state(state_number)
4839                .and_then(|state| state.transitions.first())
4840            else {
4841                continue;
4842            };
4843            let follow_state = *follow_state;
4844            if self
4845                .cached_state_expected_token_set(atn, follow_state)
4846                .contains(symbol)
4847            {
4848                return true;
4849            }
4850            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
4851                return false;
4852            }
4853        }
4854        symbol == TOKEN_EOF
4855    }
4856
4857    fn collect_context_expected_symbols(
4858        &mut self,
4859        atn: &Atn,
4860        context: &Rc<PredictionContext>,
4861        expected: &mut BTreeSet<i32>,
4862    ) {
4863        if context.is_empty() {
4864            expected.insert(TOKEN_EOF);
4865            return;
4866        }
4867        for index in 0..context.len() {
4868            let Some(return_state) = context.return_state(index) else {
4869                continue;
4870            };
4871            if return_state == EMPTY_RETURN_STATE {
4872                expected.insert(TOKEN_EOF);
4873                continue;
4874            }
4875            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
4876            if self.cached_state_can_reach_rule_stop(atn, return_state)
4877                && let Some(parent) = context.parent(index)
4878            {
4879                self.collect_context_expected_symbols(atn, &parent, expected);
4880            }
4881        }
4882    }
4883
4884    fn collect_context_expected_token_set(
4885        &mut self,
4886        atn: &Atn,
4887        context: &Rc<PredictionContext>,
4888        expected: &mut TokenBitSet,
4889    ) {
4890        if context.is_empty() {
4891            expected.insert(TOKEN_EOF);
4892            return;
4893        }
4894        for index in 0..context.len() {
4895            let Some(return_state) = context.return_state(index) else {
4896                continue;
4897            };
4898            if return_state == EMPTY_RETURN_STATE {
4899                expected.insert(TOKEN_EOF);
4900                continue;
4901            }
4902            let state_expected = self.cached_state_expected_token_set(atn, return_state);
4903            expected.extend_from(&state_expected);
4904            if self.cached_state_can_reach_rule_stop(atn, return_state)
4905                && let Some(parent) = context.parent(index)
4906            {
4907                self.collect_context_expected_token_set(atn, &parent, expected);
4908            }
4909        }
4910    }
4911
4912    /// Builds a generated no-viable-alternative parser error.
4913    pub fn no_viable_alternative_error(&mut self, start_index: usize) -> AntlrError {
4914        let error_index = self.input.index();
4915        self.no_viable_alternative_error_at(start_index, error_index)
4916    }
4917
4918    /// Builds a generated no-viable-alternative parser error at the simulator's
4919    /// failing lookahead index. `adaptive_predict` restores the input cursor
4920    /// before returning, so generated parsers have to pass the recorded index
4921    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
4922    pub fn no_viable_alternative_error_at(
4923        &mut self,
4924        start_index: usize,
4925        error_index: usize,
4926    ) -> AntlrError {
4927        let diagnostic = self.no_viable_alternative(start_index, error_index);
4928        AntlrError::ParserError {
4929            line: diagnostic.line,
4930            column: diagnostic.column,
4931            message: diagnostic.message,
4932        }
4933    }
4934
4935    /// Builds a generated failed-predicate parser error.
4936    pub fn failed_predicate_error(&mut self, message: impl Into<String>) -> AntlrError {
4937        let current = self.input.lt(1).cloned();
4938        AntlrError::ParserError {
4939            line: current.as_ref().map(Token::line).unwrap_or_default(),
4940            column: current.as_ref().map(Token::column).unwrap_or_default(),
4941            message: format!("rule failed predicate: {}", message.into()),
4942        }
4943    }
4944
4945    /// Builds a generated parser error for a semantic predicate with ANTLR's
4946    /// `<fail='...'>` option.
4947    pub fn failed_predicate_option_error(
4948        &mut self,
4949        rule_index: usize,
4950        message: impl Into<String>,
4951    ) -> AntlrError {
4952        let current = self.input.lt(1).cloned();
4953        let rule_name = self
4954            .rule_names()
4955            .get(rule_index)
4956            .map_or_else(|| rule_index.to_string(), Clone::clone);
4957        AntlrError::ParserError {
4958            line: current.as_ref().map(Token::line).unwrap_or_default(),
4959            column: current.as_ref().map(Token::column).unwrap_or_default(),
4960            message: format!("rule {rule_name} {}", message.into()),
4961        }
4962    }
4963
4964    /// Builds a generated parser-action event at the current input position.
4965    pub fn parser_action_at_current(
4966        &mut self,
4967        source_state: usize,
4968        rule_index: usize,
4969        start_index: usize,
4970        consumed_eof: bool,
4971    ) -> ParserAction {
4972        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
4973        ParserAction::new(source_state, rule_index, start_index, stop_index)
4974    }
4975
4976    /// Offers a committed parser action event to the user semantic hook.
4977    ///
4978    /// Generated parsers call this for action source states that were present
4979    /// in the ATN but not translated into a built-in Rust action template.
4980    pub fn parser_action_hook(&mut self, action: ParserAction, tree: &ParseTree) -> bool {
4981        let rule_index = action.rule_index();
4982        let rule_name = self.rule_names().get(rule_index).cloned();
4983        let context = match tree {
4984            ParseTree::Rule(rule) if rule.context().rule_index() == rule_index => {
4985                Some(rule.context())
4986            }
4987            ParseTree::Rule(_) | ParseTree::Terminal(_) | ParseTree::Error(_) => None,
4988        };
4989        let input = &mut self.input;
4990        let semantic_hooks = &mut self.semantic_hooks;
4991        let member_values = &self.int_members;
4992        let mut ctx = ParserSemCtx {
4993            input,
4994            rule_index,
4995            coordinate_index: usize::MAX,
4996            rule_name,
4997            context,
4998            tree: Some(tree),
4999            local_int_arg: None,
5000            member_values,
5001            action: Some(action),
5002        };
5003        let handled = semantic_hooks.action(&mut ctx, action);
5004        // This action reached the hook because it had no translated arm. If no
5005        // hook handled it either (`SemanticHooks::action` returns `false`), the
5006        // committed action is silently dropped — record it so the parse entry
5007        // can fail loud under the fail-loud boundary, mirroring unknown
5008        // predicates. `assume-*` policies opt out of the fail-loud recording.
5009        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
5010            let coordinate = (rule_index, action.source_state());
5011            if !self.unhandled_action_hits.contains(&coordinate) {
5012                self.unhandled_action_hits.push(coordinate);
5013            }
5014        }
5015        handled
5016    }
5017
5018    /// Attempts to execute a whole generated rule by committing simulator
5019    /// decisions directly. Unsupported constructs or decisions that need
5020    /// full-context / predicate evaluation restore the input cursor and fall
5021    /// back to [`Self::parse_atn_rule`].
5022    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
5023        &mut self,
5024        atn: &'atn Atn,
5025        simulator: &mut ParserAtnSimulator<'atn>,
5026        rule_index: usize,
5027    ) -> Result<ParseTree, AntlrError> {
5028        let start_index = self.current_visible_index();
5029        self.clear_prediction_diagnostics();
5030        self.reset_per_parse_caches();
5031        let mut decision_by_state = vec![None; atn.states().len()];
5032        for (decision, &state_number) in atn.decision_to_state().iter().enumerate() {
5033            if let Some(slot) = decision_by_state.get_mut(state_number) {
5034                *slot = Some(decision);
5035            }
5036        }
5037
5038        let result = DirectAdaptiveParser {
5039            parser: self,
5040            atn,
5041            simulator,
5042            decision_by_state,
5043            steps: 0,
5044        }
5045        .parse_rule(rule_index, -1, 0);
5046
5047        match result {
5048            Ok(tree) => {
5049                report_token_source_errors(&self.input.drain_source_errors());
5050                Ok(tree)
5051            }
5052            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
5053                let _ = reason;
5054                self.input.seek(start_index);
5055                self.parse_atn_rule(atn, rule_index)
5056            }
5057        }
5058    }
5059
5060    /// Parses a generated rule by interpreting the parser ATN from the rule's
5061    /// start state to its stop state.
5062    ///
5063    /// The recognizer backtracks across alternatives and loop exits using token
5064    /// stream indices instead of committing to input consumption immediately.
5065    /// Once a viable ATN path is found, the parser commits the accepted token
5066    /// interval and returns a rule node whose children mirror every grammar
5067    /// rule invocation reached on that path, matching ANTLR's parse-tree
5068    /// shape.
5069    pub fn parse_atn_rule(
5070        &mut self,
5071        atn: &Atn,
5072        rule_index: usize,
5073    ) -> Result<ParseTree, AntlrError> {
5074        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
5075    }
5076
5077    /// Parses a generated rule by interpreting the parser ATN with an initial
5078    /// left-recursive precedence threshold.
5079    pub fn parse_atn_rule_with_precedence(
5080        &mut self,
5081        atn: &Atn,
5082        rule_index: usize,
5083        precedence: i32,
5084    ) -> Result<ParseTree, AntlrError> {
5085        let start_state = atn
5086            .rule_to_start_state()
5087            .get(rule_index)
5088            .copied()
5089            .ok_or_else(|| {
5090                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
5091            })?;
5092        let stop_state = atn
5093            .rule_to_stop_state()
5094            .get(rule_index)
5095            .copied()
5096            .filter(|state| *state != usize::MAX)
5097            .ok_or_else(|| {
5098                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
5099            })?;
5100
5101        let start_index = self.current_visible_index();
5102        self.clear_prediction_diagnostics();
5103        self.reset_per_parse_caches();
5104        let caller_follow_state = self.pending_invoking_follow_state(atn);
5105        self.fast_recovery_enabled = false;
5106        self.fast_token_nodes_enabled = false;
5107        let top_request = FastRecognizeTopRequest {
5108            start_state,
5109            stop_state,
5110            start_index,
5111            precedence,
5112            caller_follow_state,
5113        };
5114        let first_pass = self.fast_recognize_top(atn, top_request);
5115        self.fast_token_nodes_enabled = true;
5116        let needs_tree_retry = matches!(
5117            &first_pass,
5118            Ok((outcome, _)) if self.build_parse_trees && outcome.nodes.has_left_recursive_boundary()
5119        );
5120        let needs_retry = match &first_pass {
5121            // The FIRST-set prefilter trims speculative rule calls that can't
5122            // match the current lookahead — useful for perf on grammars with
5123            // many epsilon-reachable rules, but the trim also bypasses
5124            // single-token insertion / deletion recovery that ANTLR's
5125            // reference parser runs at the child rule's first consuming
5126            // transition. Retry without the prefilter whenever the first pass
5127            // either produced no outcome at all or produced a recovered
5128            // outcome (diagnostics non-empty), since the second pass might
5129            // surface a child-level recovery with cleaner diagnostics or
5130            // closer parity to ANTLR's tree shape. Left-recursive tree
5131            // boundaries also need the token-node pass; otherwise the fold has
5132            // no concrete left operand to wrap into ANTLR's recursive context.
5133            Err(_) => true,
5134            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
5135        };
5136        let (outcome, _expected) = if needs_retry {
5137            self.fast_first_set_prefilter = false;
5138            self.fast_recovery_enabled = false;
5139            let clean_retry = self.fast_recognize_top(atn, top_request);
5140            let clean_selected = if needs_tree_retry {
5141                match clean_retry {
5142                    ok @ Ok(_) => ok,
5143                    Err(_) => first_pass,
5144                }
5145            } else {
5146                select_better_top_outcome(first_pass, clean_retry)
5147            };
5148            let selected = if clean_selected.is_err()
5149                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
5150            {
5151                self.fast_recovery_enabled = true;
5152                let recovery_retry = self.fast_recognize_top(atn, top_request);
5153                select_better_top_outcome(clean_selected, recovery_retry)
5154            } else {
5155                clean_selected
5156            };
5157            self.fast_first_set_prefilter = true;
5158            self.fast_recovery_enabled = true;
5159            selected.map_err(|expected| {
5160                let error = self.recognition_error(rule_index, start_index, &expected);
5161                self.record_syntax_errors(1);
5162                report_token_source_errors(&self.input.drain_source_errors());
5163                error
5164            })?
5165        } else {
5166            first_pass.expect("first_pass is Ok in the no-retry branch")
5167        };
5168        self.record_syntax_errors(outcome.diagnostics.len());
5169        report_parser_diagnostics(&self.prediction_diagnostics);
5170        report_parser_diagnostics(&outcome.diagnostics);
5171        report_token_source_errors(&self.input.drain_source_errors());
5172        let mut context = ParserRuleContext::with_child_capacity(
5173            rule_index,
5174            self.state(),
5175            if self.build_parse_trees {
5176                outcome.nodes.len()
5177            } else {
5178                0
5179            },
5180        );
5181        if let Some(token) = self.token_ref_at(start_index) {
5182            context.set_start_ref(token);
5183        }
5184        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
5185        if let Some(token) = stop_index.and_then(|token_index| self.token_ref_at(token_index)) {
5186            context.set_stop_ref(token);
5187        }
5188        if self.build_parse_trees {
5189            if outcome.nodes.has_left_recursive_boundary() {
5190                let folded = fold_fast_left_recursive_boundaries(outcome.nodes.to_vec());
5191                if folded.iter().any(|node| {
5192                    matches!(
5193                        node.as_ref(),
5194                        FastRecognizedNode::Token { .. }
5195                            | FastRecognizedNode::ErrorToken { .. }
5196                            | FastRecognizedNode::MissingToken { .. }
5197                    )
5198                }) {
5199                    for node in &folded {
5200                        context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
5201                    }
5202                } else {
5203                    self.add_fast_implicit_token_children(
5204                        &mut context,
5205                        start_index,
5206                        stop_index,
5207                        &folded,
5208                    )?;
5209                }
5210            } else if outcome.nodes.has_explicit_token_node() {
5211                for node in outcome.nodes.iter() {
5212                    context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
5213                }
5214            } else {
5215                self.add_fast_implicit_token_children_iter(
5216                    &mut context,
5217                    start_index,
5218                    stop_index,
5219                    outcome.nodes.iter(),
5220                )?;
5221            }
5222        }
5223        self.input.seek(outcome.index);
5224
5225        Ok(self.rule_node(context))
5226    }
5227
5228    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
5229        let invoking_state = self.pending_invoking_states.last().copied()?;
5230        let state_number = usize::try_from(invoking_state).ok()?;
5231        match atn.state(state_number)?.transitions.first()? {
5232            Transition::Rule { follow_state, .. } => Some(*follow_state),
5233            _ => None,
5234        }
5235    }
5236
5237    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
5238        // Generated callers own statement separators; leave them available when
5239        // an interpreted child rule can either stop before or consume one.
5240        let token_type = self.token_type_at(index);
5241        let visible_channel = self.input.channel();
5242        let token = self.token_at(index);
5243        let is_boundary = token
5244            .as_ref()
5245            .and_then(Token::text)
5246            .is_some_and(is_caller_follow_boundary_text);
5247        let is_boundary_gap = token.as_ref().is_some_and(|token| {
5248            token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
5249        });
5250        (token_type, is_boundary, is_boundary_gap)
5251    }
5252
5253    /// Runs the fast recognizer once from the rule's start state and returns
5254    /// the best outcome or the per-attempt expected-token accumulator. The
5255    /// caller flips `fast_first_set_prefilter` between calls when a retry is
5256    /// needed, so the FIRST-set cache is left intact across both passes.
5257    fn fast_recognize_top(
5258        &mut self,
5259        atn: &Atn,
5260        request: FastRecognizeTopRequest,
5261    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
5262        let FastRecognizeTopRequest {
5263            start_state,
5264            stop_state,
5265            start_index,
5266            precedence,
5267            caller_follow_state,
5268        } = request;
5269        // `input.size()` is intentionally only the currently buffered token
5270        // count here. Do not restore an up-front fill just to size this map:
5271        // the fixed floor avoids small-input churn, and large inputs grow the
5272        // cache after the deferred-fill threshold without forcing startup
5273        // tokenization. The 8x multiplier matches the empirical
5274        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
5275        // Kotlin ~12× memo entries per token), so the table avoids one
5276        // rehash on the typical hot path.
5277        let memo_capacity = self.input.size().saturating_mul(8).clamp(65_536, 524_288);
5278        let mut visiting = FxHashSet::with_capacity_and_hasher(256, FxBuildHasher::default());
5279        let mut memo = FxHashMap::with_capacity_and_hasher(memo_capacity, FxBuildHasher::default());
5280        let mut expected = ExpectedTokens::default();
5281        let empty_recovery = self.empty_recovery_symbols();
5282        let outcomes = self.recognize_state_fast(
5283            atn,
5284            FastRecognizeRequest {
5285                state_number: start_state,
5286                stop_state,
5287                index: start_index,
5288                rule_start_index: start_index,
5289                decision_start_index: None,
5290                precedence,
5291                depth: 0,
5292                recovery_symbols: empty_recovery,
5293                recovery_state: None,
5294            },
5295            &mut visiting,
5296            &mut memo,
5297            &mut expected,
5298        );
5299        #[cfg(feature = "perf-counters")]
5300        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
5301            perf_counters::dump();
5302            perf_counters::reset();
5303        }
5304        let caller_follow =
5305            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
5306        match select_best_fast_outcome(
5307            outcomes.into_iter(),
5308            self.prediction_mode,
5309            caller_follow.as_deref(),
5310            |index| self.caller_follow_token_info(index),
5311        ) {
5312            Some(outcome) => Ok((outcome, expected)),
5313            None => Err(expected),
5314        }
5315    }
5316
5317    /// Converts a recognized fast-recognizer node into a public parse-tree
5318    /// node, mirroring [`Self::recognized_node_tree`] for the slow path.
5319    fn fast_recognized_node_tree(
5320        &mut self,
5321        node: &FastRecognizedNode,
5322    ) -> Result<ParseTree, AntlrError> {
5323        match node {
5324            FastRecognizedNode::Token { index } => {
5325                let token = self
5326                    .input
5327                    .get_ref(*index)
5328                    .ok_or_else(|| AntlrError::ParserError {
5329                        line: 0,
5330                        column: 0,
5331                        message: format!("missing token at index {index}"),
5332                    })?;
5333                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
5334            }
5335            FastRecognizedNode::ErrorToken { index } => {
5336                let token = self
5337                    .input
5338                    .get_ref(*index)
5339                    .ok_or_else(|| AntlrError::ParserError {
5340                        line: 0,
5341                        column: 0,
5342                        message: format!("missing error token at index {index}"),
5343                    })?;
5344                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
5345            }
5346            FastRecognizedNode::MissingToken {
5347                token_type,
5348                at_index,
5349                text,
5350            } => {
5351                let current = self.token_at(*at_index);
5352                let token = CommonToken::new(*token_type)
5353                    .with_text(text.as_str())
5354                    .with_span(usize::MAX, usize::MAX)
5355                    .with_position(
5356                        current.as_ref().map(Token::line).unwrap_or_default(),
5357                        current.as_ref().map(Token::column).unwrap_or_default(),
5358                    );
5359                Ok(ParseTree::Error(ErrorNode::new(token)))
5360            }
5361            FastRecognizedNode::Rule {
5362                rule_index,
5363                invoking_state,
5364                start_index,
5365                stop_index,
5366                children,
5367            } => {
5368                let mut context = ParserRuleContext::with_child_capacity(
5369                    *rule_index,
5370                    *invoking_state,
5371                    children.len(),
5372                );
5373                if let Some(token) = self.token_ref_at(*start_index) {
5374                    context.set_start_ref(token);
5375                }
5376                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
5377                    context.set_stop_ref(token);
5378                }
5379                if children.has_left_recursive_boundary() {
5380                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
5381                    for child in &folded {
5382                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
5383                    }
5384                } else {
5385                    for child in children.iter() {
5386                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
5387                    }
5388                }
5389                Ok(self.rule_node(context))
5390            }
5391            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
5392                Err(AntlrError::Unsupported(format!(
5393                    "unfolded left-recursive boundary for rule {rule_index}"
5394                )))
5395            }
5396        }
5397    }
5398
5399    fn fast_recognized_node_tree_with_implicit_tokens(
5400        &mut self,
5401        node: &FastRecognizedNode,
5402    ) -> Result<ParseTree, AntlrError> {
5403        match node {
5404            FastRecognizedNode::Rule {
5405                rule_index,
5406                invoking_state,
5407                start_index,
5408                stop_index,
5409                children,
5410            } => {
5411                let mut context = ParserRuleContext::with_child_capacity(
5412                    *rule_index,
5413                    *invoking_state,
5414                    children.len(),
5415                );
5416                if let Some(token) = self.token_ref_at(*start_index) {
5417                    context.set_start_ref(token);
5418                }
5419                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
5420                    context.set_stop_ref(token);
5421                }
5422                if children.has_left_recursive_boundary() {
5423                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
5424                    self.add_fast_implicit_token_children(
5425                        &mut context,
5426                        *start_index,
5427                        *stop_index,
5428                        &folded,
5429                    )?;
5430                } else {
5431                    self.add_fast_implicit_token_children_iter(
5432                        &mut context,
5433                        *start_index,
5434                        *stop_index,
5435                        children.iter(),
5436                    )?;
5437                }
5438                Ok(self.rule_node(context))
5439            }
5440            _ => self.fast_recognized_node_tree(node),
5441        }
5442    }
5443
5444    fn add_fast_implicit_token_children(
5445        &mut self,
5446        context: &mut ParserRuleContext,
5447        start_index: usize,
5448        stop_index: Option<usize>,
5449        children: &[Rc<FastRecognizedNode>],
5450    ) -> Result<(), AntlrError> {
5451        self.add_fast_implicit_token_children_iter(
5452            context,
5453            start_index,
5454            stop_index,
5455            children.iter(),
5456        )
5457    }
5458
5459    fn add_fast_implicit_token_children_iter<'a>(
5460        &mut self,
5461        context: &mut ParserRuleContext,
5462        start_index: usize,
5463        stop_index: Option<usize>,
5464        children: impl IntoIterator<Item = &'a Rc<FastRecognizedNode>>,
5465    ) -> Result<(), AntlrError> {
5466        let mut cursor = Some(start_index);
5467        for child in children {
5468            if let Some((child_start, child_stop)) = fast_recognized_node_span(child.as_ref()) {
5469                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
5470                context.add_child(
5471                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
5472                );
5473                if let Some(child_stop) = child_stop {
5474                    cursor = self.next_visible_after_token(child_stop);
5475                }
5476            } else {
5477                context.add_child(
5478                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
5479                );
5480            }
5481        }
5482        if let Some(stop) = stop_index {
5483            self.add_visible_terminals_through(context, cursor, stop)?;
5484        }
5485        Ok(())
5486    }
5487
5488    fn add_visible_terminals_before(
5489        &mut self,
5490        context: &mut ParserRuleContext,
5491        cursor: &mut Option<usize>,
5492        before: usize,
5493    ) -> Result<(), AntlrError> {
5494        let Some(stop) = before.checked_sub(1) else {
5495            return Ok(());
5496        };
5497        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
5498        *cursor = next;
5499        Ok(())
5500    }
5501
5502    fn add_visible_terminals_through(
5503        &mut self,
5504        context: &mut ParserRuleContext,
5505        mut cursor: Option<usize>,
5506        stop: usize,
5507    ) -> Result<Option<usize>, AntlrError> {
5508        while let Some(index) = cursor {
5509            if index > stop {
5510                return Ok(Some(index));
5511            }
5512            let token = self
5513                .input
5514                .get_ref(index)
5515                .ok_or_else(|| AntlrError::ParserError {
5516                    line: 0,
5517                    column: 0,
5518                    message: format!("missing token at index {index}"),
5519                })?;
5520            let is_eof = token.token_type() == TOKEN_EOF;
5521            context.add_child(ParseTree::Terminal(TerminalNode::from_ref(token)));
5522            if is_eof {
5523                return Ok(None);
5524            }
5525            cursor = self.next_visible_after_token(index);
5526        }
5527        Ok(None)
5528    }
5529
5530    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
5531        let next = self.input.next_visible_after(index);
5532        (next != index).then_some(next)
5533    }
5534
5535    /// Parses a generated rule and returns semantic actions reached on the
5536    /// selected ATN path.
5537    ///
5538    /// This slower path preserves action ordering and token intervals for
5539    /// generated code that replays target-specific action templates after the
5540    /// recognizer has chosen one viable parse path.
5541    pub fn parse_atn_rule_with_actions(
5542        &mut self,
5543        atn: &Atn,
5544        rule_index: usize,
5545    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5546        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
5547    }
5548
5549    /// Parses a generated rule and emits ATN actions plus selected rule-init
5550    /// actions reached on the chosen path.
5551    ///
5552    /// Generated parsers use this when a grammar contains rule-level `@init`
5553    /// templates that must run for nested rule invocations. The runtime keeps
5554    /// the action list path-sensitive, so init templates are replayed only for
5555    /// rules that were actually entered by the selected parse.
5556    pub fn parse_atn_rule_with_action_inits(
5557        &mut self,
5558        atn: &Atn,
5559        rule_index: usize,
5560        init_action_rules: &[usize],
5561    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5562        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
5563    }
5564
5565    /// Parses a generated rule with optional semantic-action replay features.
5566    ///
5567    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
5568    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
5569    /// unchanged for grammars that do not request that target template.
5570    pub fn parse_atn_rule_with_action_options(
5571        &mut self,
5572        atn: &Atn,
5573        rule_index: usize,
5574        init_action_rules: &[usize],
5575        track_alt_numbers: bool,
5576    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5577        self.parse_atn_rule_with_runtime_options(
5578            atn,
5579            rule_index,
5580            ParserRuntimeOptions {
5581                init_action_rules,
5582                track_alt_numbers,
5583                ..ParserRuntimeOptions::default()
5584            },
5585        )
5586    }
5587
5588    /// Parses a generated rule with action replay and parser predicate support.
5589    ///
5590    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
5591    /// target-template predicate semantics emitted by the generator. Missing
5592    /// entries are treated as true so unsupported predicate-free grammars keep
5593    /// the previous unconditional transition behavior.
5594    pub fn parse_atn_rule_with_runtime_options(
5595        &mut self,
5596        atn: &Atn,
5597        rule_index: usize,
5598        options: ParserRuntimeOptions<'_>,
5599    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5600        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
5601    }
5602
5603    /// Parses a generated rule with action replay, parser predicate support,
5604    /// and an initial left-recursive precedence threshold.
5605    pub fn parse_atn_rule_with_runtime_options_and_precedence(
5606        &mut self,
5607        atn: &Atn,
5608        rule_index: usize,
5609        precedence: i32,
5610        options: ParserRuntimeOptions<'_>,
5611    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5612        let ParserRuntimeOptions {
5613            init_action_rules,
5614            track_alt_numbers,
5615            predicates,
5616            semantics,
5617            rule_args,
5618            member_actions,
5619            return_actions,
5620            unknown_predicate_policy,
5621        } = options;
5622        if init_action_rules.is_empty()
5623            && !track_alt_numbers
5624            && predicates.is_empty()
5625            && semantics.is_none()
5626            && rule_args.is_empty()
5627            && member_actions.is_empty()
5628            && return_actions.is_empty()
5629            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
5630            && !atn_has_observable_action_transitions(atn)
5631            && (!self.semantic_hooks.observes_parser_predicates()
5632                || !atn_has_predicate_transitions(atn))
5633        {
5634            return self
5635                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
5636                .map(|tree| (tree, Vec::new()));
5637        }
5638        self.unknown_predicate_policy = unknown_predicate_policy;
5639        // A generated parent may have already recorded unknown-predicate
5640        // coordinates before descending into this (interpreted) child. Clearing
5641        // unconditionally would drop them before the parent's public entry
5642        // surfaces them, so stash and restore around this call: recognition sees
5643        // only the hits it records itself (so the fail-loud check below reflects
5644        // this rule), and the parent's prior hits are merged back afterward.
5645        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
5646        let start_state = atn
5647            .rule_to_start_state()
5648            .get(rule_index)
5649            .copied()
5650            .ok_or_else(|| {
5651                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
5652            })?;
5653        let stop_state = atn
5654            .rule_to_stop_state()
5655            .get(rule_index)
5656            .copied()
5657            .filter(|state| *state != usize::MAX)
5658            .ok_or_else(|| {
5659                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
5660            })?;
5661
5662        let start_index = self.current_visible_index();
5663        self.clear_prediction_diagnostics();
5664        self.reset_per_parse_caches();
5665        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
5666        let invoking_state = self.pending_invoking_states.pop();
5667        let local_int_arg = invoking_state
5668            .and_then(|state| usize::try_from(state).ok())
5669            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
5670        let mut visiting = BTreeSet::new();
5671        let mut memo = BTreeMap::new();
5672        let mut expected = ExpectedTokens::default();
5673        let member_values = self.int_members.clone();
5674        let return_values = BTreeMap::new();
5675        let outcomes = self.recognize_state(
5676            atn,
5677            RecognizeRequest {
5678                state_number: start_state,
5679                stop_state,
5680                index: start_index,
5681                rule_start_index: start_index,
5682                decision_start_index: None,
5683                init_action_rules: &init_action_rules,
5684                predicates,
5685                semantics,
5686                rule_args,
5687                member_actions,
5688                return_actions,
5689                local_int_arg,
5690                member_values,
5691                return_values,
5692                rule_alt_number: 0,
5693                track_alt_numbers,
5694                consumed_eof: false,
5695                precedence,
5696                depth: 0,
5697                recovery_symbols: BTreeSet::new(),
5698                recovery_state: None,
5699            },
5700            &mut visiting,
5701            &mut memo,
5702            &mut expected,
5703        );
5704        if let Some(error) = self.unknown_semantic_error() {
5705            report_token_source_errors(&self.input.drain_source_errors());
5706            // Keep the recorded coordinates: when this interpreted rule is a
5707            // child of a generated parent, the parent's catch block recovers an
5708            // ordinary `AntlrError` into a partial subtree, so the fail-loud
5709            // coordinate must survive on the parser for the top-level entry's
5710            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
5711            // is handled by clearing at the top-level generated entry instead.
5712            return Err(error);
5713        }
5714        // Recognition recorded no unresolved coordinate of its own; merge the
5715        // parent's prior hits back so its public entry can still surface them.
5716        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
5717        let Some(outcome) = select_best_outcome(outcomes.into_iter(), self.prediction_mode) else {
5718            let error = self.recognition_error(rule_index, start_index, &expected);
5719            self.record_syntax_errors(1);
5720            report_token_source_errors(&self.input.drain_source_errors());
5721            return Err(error);
5722        };
5723
5724        self.record_syntax_errors(outcome.diagnostics.len());
5725        report_parser_diagnostics(&self.prediction_diagnostics);
5726        report_parser_diagnostics(&outcome.diagnostics);
5727        report_token_source_errors(&self.input.drain_source_errors());
5728        let mut actions = outcome.actions;
5729        if init_action_rules.contains(&rule_index) {
5730            actions.insert(
5731                0,
5732                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
5733            );
5734        }
5735        let mut context =
5736            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
5737        if track_alt_numbers {
5738            context.set_alt_number(outcome.alt_number);
5739        }
5740        for (name, value) in outcome.return_values {
5741            context.set_int_return(name, value);
5742        }
5743        if let Some(token) = self.token_ref_at(start_index) {
5744            context.set_start_ref(token);
5745        }
5746        if let Some(token) = self.rule_stop_token_ref(outcome.index, outcome.consumed_eof) {
5747            context.set_stop_ref(token);
5748        }
5749        if self.build_parse_trees {
5750            let nodes = fold_left_recursive_boundaries(outcome.nodes);
5751            for node in &nodes {
5752                context.add_child(self.recognized_node_tree(node, track_alt_numbers)?);
5753            }
5754        }
5755        self.input.seek(outcome.index);
5756
5757        Ok((self.rule_node(context), actions))
5758    }
5759
5760    /// Temporary parser entry used by generated parser methods while the parser
5761    /// ATN simulator is being implemented.
5762    ///
5763    /// This keeps generated parser crates buildable and gives us a stable method
5764    /// surface for every grammar rule. It intentionally accepts all remaining
5765    /// tokens into one rule context; it is not the final parser semantics.
5766    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
5767        let mut context = ParserRuleContext::new(rule_index, self.state());
5768        while self.la(1) != TOKEN_EOF {
5769            let token_type = self.la(1);
5770            let child = self.match_token(token_type)?;
5771            if self.build_parse_trees {
5772                context.add_child(child);
5773            }
5774        }
5775        if self.build_parse_trees {
5776            context.add_child(self.match_eof()?);
5777        }
5778        Ok(self.rule_node(context))
5779    }
5780
5781    /// Builds the parser error reported when no ATN path can reach the active
5782    /// rule stop state.
5783    fn recognition_error(
5784        &mut self,
5785        rule_index: usize,
5786        start_index: usize,
5787        expected: &ExpectedTokens,
5788    ) -> AntlrError {
5789        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
5790        self.input.seek(index);
5791        let current = self.input.lt(1).cloned();
5792        let line = current.as_ref().map(Token::line).unwrap_or_default();
5793        let column = current.as_ref().map(Token::column).unwrap_or_default();
5794        AntlrError::ParserError {
5795            line,
5796            column,
5797            message,
5798        }
5799    }
5800
5801    /// Builds the token index and ANTLR-compatible message for a failed rule.
5802    fn expected_error_message(
5803        &mut self,
5804        rule_index: usize,
5805        start_index: usize,
5806        expected: &ExpectedTokens,
5807    ) -> (usize, String) {
5808        let index = expected
5809            .index
5810            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
5811            .unwrap_or_else(|| self.input.index());
5812        self.input.seek(index);
5813        let current = self.input.lt(1).cloned();
5814        let message = if expected
5815            .no_viable
5816            .as_ref()
5817            .is_some_and(|no_viable| no_viable.error_index == index)
5818        {
5819            let start = expected
5820                .no_viable
5821                .as_ref()
5822                .map_or(start_index, |no_viable| no_viable.start_index);
5823            let text = display_input_text(&self.input.text(start, index));
5824            format!("no viable alternative at input '{text}'")
5825        } else if expected.symbols.is_empty() {
5826            if expected.index.is_some() {
5827                let found = current
5828                    .as_ref()
5829                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
5830                if current
5831                    .as_ref()
5832                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
5833                {
5834                    format!(
5835                        "missing {} at {found}",
5836                        self.expected_symbols_display(&expected.symbols)
5837                    )
5838                } else {
5839                    format!("mismatched input {found}")
5840                }
5841            } else {
5842                format!("no viable alternative while parsing rule {rule_index}")
5843            }
5844        } else {
5845            format!(
5846                "mismatched input {} expecting {}",
5847                current
5848                    .as_ref()
5849                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5850                self.expected_symbols_display(&expected.symbols)
5851            )
5852        };
5853        (index, message)
5854    }
5855
5856    /// Converts a failed child rule into a recovered outcome so the parent can
5857    /// continue after reporting the child diagnostic.
5858    fn child_rule_failure_recovery(
5859        &mut self,
5860        rule_index: usize,
5861        start_index: usize,
5862        sync_symbols: &BTreeSet<i32>,
5863        member_values: BTreeMap<usize, i64>,
5864        expected: &ExpectedTokens,
5865    ) -> Option<RecognizeOutcome> {
5866        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
5867        let token = self.token_at(error_index);
5868        let mut next_index = error_index;
5869        loop {
5870            let symbol = self.token_type_at(next_index);
5871            if sync_symbols.contains(&symbol) {
5872                if next_index == error_index {
5873                    return None;
5874                }
5875                break;
5876            }
5877            if symbol == TOKEN_EOF {
5878                break;
5879            }
5880            let after = self.consume_index(next_index, symbol);
5881            if after == next_index {
5882                break;
5883            }
5884            next_index = after;
5885        }
5886        Some(RecognizeOutcome {
5887            index: next_index,
5888            consumed_eof: false,
5889            alt_number: 0,
5890            member_values,
5891            return_values: BTreeMap::new(),
5892            diagnostics: vec![diagnostic_for_token(token.as_ref(), message)],
5893            decisions: Vec::new(),
5894            actions: Vec::new(),
5895            nodes: vec![RecognizedNode::ErrorToken { index: error_index }],
5896        })
5897    }
5898
5899    /// Adapts the optional recovery result to the normal outcome list used by
5900    /// rule-call transitions.
5901    fn child_rule_failure_recovery_outcomes(
5902        &mut self,
5903        request: ChildRuleFailureRecovery<'_>,
5904    ) -> Vec<RecognizeOutcome> {
5905        let sync_symbols =
5906            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
5907        self.child_rule_failure_recovery(
5908            request.rule_index,
5909            request.start_index,
5910            &sync_symbols,
5911            request.member_values,
5912            request.expected,
5913        )
5914        .into_iter()
5915        .collect()
5916    }
5917
5918    /// Formats expected token types using ANTLR's single-token or set syntax.
5919    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
5920        expected_symbols_display(symbols, self.vocabulary())
5921    }
5922
5923    /// Returns the single-token deletion repair if the token after `index`
5924    /// satisfies the failed consuming transition.
5925    fn single_token_deletion(
5926        &mut self,
5927        transition: &Transition,
5928        index: usize,
5929        max_token_type: i32,
5930        expected_symbols: &BTreeSet<i32>,
5931    ) -> Option<(ParserDiagnostic, usize, i32)> {
5932        let current_symbol = self.token_type_at(index);
5933        if current_symbol == TOKEN_EOF {
5934            return None;
5935        }
5936        let next_index = self.consume_index(index, current_symbol);
5937        if next_index == index {
5938            return None;
5939        }
5940        let next_symbol = self.token_type_at(next_index);
5941        if !transition.matches(next_symbol, 1, max_token_type) {
5942            return None;
5943        }
5944        let transition_expected = transition_expected_symbols(transition, max_token_type);
5945        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
5946            &transition_expected
5947        } else {
5948            expected_symbols
5949        });
5950        let current = self.token_at(index);
5951        let message = format!(
5952            "extraneous input {} expecting {expected_display}",
5953            current
5954                .as_ref()
5955                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
5956        );
5957        Some((
5958            diagnostic_for_token(current.as_ref(), message),
5959            next_index,
5960            next_symbol,
5961        ))
5962    }
5963
5964    /// Returns the repair used when deleting the current token lets a recovery
5965    /// state continue with the following token.
5966    fn current_token_deletion(
5967        &mut self,
5968        index: usize,
5969        expected_symbols: &BTreeSet<i32>,
5970    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
5971        if expected_symbols.is_empty() {
5972            return None;
5973        }
5974        let current_symbol = self.token_type_at(index);
5975        if current_symbol == TOKEN_EOF {
5976            return None;
5977        }
5978        let current = self.token_at(index);
5979        let message = format!(
5980            "extraneous input {} expecting {}",
5981            current
5982                .as_ref()
5983                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5984            self.expected_symbols_display(expected_symbols)
5985        );
5986        let diagnostic = diagnostic_for_token(current.as_ref(), message);
5987        let mut skipped = Vec::new();
5988        let mut cursor = index;
5989        loop {
5990            let symbol = self.token_type_at(cursor);
5991            if symbol == TOKEN_EOF {
5992                return None;
5993            }
5994            skipped.push(cursor);
5995            let next_index = self.consume_index(cursor, symbol);
5996            if next_index == cursor {
5997                return None;
5998            }
5999            let next_symbol = self.token_type_at(next_index);
6000            if expected_symbols.contains(&next_symbol) {
6001                return Some((diagnostic, next_index, skipped));
6002            }
6003            cursor = next_index;
6004        }
6005    }
6006
6007    /// Returns the single-token insertion repair for a failed consuming
6008    /// transition. The caller validates the repair by continuing from the
6009    /// transition target at the same input index.
6010    fn single_token_insertion(
6011        &mut self,
6012        transition: &Transition,
6013        index: usize,
6014        max_token_type: i32,
6015        expected_symbols: &BTreeSet<i32>,
6016        follow_symbols: &BTreeSet<i32>,
6017    ) -> Option<(ParserDiagnostic, i32, String)> {
6018        let current_symbol = self.token_type_at(index);
6019        if !follow_symbols.contains(&current_symbol) {
6020            return None;
6021        }
6022        let transition_expected = transition_expected_symbols(transition, max_token_type);
6023        let token_type = transition_expected.iter().next().copied()?;
6024        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
6025            &transition_expected
6026        } else {
6027            expected_symbols
6028        });
6029        let mut token_symbols = BTreeSet::new();
6030        token_symbols.insert(token_type);
6031        let missing_token_display = self.expected_symbols_display(&token_symbols);
6032        let current = self.token_at(index);
6033        let message = format!(
6034            "missing {expected_display} at {}",
6035            current
6036                .as_ref()
6037                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
6038        );
6039        let text = format!("<missing {missing_token_display}>");
6040        Some((
6041            diagnostic_for_token(current.as_ref(), message),
6042            token_type,
6043            text,
6044        ))
6045    }
6046
6047    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
6048    /// skip the unexpected current token when the following token satisfies the
6049    /// transition that failed.
6050    fn fast_single_token_deletion_recovery(
6051        &mut self,
6052        recovery: FastRecoveryRequest<'_, '_>,
6053    ) -> Vec<FastRecognizeOutcome> {
6054        let FastRecoveryRequest {
6055            atn,
6056            transition,
6057            expected_symbols,
6058            target,
6059            request,
6060            visiting,
6061            memo,
6062            expected,
6063        } = recovery;
6064        let FastRecognizeRequest {
6065            stop_state,
6066            index,
6067            rule_start_index,
6068            decision_start_index,
6069            precedence,
6070            depth,
6071            ..
6072        } = request;
6073        let Some((diagnostic, next_index, next_symbol)) =
6074            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
6075        else {
6076            return Vec::new();
6077        };
6078        let after_next = self.consume_index(next_index, next_symbol);
6079        let empty_recovery = self.empty_recovery_symbols();
6080        self.recognize_state_fast(
6081            atn,
6082            FastRecognizeRequest {
6083                state_number: target,
6084                stop_state,
6085                index: after_next,
6086                rule_start_index,
6087                decision_start_index,
6088                precedence,
6089                depth: depth + 1,
6090                recovery_symbols: empty_recovery,
6091                recovery_state: None,
6092            },
6093            visiting,
6094            memo,
6095            expected,
6096        )
6097        .into_iter()
6098        .map(|mut outcome| {
6099            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
6100            outcome.diagnostics.insert(0, diagnostic.clone());
6101            if self.fast_token_nodes_enabled {
6102                outcome
6103                    .nodes
6104                    .prepend(Rc::new(FastRecognizedNode::Token { index: next_index }));
6105                outcome
6106                    .nodes
6107                    .prepend(Rc::new(FastRecognizedNode::ErrorToken { index }));
6108            }
6109            outcome
6110        })
6111        .collect()
6112    }
6113
6114    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
6115    /// pretend the expected transition token was present and continue without
6116    /// consuming the current token.
6117    fn fast_single_token_insertion_recovery(
6118        &mut self,
6119        recovery: FastRecoveryRequest<'_, '_>,
6120    ) -> Vec<FastRecognizeOutcome> {
6121        let FastRecoveryRequest {
6122            atn,
6123            transition,
6124            expected_symbols,
6125            target,
6126            request,
6127            visiting,
6128            memo,
6129            expected,
6130        } = recovery;
6131        let FastRecognizeRequest {
6132            stop_state,
6133            index,
6134            rule_start_index,
6135            decision_start_index,
6136            precedence,
6137            depth,
6138            ..
6139        } = request;
6140        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
6141        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
6142            transition,
6143            index,
6144            atn.max_token_type(),
6145            &expected_symbols,
6146            &follow_symbols,
6147        ) else {
6148            return Vec::new();
6149        };
6150        let empty_recovery = self.empty_recovery_symbols();
6151        self.recognize_state_fast(
6152            atn,
6153            FastRecognizeRequest {
6154                state_number: target,
6155                stop_state,
6156                index,
6157                rule_start_index,
6158                decision_start_index,
6159                precedence,
6160                depth: depth + 1,
6161                recovery_symbols: empty_recovery,
6162                recovery_state: None,
6163            },
6164            visiting,
6165            memo,
6166            expected,
6167        )
6168        .into_iter()
6169        .map(|mut outcome| {
6170            outcome.diagnostics.insert(0, diagnostic.clone());
6171            outcome
6172                .nodes
6173                .prepend(Rc::new(FastRecognizedNode::MissingToken {
6174                    token_type,
6175                    at_index: index,
6176                    text: text.clone(),
6177                }));
6178            outcome
6179        })
6180        .collect()
6181    }
6182
6183    /// Retries the current fast-recognition state after deleting one
6184    /// unexpected token that precedes a valid loop or block continuation.
6185    fn fast_current_token_deletion_recovery(
6186        &mut self,
6187        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
6188    ) -> Vec<FastRecognizeOutcome> {
6189        let FastCurrentTokenDeletionRequest {
6190            atn,
6191            expected_symbols,
6192            mut request,
6193            visiting,
6194            memo,
6195            expected,
6196        } = recovery;
6197        if request.index == request.rule_start_index {
6198            return Vec::new();
6199        }
6200        let Some((diagnostic, next_index, skipped)) =
6201            self.current_token_deletion(request.index, &expected_symbols)
6202        else {
6203            return Vec::new();
6204        };
6205        request.state_number = request.recovery_state.unwrap_or(request.state_number);
6206        request.index = next_index;
6207        request.depth += 1;
6208        request.recovery_state = None;
6209        self.recognize_state_fast(atn, request, visiting, memo, expected)
6210            .into_iter()
6211            .map(|mut outcome| {
6212                outcome.diagnostics.insert(0, diagnostic.clone());
6213                for index in skipped.iter().rev() {
6214                    outcome
6215                        .nodes
6216                        .prepend(Rc::new(FastRecognizedNode::ErrorToken { index: *index }));
6217                }
6218                outcome
6219            })
6220            .collect()
6221    }
6222
6223    /// Converts a failed child rule into a recovered fast-recognizer outcome so
6224    /// the parent can keep its child rule context and continue at a sync token.
6225    fn fast_child_rule_failure_recovery(
6226        &mut self,
6227        rule_index: usize,
6228        start_index: usize,
6229        sync_symbols: &BTreeSet<i32>,
6230        expected: &ExpectedTokens,
6231    ) -> Option<FastRecognizeOutcome> {
6232        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
6233        let token = self.token_at(error_index);
6234        let mut next_index = error_index;
6235        loop {
6236            let symbol = self.token_type_at(next_index);
6237            if sync_symbols.contains(&symbol) {
6238                if next_index == error_index {
6239                    return None;
6240                }
6241                break;
6242            }
6243            if symbol == TOKEN_EOF {
6244                break;
6245            }
6246            let after = self.consume_index(next_index, symbol);
6247            if after == next_index {
6248                break;
6249            }
6250            next_index = after;
6251        }
6252        let mut diagnostics = FastDiagnostics::new();
6253        diagnostics.insert(0, diagnostic_for_token(token.as_ref(), message));
6254        let mut nodes = NodeList::new();
6255        if self.fast_token_nodes_enabled {
6256            nodes.prepend(Rc::new(FastRecognizedNode::ErrorToken {
6257                index: error_index,
6258            }));
6259        }
6260        Some(FastRecognizeOutcome {
6261            index: next_index,
6262            consumed_eof: false,
6263            diagnostics,
6264            nodes,
6265        })
6266    }
6267
6268    /// Adapts the optional child-rule recovery result to the fast-recognizer
6269    /// outcome list used by rule-call transitions.
6270    fn fast_child_rule_failure_recovery_outcomes(
6271        &mut self,
6272        request: FastChildRuleFailureRecoveryRequest<'_>,
6273    ) -> Vec<FastRecognizeOutcome> {
6274        let FastChildRuleFailureRecoveryRequest {
6275            atn,
6276            rule_index,
6277            start_index,
6278            follow_state,
6279            stop_state,
6280            expected,
6281        } = request;
6282        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
6283        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
6284            .into_iter()
6285            .collect()
6286    }
6287
6288    /// Attempts to reach `stop_state` from `state_number` without committing
6289    /// token consumption to the parser's public stream position.
6290    #[allow(clippy::too_many_lines)]
6291    fn recognize_state_fast(
6292        &mut self,
6293        atn: &Atn,
6294        request: FastRecognizeRequest,
6295        visiting: &mut FxHashSet<FastRecognizeKey>,
6296        memo: &mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
6297        expected: &mut ExpectedTokens,
6298    ) -> Vec<FastRecognizeOutcome> {
6299        #[cfg(feature = "perf-counters")]
6300        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
6301        let FastRecognizeRequest {
6302            mut state_number,
6303            stop_state,
6304            mut index,
6305            rule_start_index,
6306            decision_start_index,
6307            precedence,
6308            mut depth,
6309            recovery_symbols,
6310            recovery_state,
6311        } = request;
6312        // Walk straight-line epsilon chains in a loop instead of recursing
6313        // into `recognize_state_fast` for each intermediate state. ATN
6314        // serialization places long sequences of `BasicBlock` epsilon
6315        // transitions between decisions: turning that chain into a loop
6316        // collapses many recursive calls (and their memo lookups, vec
6317        // allocations, and visit-set churn) into a single function frame.
6318        // The loop exits as soon as we hit the original state's logic
6319        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
6320        // precedence) so existing fanout, recovery, and memoization still
6321        // apply unchanged.
6322        //
6323        // The inline case also handles single-atom-match states on the
6324        // happy-pass path: when the lone consuming transition matches the
6325        // current lookahead, advance the index and continue without paying
6326        // for a full `recognize_state_fast` recursion. We track tokens we
6327        // consumed inline in `inline_consumed_tokens` so they can be
6328        // prepended onto the eventual outcome list once we hit a state
6329        // whose handling falls outside this fast loop.
6330        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
6331        let mut inline_consumed_eof = false;
6332        loop {
6333            if depth > RECOGNITION_DEPTH_LIMIT {
6334                return Vec::new();
6335            }
6336            if state_number == stop_state {
6337                let mut nodes = NodeList::new();
6338                if self.fast_token_nodes_enabled {
6339                    for token_index in inline_consumed_tokens.iter().rev() {
6340                        nodes.prepend(Rc::new(FastRecognizedNode::Token {
6341                            index: *token_index,
6342                        }));
6343                    }
6344                }
6345                return vec![FastRecognizeOutcome {
6346                    index,
6347                    consumed_eof: inline_consumed_eof,
6348                    diagnostics: FastDiagnostics::new(),
6349                    nodes,
6350                }];
6351            }
6352            let Some(state) = atn.state(state_number) else {
6353                return Vec::new();
6354            };
6355            if state.transitions.len() == 1
6356                && !starts_prediction_decision(state)
6357                && !state.precedence_rule_decision
6358            {
6359                match &state.transitions[0] {
6360                    Transition::Epsilon { target }
6361                    | Transition::Predicate { target, .. }
6362                    | Transition::Action { target, .. }
6363                        if left_recursive_boundary(atn, state, *target).is_none() =>
6364                    {
6365                        #[cfg(feature = "perf-counters")]
6366                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
6367                        state_number = *target;
6368                        depth += 1;
6369                        continue;
6370                    }
6371                    Transition::Precedence {
6372                        target,
6373                        precedence: transition_precedence,
6374                    } if *transition_precedence >= precedence
6375                        && left_recursive_boundary(atn, state, *target).is_none() =>
6376                    {
6377                        #[cfg(feature = "perf-counters")]
6378                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
6379                        state_number = *target;
6380                        depth += 1;
6381                        continue;
6382                    }
6383                    // Single-atom / range / set / wildcard / not-set states
6384                    // are common (~17K of ~125K calls on C#) and almost
6385                    // always succeed in pass 1: no fanout, no recovery, no
6386                    // diagnostics. Inline the token match and continue
6387                    // walking instead of recursing — the recursive path
6388                    // would just allocate a Vec, build one outcome, prepend
6389                    // a Token node, and return. Skip pass 2 (recovery
6390                    // enabled): there the failure branch matters and the
6391                    // existing recursive code records expected symbols.
6392                    Transition::Atom { target, .. }
6393                    | Transition::Range { target, .. }
6394                    | Transition::Set { target, .. }
6395                    | Transition::NotSet { target, .. }
6396                    | Transition::Wildcard { target, .. }
6397                        if !self.fast_recovery_enabled =>
6398                    {
6399                        let symbol = self.token_type_at(index);
6400                        let transition = &state.transitions[0];
6401                        if transition.matches(symbol, 1, atn.max_token_type()) {
6402                            #[cfg(feature = "perf-counters")]
6403                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
6404                            if self.fast_token_nodes_enabled {
6405                                inline_consumed_tokens.push(index);
6406                            }
6407                            inline_consumed_eof |= symbol == TOKEN_EOF;
6408                            index = self.consume_index(index, symbol);
6409                            state_number = *target;
6410                            depth += 1;
6411                            continue;
6412                        }
6413                        // Fall through to break and let the regular
6414                        // body handle the no-match case (returns empty).
6415                    }
6416                    _ => {}
6417                }
6418            }
6419            break;
6420        }
6421        // If we collected token nodes inline but bail to the recursive
6422        // body (decision state, rule call, etc.), the outcomes returned
6423        // below will need those token nodes prepended.
6424        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
6425        let Some(state) = atn.state(state_number) else {
6426            return Vec::new();
6427        };
6428        let transition_count = state.transitions.len();
6429        let memo_lookup_enabled = self.fast_recovery_enabled || transition_count > 1;
6430        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
6431        // fields and the rule/decision boundary indices are pure plumbing —
6432        // they only affect the recovery branch and the no-viable diagnostic
6433        // recording, neither of which fires when recovery is off. Zeroing
6434        // them in the memo key collapses calls that visit the same
6435        // `(state, index)` from different rule-call sites onto one cache
6436        // entry, which is the dominant cost on large grammars (e.g. C#) where
6437        // many rules eventually delegate into the same `expression` /
6438        // `primary_expression` / `type` branches.
6439        let key = if self.fast_recovery_enabled {
6440            FastRecognizeKey {
6441                state_number,
6442                stop_state,
6443                index,
6444                rule_start_index,
6445                decision_start_index,
6446                precedence,
6447                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
6448                recovery_state,
6449            }
6450        } else {
6451            FastRecognizeKey {
6452                state_number,
6453                stop_state,
6454                index,
6455                rule_start_index: 0,
6456                decision_start_index: None,
6457                precedence,
6458                recovery_symbols_id: 0,
6459                recovery_state: None,
6460            }
6461        };
6462        if memo_lookup_enabled {
6463            if let Some(outcomes) = memo.get(&key) {
6464                #[cfg(feature = "perf-counters")]
6465                {
6466                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
6467                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
6468                }
6469                // Materialize a fresh `Vec` from the cached slice; the caller
6470                // mutates per-outcome state (eof flags, prepended nodes) so we
6471                // can't hand them the shared backing.
6472                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
6473                    let inline_eof = inline_consumed_eof;
6474                    let inline_tokens = &inline_consumed_tokens;
6475                    return outcomes
6476                        .iter()
6477                        .cloned()
6478                        .map(|mut outcome| {
6479                            if inline_eof {
6480                                outcome.consumed_eof = true;
6481                            }
6482                            if self.fast_token_nodes_enabled {
6483                                for token_index in inline_tokens.iter().rev() {
6484                                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
6485                                        index: *token_index,
6486                                    }));
6487                                }
6488                            }
6489                            outcome
6490                        })
6491                        .collect();
6492                }
6493                return outcomes.to_vec();
6494            }
6495            #[cfg(feature = "perf-counters")]
6496            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
6497        }
6498
6499        // Cycle detection: clean recognition keeps the narrow static cycle
6500        // guard used on hot paths. Recovery needs the broader epsilon-state
6501        // guard because an otherwise non-nullable loop body can recover as an
6502        // empty child at EOF and re-enter the loop at the same token.
6503        let needs_cycle_guard = if self.fast_recovery_enabled {
6504            state.transitions.iter().any(Transition::is_epsilon)
6505        } else {
6506            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
6507        };
6508        #[cfg(feature = "perf-counters")]
6509        if needs_cycle_guard {
6510            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
6511        } else {
6512            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
6513            match &state.transitions[0] {
6514                Transition::Rule { .. } => {
6515                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
6516                }
6517                Transition::Atom { .. }
6518                | Transition::Range { .. }
6519                | Transition::Set { .. }
6520                | Transition::NotSet { .. }
6521                | Transition::Wildcard { .. } => {
6522                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
6523                }
6524                _ => {
6525                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
6526                }
6527            }
6528        }
6529        let has_inserted_cycle_guard = if needs_cycle_guard {
6530            if !visiting.insert(key.clone()) {
6531                #[cfg(feature = "perf-counters")]
6532                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
6533                return Vec::new();
6534            }
6535            true
6536        } else {
6537            false
6538        };
6539        let next_decision_start_index = if starts_prediction_decision(state) {
6540            Some(index)
6541        } else {
6542            decision_start_index
6543        };
6544        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
6545            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
6546        } else {
6547            (Rc::clone(&recovery_symbols), recovery_state)
6548        };
6549
6550        // Lookahead-based pruning. At a multi-alternative state we cache the
6551        // look-1 set of every outgoing transition; on visit we keep only the
6552        // transitions whose look-1 can accept the current lookahead (or that
6553        // can be reached without consuming and so could legitimately match a
6554        // shorter input). This is the main speedup vs. blind speculative
6555        // recursion: it lets each visit fan out only to the alternatives that
6556        // could possibly contribute a clean parse, mirroring the SLL phase of
6557        // ANTLR's adaptive prediction.
6558        //
6559        // Pruning is skipped at:
6560        //   * rule-start states (a child rule call may need every internal
6561        //     transition to surface single-token recovery diagnostics that
6562        //     ANTLR's reference parser emits at the rule's first consuming
6563        //     transition; the FIRST-set retry path turns the prefilter off
6564        //     entirely so let's keep this lightweight too),
6565        //   * left-recursive precedence loops (the precedence transition's
6566        //     gating is dynamic),
6567        //   * states with too few alternatives to benefit.
6568        let transition_count = state.transitions.len();
6569        let lookahead_filter = if transition_count > 1
6570            && self.fast_first_set_prefilter
6571            && !state.precedence_rule_decision
6572            && (!self.fast_recovery_enabled || state.kind != AtnStateKind::RuleStart)
6573        {
6574            state
6575                .rule_index
6576                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index).copied())
6577                .map(|rule_stop| {
6578                    let symbol = self.token_type_at(index);
6579                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6580                    (symbol, entry)
6581                })
6582        } else {
6583            None
6584        };
6585        // LL(1) fast path: when the FIRST sets for the decision are disjoint
6586        // and none is nullable, the lookahead deterministically selects one
6587        // alternative. The recursive recognizer can then commit to that single
6588        // alt without iterating every transition through `should_skip_via_lookahead`
6589        // — saving (transition_count - 1) filter probes per visit.
6590        //
6591        // Result is cached per `(state, lookahead_token)` on the parser
6592        // instance, so subsequent visits skip the FIRST-set scan entirely.
6593        let ll1_only_alt: Option<usize> = if transition_count > 1
6594            && let Some((symbol, entry)) = lookahead_filter.as_ref()
6595        {
6596            let key = (state.state_number, *symbol);
6597            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
6598                cached
6599            } else {
6600                let result = ll1_unique_alt(entry, *symbol);
6601                self.ll1_decision_cache.insert(key, result);
6602                result
6603            }
6604        } else {
6605            None
6606        };
6607        let lookahead_filter = lookahead_filter.as_ref();
6608        // Pre-size only when we expect at least one outcome to land — most
6609        // single-transition fall-throughs (the loop above didn't catch
6610        // because they're atom/rule/predicate) push at most one entry, so
6611        // reserving one slot avoids a reallocation while keeping the
6612        // unused-slot waste at one element.
6613        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
6614        for (transition_index, transition) in state.transitions.iter().enumerate() {
6615            if let Some(alt) = ll1_only_alt {
6616                // LL(1) determinism: skip every alt except the chosen one.
6617                if alt != transition_index {
6618                    continue;
6619                }
6620            } else if should_skip_via_lookahead(
6621                transition,
6622                transition_index,
6623                lookahead_filter,
6624                index,
6625                self.fast_recovery_enabled,
6626                expected,
6627            ) {
6628                continue;
6629            }
6630            match transition {
6631                Transition::Epsilon { target }
6632                | Transition::Predicate { target, .. }
6633                | Transition::Action { target, .. } => {
6634                    #[cfg(feature = "perf-counters")]
6635                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
6636                    let boundary = left_recursive_boundary(atn, state, *target);
6637                    outcomes.extend(
6638                        self.recognize_state_fast(
6639                            atn,
6640                            FastRecognizeRequest {
6641                                state_number: *target,
6642                                stop_state,
6643                                index,
6644                                rule_start_index,
6645                                decision_start_index: next_decision_start_index,
6646                                precedence,
6647                                depth: depth + 1,
6648                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6649                                recovery_state: epsilon_recovery_state,
6650                            },
6651                            visiting,
6652                            memo,
6653                            expected,
6654                        )
6655                        .into_iter()
6656                        .map(|mut outcome| {
6657                            if let Some(rule_index) = boundary {
6658                                outcome.nodes.prepend(Rc::new(
6659                                    FastRecognizedNode::LeftRecursiveBoundary { rule_index },
6660                                ));
6661                            }
6662                            outcome
6663                        }),
6664                    );
6665                }
6666                Transition::Precedence {
6667                    target,
6668                    precedence: transition_precedence,
6669                } => {
6670                    if *transition_precedence >= precedence {
6671                        let boundary = left_recursive_boundary(atn, state, *target);
6672                        outcomes.extend(
6673                            self.recognize_state_fast(
6674                                atn,
6675                                FastRecognizeRequest {
6676                                    state_number: *target,
6677                                    stop_state,
6678                                    index,
6679                                    rule_start_index,
6680                                    decision_start_index: next_decision_start_index,
6681                                    precedence,
6682                                    depth: depth + 1,
6683                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6684                                    recovery_state: epsilon_recovery_state,
6685                                },
6686                                visiting,
6687                                memo,
6688                                expected,
6689                            )
6690                            .into_iter()
6691                            .map(|mut outcome| {
6692                                if let Some(rule_index) = boundary {
6693                                    outcome.nodes.prepend(Rc::new(
6694                                        FastRecognizedNode::LeftRecursiveBoundary { rule_index },
6695                                    ));
6696                                }
6697                                outcome
6698                            }),
6699                        );
6700                    }
6701                }
6702                Transition::Rule {
6703                    target,
6704                    rule_index,
6705                    follow_state,
6706                    precedence: rule_precedence,
6707                    ..
6708                } => {
6709                    #[cfg(feature = "perf-counters")]
6710                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
6711                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
6712                    else {
6713                        continue;
6714                    };
6715                    // Lookahead-based pruning. The recognizer would otherwise
6716                    // explore every speculative rule call, producing exponential
6717                    // work on grammars with many epsilon-reachable rules. When
6718                    // the rule is non-nullable and its FIRST set excludes the
6719                    // current lookahead, recursion can't find a clean path
6720                    // *through this rule*. Skipping is only safe if some sibling
6721                    // transition can still consume the lookahead — otherwise the
6722                    // rule call is the sole continuation and must run so the
6723                    // single-token insertion / deletion recovery inside the
6724                    // called rule can fire (mirroring ANTLR's reference behavior
6725                    // of conjuring a missing token at child-rule entry).
6726                    let symbol = self.token_type_at(index);
6727                    if self.fast_first_set_prefilter {
6728                        // Probe the shared cross-parse cache first; build
6729                        // the entry on miss and intern it there. The
6730                        // computation is purely a function of the ATN, so
6731                        // the cached entry is reused across parses (and
6732                        // freshly-instantiated parser values that share
6733                        // the same `&'static Atn`).
6734                        //
6735                        // `rule_first_set` returns the computed entry
6736                        // directly — it intentionally skips inserting into
6737                        // the cache when the FIRST-set walk hit a cycle, so
6738                        // we cannot assume the entry is in the cache after
6739                        // computing it.
6740                        let first = self.cached_rule_first_set(atn, *target, child_stop);
6741                        if should_skip_rule_via_first_set(
6742                            &first,
6743                            symbol,
6744                            self.fast_recovery_enabled,
6745                            index,
6746                            expected,
6747                        ) {
6748                            continue;
6749                        }
6750                    }
6751                    let expected_before_child =
6752                        self.fast_recovery_enabled.then(|| expected.clone());
6753                    let mut children = self.recognize_state_fast(
6754                        atn,
6755                        FastRecognizeRequest {
6756                            state_number: *target,
6757                            stop_state: child_stop,
6758                            index,
6759                            rule_start_index: index,
6760                            decision_start_index: None,
6761                            precedence: *rule_precedence,
6762                            depth: depth + 1,
6763                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6764                            recovery_state: epsilon_recovery_state,
6765                        },
6766                        visiting,
6767                        memo,
6768                        expected,
6769                    );
6770                    if children.is_empty() && self.fast_recovery_enabled {
6771                        children = self.fast_child_rule_failure_recovery_outcomes(
6772                            FastChildRuleFailureRecoveryRequest {
6773                                atn,
6774                                rule_index: *rule_index,
6775                                start_index: index,
6776                                follow_state: *follow_state,
6777                                stop_state,
6778                                expected,
6779                            },
6780                        );
6781                    }
6782                    if let Some(expected_before_child) = expected_before_child {
6783                        if children
6784                            .iter()
6785                            .any(|child| child.diagnostics.is_empty() && child.index > index)
6786                        {
6787                            *expected = expected_before_child;
6788                        }
6789                    }
6790                    for child in children {
6791                        let child_index = child.index;
6792                        let child_consumed_eof = child.consumed_eof;
6793                        let child_diagnostics = child.diagnostics;
6794                        let empty_recovery = self.empty_recovery_symbols();
6795                        let follow_outcomes = self.recognize_state_fast(
6796                            atn,
6797                            FastRecognizeRequest {
6798                                state_number: *follow_state,
6799                                stop_state,
6800                                index: child_index,
6801                                rule_start_index,
6802                                decision_start_index: next_decision_start_index,
6803                                precedence,
6804                                depth: depth + 1,
6805                                recovery_symbols: empty_recovery,
6806                                recovery_state: None,
6807                            },
6808                            visiting,
6809                            memo,
6810                            expected,
6811                        );
6812                        if follow_outcomes.is_empty() {
6813                            continue;
6814                        }
6815                        let child_node = Rc::new(FastRecognizedNode::Rule {
6816                            rule_index: *rule_index,
6817                            invoking_state: invoking_state_number(state_number),
6818                            start_index: index,
6819                            stop_index: self.rule_stop_token_index(child_index, child_consumed_eof),
6820                            children: child.nodes,
6821                        });
6822                        let child_diags_empty = child_diagnostics.is_empty();
6823                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
6824                            outcome.consumed_eof |= child_consumed_eof;
6825                            // Skip the prepend dance when there's nothing to
6826                            // merge from the child — common case in pass 1.
6827                            if !child_diags_empty {
6828                                let mut diagnostics = child_diagnostics.clone();
6829                                diagnostics.append(&mut outcome.diagnostics);
6830                                outcome.diagnostics = diagnostics;
6831                            }
6832                            outcome.nodes.prepend(Rc::clone(&child_node));
6833                            outcome
6834                        }));
6835                    }
6836                }
6837                Transition::Atom { target, .. }
6838                | Transition::Range { target, .. }
6839                | Transition::Set { target, .. }
6840                | Transition::NotSet { target, .. }
6841                | Transition::Wildcard { target, .. } => {
6842                    #[cfg(feature = "perf-counters")]
6843                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
6844                    let symbol = self.token_type_at(index);
6845                    if transition.matches(symbol, 1, atn.max_token_type()) {
6846                        let next_index = self.consume_index(index, symbol);
6847                        let empty_recovery = self.empty_recovery_symbols();
6848                        outcomes.extend(
6849                            self.recognize_state_fast(
6850                                atn,
6851                                FastRecognizeRequest {
6852                                    state_number: *target,
6853                                    stop_state,
6854                                    index: next_index,
6855                                    rule_start_index,
6856                                    decision_start_index: next_decision_start_index,
6857                                    precedence,
6858                                    depth: depth + 1,
6859                                    recovery_symbols: empty_recovery,
6860                                    recovery_state: None,
6861                                },
6862                                visiting,
6863                                memo,
6864                                expected,
6865                            )
6866                            .into_iter()
6867                            .map(|mut outcome| {
6868                                outcome.consumed_eof |= symbol == TOKEN_EOF;
6869                                if self.fast_token_nodes_enabled {
6870                                    outcome
6871                                        .nodes
6872                                        .prepend(Rc::new(FastRecognizedNode::Token { index }));
6873                                }
6874                                outcome
6875                            }),
6876                        );
6877                    } else {
6878                        if !self.fast_recovery_enabled {
6879                            // In pass 1 there is no recovery to attempt; the
6880                            // recovery branch below would never run, and the
6881                            // `expected_symbols` computation is just there
6882                            // to gate that branch. Skipping it eliminates
6883                            // ~1× `state_expected_symbols` lookup per failed
6884                            // atom transition (≈82K on mono-statement.cs)
6885                            // for zero observable behavior change.
6886                            continue;
6887                        }
6888                        let expected_symbols = fast_recovery_expected_symbols(
6889                            self,
6890                            atn,
6891                            state.state_number,
6892                            &recovery_symbols,
6893                        );
6894                        if expected_symbols.contains(&symbol) {
6895                            continue;
6896                        }
6897                        {
6898                            expected.record_transition(index, transition, atn.max_token_type());
6899                            record_no_viable_if_ambiguous(
6900                                expected,
6901                                next_decision_start_index,
6902                                index,
6903                            );
6904                            outcomes.extend(self.fast_single_token_deletion_recovery(
6905                                FastRecoveryRequest {
6906                                    atn,
6907                                    transition,
6908                                    expected_symbols: Rc::clone(&expected_symbols),
6909                                    target: *target,
6910                                    request: FastRecognizeRequest {
6911                                        state_number,
6912                                        stop_state,
6913                                        index,
6914                                        rule_start_index,
6915                                        decision_start_index,
6916                                        precedence,
6917                                        depth,
6918                                        recovery_symbols: Rc::clone(&recovery_symbols),
6919                                        recovery_state,
6920                                    },
6921                                    visiting,
6922                                    memo,
6923                                    expected,
6924                                },
6925                            ));
6926                            if !state_is_left_recursive_rule(atn, state) {
6927                                outcomes.extend(self.fast_single_token_insertion_recovery(
6928                                    FastRecoveryRequest {
6929                                        atn,
6930                                        transition,
6931                                        expected_symbols: Rc::clone(&expected_symbols),
6932                                        target: *target,
6933                                        request: FastRecognizeRequest {
6934                                            state_number,
6935                                            stop_state,
6936                                            index,
6937                                            rule_start_index,
6938                                            decision_start_index,
6939                                            precedence,
6940                                            depth,
6941                                            recovery_symbols: Rc::clone(&recovery_symbols),
6942                                            recovery_state,
6943                                        },
6944                                        visiting,
6945                                        memo,
6946                                        expected,
6947                                    },
6948                                ));
6949                            }
6950                            outcomes.extend(self.fast_current_token_deletion_recovery(
6951                                FastCurrentTokenDeletionRequest {
6952                                    atn,
6953                                    expected_symbols,
6954                                    request: FastRecognizeRequest {
6955                                        state_number,
6956                                        stop_state,
6957                                        index,
6958                                        rule_start_index,
6959                                        decision_start_index,
6960                                        precedence,
6961                                        depth,
6962                                        recovery_symbols: Rc::clone(&recovery_symbols),
6963                                        recovery_state,
6964                                    },
6965                                    visiting,
6966                                    memo,
6967                                    expected,
6968                                },
6969                            ));
6970                        }
6971                    }
6972                }
6973            }
6974        }
6975
6976        if has_inserted_cycle_guard {
6977            visiting.remove(&key);
6978        }
6979        if matches!(
6980            self.prediction_mode,
6981            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
6982        ) && self.fast_recovery_enabled
6983        {
6984            // Without recovery enabled every outcome already has empty
6985            // diagnostics, so the discard pass is a no-op — skipping it
6986            // saves an iter+retain on each of the ~1M visits.
6987            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
6988        }
6989        if self.fast_recovery_enabled {
6990            dedupe_fast_outcomes(&mut outcomes);
6991        } else {
6992            dedupe_clean_fast_outcomes(&mut outcomes);
6993        }
6994        // Skip memoization for single-transition states whose outcome is
6995        // unambiguous: they only get re-entered if the caller revisits the
6996        // exact same call site, which is rare since the loop above already
6997        // collapsed straight-line epsilon walks. Multi-alternative states
6998        // are where backtracking actually revisits the same coordinate, so
6999        // we still memoize there. With recovery on we keep the existing
7000        // memoization unconditionally because the recovery branch may
7001        // record diagnostics that the cache must surface to repeated
7002        // failed visits.
7003        let should_memoize = self.fast_recovery_enabled
7004            || (transition_count > 1
7005                && (outcomes.is_empty()
7006                    || outcomes.len() > 1
7007                    || (outcomes.len() == 1 && self.should_memoize_single_outcome(&key))));
7008        // Apply inline pending state to each outcome before returning.
7009        // Tokens consumed inline by the loop-collapse don't appear in the
7010        // recursive recognizer's output, so we need to prepend them here.
7011        let apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
7012            if inline_consumed_eof {
7013                outcome.consumed_eof = true;
7014            }
7015            if !inline_consumed_tokens.is_empty() {
7016                for token_index in inline_consumed_tokens.iter().rev() {
7017                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
7018                        index: *token_index,
7019                    }));
7020                }
7021            }
7022            outcome
7023        };
7024        if should_memoize {
7025            #[cfg(feature = "perf-counters")]
7026            {
7027                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
7028                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
7029                match outcomes.len() {
7030                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
7031                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
7032                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
7033                }
7034            }
7035            // The memo is keyed by the loop-exit `(state_number, index)` so
7036            // the inline-consumed tokens belong to *this* call's output, not
7037            // the cached result. Memoize the bare outcomes (without the
7038            // inline-pending data), then prepend the inline data on return.
7039            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
7040            memo.insert(key, Rc::clone(&stored));
7041            if inline_pending {
7042                return stored.iter().cloned().map(apply_inline_pending).collect();
7043            }
7044            return stored.to_vec();
7045        }
7046        #[cfg(feature = "perf-counters")]
7047        match outcomes.len() {
7048            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
7049            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
7050            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
7051        }
7052        if inline_pending {
7053            return outcomes.into_iter().map(apply_inline_pending).collect();
7054        }
7055        outcomes
7056    }
7057
7058    /// Explores single-token deletion recovery while preserving the matched
7059    /// token and skipped error token in the selected parse tree path.
7060    fn single_token_deletion_recovery(
7061        &mut self,
7062        recovery: RecoveryRequest<'_, '_>,
7063    ) -> Vec<RecognizeOutcome> {
7064        let RecoveryRequest {
7065            atn,
7066            transition,
7067            expected_symbols,
7068            target,
7069            request,
7070            visiting,
7071            memo,
7072            expected,
7073        } = recovery;
7074        let RecognizeRequest {
7075            stop_state,
7076            index,
7077            rule_start_index,
7078            decision_start_index,
7079            init_action_rules,
7080            predicates,
7081            semantics,
7082            rule_args,
7083            member_actions,
7084            return_actions,
7085            local_int_arg,
7086            member_values,
7087            return_values,
7088            rule_alt_number,
7089            track_alt_numbers,
7090            consumed_eof,
7091            precedence,
7092            depth,
7093            ..
7094        } = request;
7095        let Some((diagnostic, next_index, next_symbol)) =
7096            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
7097        else {
7098            return Vec::new();
7099        };
7100        let after_next = self.consume_index(next_index, next_symbol);
7101        self.recognize_state(
7102            atn,
7103            RecognizeRequest {
7104                state_number: target,
7105                stop_state,
7106                index: after_next,
7107                rule_start_index,
7108                decision_start_index,
7109                init_action_rules,
7110                predicates,
7111                semantics,
7112                rule_args,
7113                member_actions,
7114                return_actions,
7115                local_int_arg,
7116                member_values,
7117                return_values,
7118                rule_alt_number,
7119                track_alt_numbers,
7120                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
7121                precedence,
7122                depth: depth + 1,
7123                recovery_symbols: BTreeSet::new(),
7124                recovery_state: None,
7125            },
7126            visiting,
7127            memo,
7128            expected,
7129        )
7130        .into_iter()
7131        .map(|mut outcome| {
7132            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
7133            outcome.diagnostics.insert(0, diagnostic.clone());
7134            outcome
7135                .nodes
7136                .insert(0, RecognizedNode::Token { index: next_index });
7137            outcome
7138                .nodes
7139                .insert(0, RecognizedNode::ErrorToken { index });
7140            outcome
7141        })
7142        .collect()
7143    }
7144
7145    /// Retries the current recognition state after deleting one unexpected
7146    /// token, preserving the deleted token as an error node in the parse tree.
7147    fn current_token_deletion_recovery(
7148        &mut self,
7149        recovery: CurrentTokenDeletionRequest<'_, '_>,
7150    ) -> Vec<RecognizeOutcome> {
7151        let CurrentTokenDeletionRequest {
7152            atn,
7153            expected_symbols,
7154            mut request,
7155            visiting,
7156            memo,
7157            expected,
7158        } = recovery;
7159        let error_index = request.index;
7160        if error_index == request.rule_start_index {
7161            return Vec::new();
7162        }
7163        let Some((diagnostic, next_index, skipped)) =
7164            self.current_token_deletion(error_index, &expected_symbols)
7165        else {
7166            return Vec::new();
7167        };
7168        request.state_number = request.recovery_state.unwrap_or(request.state_number);
7169        request.index = next_index;
7170        request.depth += 1;
7171        request.recovery_state = None;
7172        self.recognize_state(atn, request, visiting, memo, expected)
7173            .into_iter()
7174            .map(|mut outcome| {
7175                outcome.diagnostics.insert(0, diagnostic.clone());
7176                for index in skipped.iter().rev() {
7177                    outcome
7178                        .nodes
7179                        .insert(0, RecognizedNode::ErrorToken { index: *index });
7180                }
7181                outcome
7182            })
7183            .collect()
7184    }
7185
7186    /// Falls back after deletion/insertion repairs cannot continue from a
7187    /// failed consuming transition.
7188    fn consuming_failure_fallback(
7189        &mut self,
7190        fallback: ConsumingFailureFallback<'_>,
7191        visiting: &mut BTreeSet<RecognizeKey>,
7192        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
7193        expected: &mut ExpectedTokens,
7194    ) -> Vec<RecognizeOutcome> {
7195        if fallback.expected_symbols.is_empty() {
7196            return Vec::new();
7197        }
7198        if fallback.symbol == TOKEN_EOF {
7199            return self.eof_consuming_failure_fallback(fallback, expected);
7200        }
7201        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
7202    }
7203
7204    /// Keeps unexpected non-EOF input visible as an error node when no repair
7205    /// path can otherwise reach the transition target.
7206    fn non_eof_consuming_failure_fallback(
7207        &mut self,
7208        fallback: ConsumingFailureFallback<'_>,
7209        visiting: &mut BTreeSet<RecognizeKey>,
7210        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
7211        expected: &mut ExpectedTokens,
7212    ) -> Vec<RecognizeOutcome> {
7213        let ConsumingFailureFallback {
7214            atn,
7215            target,
7216            request,
7217            symbol,
7218            expected_symbols,
7219            decision_start_index,
7220            decision,
7221        } = fallback;
7222        let error_index = request.index;
7223        let diagnostic =
7224            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
7225        let next_index = self.consume_index(error_index, symbol);
7226        self.recognize_state(
7227            atn,
7228            RecognizeRequest {
7229                state_number: target,
7230                stop_state: request.stop_state,
7231                index: next_index,
7232                rule_start_index: request.rule_start_index,
7233                decision_start_index,
7234                init_action_rules: request.init_action_rules,
7235                predicates: request.predicates,
7236                semantics: request.semantics,
7237                rule_args: request.rule_args,
7238                member_actions: request.member_actions,
7239                return_actions: request.return_actions,
7240                local_int_arg: request.local_int_arg,
7241                member_values: request.member_values,
7242                return_values: request.return_values,
7243                rule_alt_number: request.rule_alt_number,
7244                track_alt_numbers: request.track_alt_numbers,
7245                consumed_eof: request.consumed_eof,
7246                precedence: request.precedence,
7247                depth: request.depth + 1,
7248                recovery_symbols: BTreeSet::new(),
7249                recovery_state: None,
7250            },
7251            visiting,
7252            memo,
7253            expected,
7254        )
7255        .into_iter()
7256        .map(|mut outcome| {
7257            prepend_decision(&mut outcome, decision);
7258            outcome.diagnostics.insert(0, diagnostic.clone());
7259            outcome
7260                .nodes
7261                .insert(0, RecognizedNode::ErrorToken { index: error_index });
7262            outcome
7263        })
7264        .collect()
7265    }
7266
7267    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
7268    /// behavior of unwinding instead of inserting caller tokens at EOF.
7269    fn eof_consuming_failure_fallback(
7270        &mut self,
7271        fallback: ConsumingFailureFallback<'_>,
7272        expected: &ExpectedTokens,
7273    ) -> Vec<RecognizeOutcome> {
7274        let request = fallback.request;
7275        if request.index == request.rule_start_index {
7276            return Vec::new();
7277        }
7278        let diagnostic =
7279            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
7280        vec![RecognizeOutcome {
7281            index: request.index,
7282            consumed_eof: request.consumed_eof,
7283            alt_number: request.rule_alt_number,
7284            member_values: request.member_values,
7285            return_values: request.return_values,
7286            diagnostics: vec![diagnostic],
7287            decisions: Vec::new(),
7288            actions: Vec::new(),
7289            nodes: Vec::new(),
7290        }]
7291    }
7292
7293    /// Explores single-token insertion recovery while adding a conjured
7294    /// missing-token error node to the selected parse tree path.
7295    fn single_token_insertion_recovery(
7296        &mut self,
7297        recovery: RecoveryRequest<'_, '_>,
7298    ) -> Vec<RecognizeOutcome> {
7299        let RecoveryRequest {
7300            atn,
7301            transition,
7302            expected_symbols,
7303            target,
7304            request,
7305            visiting,
7306            memo,
7307            expected,
7308        } = recovery;
7309        let RecognizeRequest {
7310            stop_state,
7311            index,
7312            rule_start_index,
7313            decision_start_index,
7314            init_action_rules,
7315            predicates,
7316            semantics,
7317            rule_args,
7318            member_actions,
7319            return_actions,
7320            local_int_arg,
7321            member_values,
7322            return_values,
7323            rule_alt_number,
7324            track_alt_numbers,
7325            consumed_eof,
7326            precedence,
7327            depth,
7328            ..
7329        } = request;
7330        let follow_symbols = state_expected_symbols(atn, transition.target());
7331        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
7332            transition,
7333            index,
7334            atn.max_token_type(),
7335            &expected_symbols,
7336            &follow_symbols,
7337        ) else {
7338            return Vec::new();
7339        };
7340        self.recognize_state(
7341            atn,
7342            RecognizeRequest {
7343                state_number: target,
7344                stop_state,
7345                index,
7346                rule_start_index,
7347                decision_start_index,
7348                init_action_rules,
7349                predicates,
7350                semantics,
7351                rule_args,
7352                member_actions,
7353                return_actions,
7354                local_int_arg,
7355                member_values,
7356                return_values,
7357                rule_alt_number,
7358                track_alt_numbers,
7359                consumed_eof,
7360                precedence,
7361                depth: depth + 1,
7362                recovery_symbols: BTreeSet::new(),
7363                recovery_state: None,
7364            },
7365            visiting,
7366            memo,
7367            expected,
7368        )
7369        .into_iter()
7370        .map(|mut outcome| {
7371            outcome.diagnostics.insert(0, diagnostic.clone());
7372            outcome.nodes.insert(
7373                0,
7374                RecognizedNode::MissingToken {
7375                    token_type,
7376                    at_index: index,
7377                    text: text.clone(),
7378                },
7379            );
7380            outcome
7381        })
7382        .collect()
7383    }
7384
7385    /// Attempts to reach `stop_state` and carries semantic actions for the
7386    /// selected parser path.
7387    #[allow(clippy::too_many_lines)]
7388    fn recognize_state(
7389        &mut self,
7390        atn: &Atn,
7391        request: RecognizeRequest<'_>,
7392        visiting: &mut BTreeSet<RecognizeKey>,
7393        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
7394        expected: &mut ExpectedTokens,
7395    ) -> Vec<RecognizeOutcome> {
7396        let request_template = request.clone();
7397        let RecognizeRequest {
7398            state_number,
7399            stop_state,
7400            index,
7401            rule_start_index,
7402            decision_start_index,
7403            init_action_rules,
7404            predicates,
7405            semantics,
7406            rule_args,
7407            member_actions,
7408            return_actions,
7409            local_int_arg,
7410            member_values,
7411            return_values,
7412            rule_alt_number,
7413            track_alt_numbers,
7414            consumed_eof,
7415            precedence,
7416            depth,
7417            recovery_symbols,
7418            recovery_state,
7419        } = request;
7420        if depth > RECOGNITION_DEPTH_LIMIT {
7421            return Vec::new();
7422        }
7423        if state_number == stop_state {
7424            return stop_outcome(
7425                index,
7426                consumed_eof,
7427                rule_alt_number,
7428                member_values,
7429                return_values,
7430            );
7431        }
7432        let key = RecognizeKey {
7433            state_number,
7434            stop_state,
7435            index,
7436            rule_start_index,
7437            decision_start_index,
7438            local_int_arg,
7439            member_values: member_values.clone(),
7440            return_values: return_values.clone(),
7441            rule_alt_number,
7442            track_alt_numbers,
7443            consumed_eof,
7444            precedence,
7445            recovery_symbols: recovery_symbols.clone(),
7446            recovery_state,
7447        };
7448        if let Some(outcomes) = memo.get(&key) {
7449            return outcomes.clone();
7450        }
7451
7452        let visit_key = key.clone();
7453        if !visiting.insert(visit_key.clone()) {
7454            return Vec::new();
7455        }
7456
7457        let Some(state) = atn.state(state_number) else {
7458            visiting.remove(&visit_key);
7459            return Vec::new();
7460        };
7461        let next_decision_start_index = if starts_prediction_decision(state) {
7462            Some(index)
7463        } else {
7464            decision_start_index
7465        };
7466        let (epsilon_recovery_symbols, epsilon_recovery_state) =
7467            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
7468        let mut outcomes = Vec::new();
7469        for (transition_index, transition) in state.transitions.iter().enumerate() {
7470            let decision = transition_decision(atn, state, transition_index, predicates);
7471            let next_alt_number =
7472                next_alt_number(state, transition_index, rule_alt_number, track_alt_numbers);
7473            match transition {
7474                Transition::Epsilon { target } | Transition::Action { target, .. } => {
7475                    let action_rule_index = match transition {
7476                        Transition::Action { rule_index, .. } => Some(*rule_index),
7477                        _ => None,
7478                    };
7479                    outcomes.extend(self.recognize_epsilon_or_action_step(
7480                        atn,
7481                        &request_template,
7482                        EpsilonActionStep {
7483                            source_state: state_number,
7484                            target: *target,
7485                            action_rule_index,
7486                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
7487                            decision,
7488                            decision_start_index: next_decision_start_index,
7489                            alt_number: next_alt_number,
7490                            recovery_symbols: epsilon_recovery_symbols.clone(),
7491                            recovery_state: epsilon_recovery_state,
7492                        },
7493                        RecognizeScratch {
7494                            visiting,
7495                            memo,
7496                            expected,
7497                        },
7498                    ));
7499                }
7500                Transition::Predicate {
7501                    target,
7502                    rule_index,
7503                    pred_index,
7504                    ..
7505                } => {
7506                    let predicate = PredicateEval {
7507                        index,
7508                        rule_index: *rule_index,
7509                        pred_index: *pred_index,
7510                        predicates,
7511                        semantics,
7512                        context: None,
7513                        local_int_arg,
7514                        member_values: &member_values,
7515                    };
7516                    if self.parser_predicate_matches(predicate) {
7517                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
7518                        outcomes.extend(
7519                            self.recognize_state(
7520                                atn,
7521                                RecognizeRequest {
7522                                    state_number: *target,
7523                                    stop_state,
7524                                    index,
7525                                    rule_start_index,
7526                                    decision_start_index: next_decision_start_index,
7527                                    init_action_rules,
7528                                    predicates,
7529                                    semantics,
7530                                    rule_args,
7531                                    member_actions,
7532                                    return_actions,
7533                                    local_int_arg,
7534                                    member_values: member_values.clone(),
7535                                    return_values: return_values.clone(),
7536                                    rule_alt_number: next_alt_number,
7537                                    track_alt_numbers,
7538                                    consumed_eof,
7539                                    precedence,
7540                                    depth: depth + 1,
7541                                    recovery_symbols: epsilon_recovery_symbols.clone(),
7542                                    recovery_state: epsilon_recovery_state,
7543                                },
7544                                visiting,
7545                                memo,
7546                                expected,
7547                            )
7548                            .into_iter()
7549                            .map(|mut outcome| {
7550                                prepend_decision(&mut outcome, decision);
7551                                if let Some(rule_index) = left_recursive_boundary {
7552                                    outcome.nodes.insert(
7553                                        0,
7554                                        RecognizedNode::LeftRecursiveBoundary { rule_index },
7555                                    );
7556                                }
7557                                outcome
7558                            }),
7559                        );
7560                    } else if let Some(message) = semantics
7561                        .and_then(|semantics| {
7562                            self.parser_semantic_ir_predicate_failure_message(
7563                                *rule_index,
7564                                *pred_index,
7565                                semantics,
7566                            )
7567                        })
7568                        .or_else(|| {
7569                            self.parser_predicate_failure_message(
7570                                *rule_index,
7571                                *pred_index,
7572                                predicates,
7573                            )
7574                        })
7575                    {
7576                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
7577                            rule_index: *rule_index,
7578                            index,
7579                            message,
7580                            member_values: member_values.clone(),
7581                            return_values: return_values.clone(),
7582                            rule_alt_number,
7583                        }));
7584                    } else {
7585                        record_predicate_no_viable(expected, next_decision_start_index, index);
7586                    }
7587                }
7588                Transition::Precedence {
7589                    target,
7590                    precedence: transition_precedence,
7591                } => {
7592                    if *transition_precedence >= precedence {
7593                        outcomes.extend(
7594                            self.recognize_state(
7595                                atn,
7596                                RecognizeRequest {
7597                                    state_number: *target,
7598                                    stop_state,
7599                                    index,
7600                                    rule_start_index,
7601                                    decision_start_index: next_decision_start_index,
7602                                    init_action_rules,
7603                                    predicates,
7604                                    semantics,
7605                                    rule_args,
7606                                    member_actions,
7607                                    return_actions,
7608                                    local_int_arg,
7609                                    member_values: member_values.clone(),
7610                                    return_values: return_values.clone(),
7611                                    rule_alt_number: next_alt_number,
7612                                    track_alt_numbers,
7613                                    consumed_eof,
7614                                    precedence,
7615                                    depth: depth + 1,
7616                                    recovery_symbols: epsilon_recovery_symbols.clone(),
7617                                    recovery_state: epsilon_recovery_state,
7618                                },
7619                                visiting,
7620                                memo,
7621                                expected,
7622                            )
7623                            .into_iter()
7624                            .map(|mut outcome| {
7625                                prepend_decision(&mut outcome, decision);
7626                                outcome
7627                            }),
7628                        );
7629                    }
7630                }
7631                Transition::Rule {
7632                    target,
7633                    rule_index,
7634                    follow_state,
7635                    precedence: rule_precedence,
7636                    ..
7637                } => {
7638                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
7639                    else {
7640                        continue;
7641                    };
7642                    let child_local_int_arg =
7643                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
7644                    let expected_before_child = expected.clone();
7645                    let children = self.recognize_state(
7646                        atn,
7647                        RecognizeRequest {
7648                            state_number: *target,
7649                            stop_state: child_stop,
7650                            index,
7651                            rule_start_index: index,
7652                            decision_start_index: None,
7653                            init_action_rules,
7654                            predicates,
7655                            semantics,
7656                            rule_args,
7657                            member_actions,
7658                            return_actions,
7659                            local_int_arg: child_local_int_arg,
7660                            member_values: member_values.clone(),
7661                            return_values: BTreeMap::new(),
7662                            rule_alt_number: 0,
7663                            track_alt_numbers,
7664                            consumed_eof: false,
7665                            precedence: *rule_precedence,
7666                            depth: depth + 1,
7667                            recovery_symbols: epsilon_recovery_symbols.clone(),
7668                            recovery_state: epsilon_recovery_state,
7669                        },
7670                        visiting,
7671                        memo,
7672                        expected,
7673                    );
7674                    let children = if children.is_empty() {
7675                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
7676                            atn,
7677                            rule_index: *rule_index,
7678                            start_index: index,
7679                            follow_state: *follow_state,
7680                            stop_state,
7681                            member_values: member_values.clone(),
7682                            expected,
7683                        })
7684                    } else {
7685                        children
7686                    };
7687                    let preserve_child_expected =
7688                        self.child_expected_reaches_clean_eof(&children, expected);
7689                    restore_expected(
7690                        &children,
7691                        index,
7692                        expected,
7693                        expected_before_child,
7694                        preserve_child_expected,
7695                    );
7696                    for child in children {
7697                        let child_node = RecognizedNode::Rule {
7698                            rule_index: *rule_index,
7699                            invoking_state: invoking_state_number(state_number),
7700                            alt_number: child.alt_number,
7701                            start_index: index,
7702                            stop_index: self.rule_stop_token_index(child.index, child.consumed_eof),
7703                            return_values: child.return_values.clone(),
7704                            children: fold_left_recursive_boundaries(child.nodes.clone()),
7705                        };
7706                        outcomes.extend(
7707                            self.recognize_state(
7708                                atn,
7709                                RecognizeRequest {
7710                                    state_number: *follow_state,
7711                                    stop_state,
7712                                    index: child.index,
7713                                    rule_start_index,
7714                                    decision_start_index: next_decision_start_index,
7715                                    init_action_rules,
7716                                    predicates,
7717                                    semantics,
7718                                    rule_args,
7719                                    member_actions,
7720                                    return_actions,
7721                                    local_int_arg,
7722                                    member_values: child.member_values.clone(),
7723                                    return_values: return_values.clone(),
7724                                    rule_alt_number,
7725                                    track_alt_numbers,
7726                                    consumed_eof: consumed_eof || child.consumed_eof,
7727                                    precedence,
7728                                    depth: depth + 1,
7729                                    recovery_symbols: BTreeSet::new(),
7730                                    recovery_state: None,
7731                                },
7732                                visiting,
7733                                memo,
7734                                expected,
7735                            )
7736                            .into_iter()
7737                            .map(|mut outcome| {
7738                                outcome.consumed_eof |= child.consumed_eof;
7739                                let mut diagnostics = child.diagnostics.clone();
7740                                diagnostics.append(&mut outcome.diagnostics);
7741                                outcome.diagnostics = diagnostics;
7742                                let mut decisions = child.decisions.clone();
7743                                decisions.append(&mut outcome.decisions);
7744                                outcome.decisions = decisions;
7745                                prepend_decision(&mut outcome, decision);
7746                                let mut actions = child.actions.clone();
7747                                if init_action_rules.contains(rule_index) {
7748                                    actions.insert(
7749                                        0,
7750                                        ParserAction::new_rule_init(
7751                                            *rule_index,
7752                                            index,
7753                                            Some(*follow_state),
7754                                        ),
7755                                    );
7756                                }
7757                                actions.append(&mut outcome.actions);
7758                                outcome.actions = actions;
7759                                outcome.nodes.insert(0, child_node.clone());
7760                                outcome
7761                            }),
7762                        );
7763                    }
7764                }
7765                Transition::Atom { target, .. }
7766                | Transition::Range { target, .. }
7767                | Transition::Set { target, .. }
7768                | Transition::NotSet { target, .. }
7769                | Transition::Wildcard { target, .. } => {
7770                    let symbol = self.token_type_at(index);
7771                    if transition.matches(symbol, 1, atn.max_token_type()) {
7772                        let next_index = self.consume_index(index, symbol);
7773                        outcomes.extend(
7774                            self.recognize_state(
7775                                atn,
7776                                RecognizeRequest {
7777                                    state_number: *target,
7778                                    stop_state,
7779                                    index: next_index,
7780                                    rule_start_index,
7781                                    decision_start_index: next_decision_start_index,
7782                                    init_action_rules,
7783                                    predicates,
7784                                    semantics,
7785                                    rule_args,
7786                                    member_actions,
7787                                    return_actions,
7788                                    local_int_arg,
7789                                    member_values: member_values.clone(),
7790                                    return_values: return_values.clone(),
7791                                    rule_alt_number: next_alt_number,
7792                                    track_alt_numbers,
7793                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
7794                                    precedence,
7795                                    depth: depth + 1,
7796                                    recovery_symbols: BTreeSet::new(),
7797                                    recovery_state: None,
7798                                },
7799                                visiting,
7800                                memo,
7801                                expected,
7802                            )
7803                            .into_iter()
7804                            .map(|mut outcome| {
7805                                prepend_decision(&mut outcome, decision);
7806                                outcome.consumed_eof |= symbol == TOKEN_EOF;
7807                                outcome.nodes.insert(0, RecognizedNode::Token { index });
7808                                outcome
7809                            }),
7810                        );
7811                    } else {
7812                        let expected_symbols =
7813                            recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
7814                        if expected_symbols.contains(&symbol) {
7815                            continue;
7816                        }
7817                        expected.record_transition(index, transition, atn.max_token_type());
7818                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
7819                        let before_recovery = outcomes.len();
7820                        let recovery_request = request_template.clone();
7821                        outcomes.extend(
7822                            self.single_token_deletion_recovery(RecoveryRequest {
7823                                atn,
7824                                transition,
7825                                expected_symbols: expected_symbols.clone(),
7826                                target: *target,
7827                                request: recovery_request.clone(),
7828                                visiting,
7829                                memo,
7830                                expected,
7831                            })
7832                            .into_iter()
7833                            .map(|mut outcome| {
7834                                prepend_decision(&mut outcome, decision);
7835                                outcome
7836                            }),
7837                        );
7838                        if !state_is_left_recursive_rule(atn, state) {
7839                            outcomes.extend(
7840                                self.single_token_insertion_recovery(RecoveryRequest {
7841                                    atn,
7842                                    transition,
7843                                    expected_symbols: expected_symbols.clone(),
7844                                    target: *target,
7845                                    request: recovery_request.clone(),
7846                                    visiting,
7847                                    memo,
7848                                    expected,
7849                                })
7850                                .into_iter()
7851                                .map(|mut outcome| {
7852                                    prepend_decision(&mut outcome, decision);
7853                                    outcome
7854                                }),
7855                            );
7856                        }
7857                        outcomes.extend(self.current_token_deletion_recovery(
7858                            CurrentTokenDeletionRequest {
7859                                atn,
7860                                expected_symbols: expected_symbols.clone(),
7861                                request: recovery_request.clone(),
7862                                visiting,
7863                                memo,
7864                                expected,
7865                            },
7866                        ));
7867                        if outcomes.len() == before_recovery {
7868                            outcomes.extend(self.consuming_failure_fallback(
7869                                ConsumingFailureFallback {
7870                                    atn,
7871                                    target: *target,
7872                                    request: recovery_request,
7873                                    symbol,
7874                                    expected_symbols,
7875                                    decision_start_index: next_decision_start_index,
7876                                    decision,
7877                                },
7878                                visiting,
7879                                memo,
7880                                expected,
7881                            ));
7882                        }
7883                    }
7884                }
7885            }
7886        }
7887
7888        visiting.remove(&visit_key);
7889        self.record_prediction_diagnostics(atn, state, index, &outcomes);
7890        if matches!(
7891            self.prediction_mode,
7892            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
7893        ) {
7894            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes);
7895        }
7896        dedupe_outcomes(&mut outcomes);
7897        memo.insert(key, outcomes.clone());
7898        outcomes
7899    }
7900
7901    /// Follows an epsilon or semantic-action transition while preserving the
7902    /// path-local side effects that may later become generated action output.
7903    fn recognize_epsilon_or_action_step(
7904        &mut self,
7905        atn: &Atn,
7906        request: &RecognizeRequest<'_>,
7907        step: EpsilonActionStep,
7908        scratch: RecognizeScratch<'_>,
7909    ) -> Vec<RecognizeOutcome> {
7910        let RecognizeScratch {
7911            visiting,
7912            memo,
7913            expected,
7914        } = scratch;
7915        let action = step.action_rule_index.map(|rule_index| {
7916            ParserAction::new(
7917                step.source_state,
7918                rule_index,
7919                request.rule_start_index,
7920                self.rule_stop_token_index(request.index, request.consumed_eof),
7921            )
7922        });
7923        let next_member_values = if action.is_some() {
7924            member_values_after_action(
7925                step.source_state,
7926                request.member_actions,
7927                request.semantics,
7928                &request.member_values,
7929            )
7930        } else {
7931            request.member_values.clone()
7932        };
7933        let next_return_values = action.map_or_else(
7934            || request.return_values.clone(),
7935            |action| {
7936                return_values_after_action(
7937                    step.source_state,
7938                    action.rule_index(),
7939                    request.return_actions,
7940                    request.semantics,
7941                    &request.return_values,
7942                )
7943            },
7944        );
7945
7946        self.recognize_state(
7947            atn,
7948            RecognizeRequest {
7949                state_number: step.target,
7950                stop_state: request.stop_state,
7951                index: request.index,
7952                rule_start_index: request.rule_start_index,
7953                decision_start_index: step.decision_start_index,
7954                init_action_rules: request.init_action_rules,
7955                predicates: request.predicates,
7956                semantics: request.semantics,
7957                rule_args: request.rule_args,
7958                member_actions: request.member_actions,
7959                return_actions: request.return_actions,
7960                local_int_arg: request.local_int_arg,
7961                member_values: next_member_values,
7962                return_values: next_return_values,
7963                rule_alt_number: step.alt_number,
7964                track_alt_numbers: request.track_alt_numbers,
7965                consumed_eof: request.consumed_eof,
7966                precedence: request.precedence,
7967                depth: request.depth + 1,
7968                recovery_symbols: step.recovery_symbols,
7969                recovery_state: step.recovery_state,
7970            },
7971            visiting,
7972            memo,
7973            expected,
7974        )
7975        .into_iter()
7976        .map(|mut outcome| {
7977            prepend_decision(&mut outcome, step.decision);
7978            if let Some(rule_index) = step.left_recursive_boundary {
7979                outcome
7980                    .nodes
7981                    .insert(0, RecognizedNode::LeftRecursiveBoundary { rule_index });
7982            }
7983            if let Some(action) = action {
7984                outcome.actions.insert(0, action);
7985            }
7986            outcome
7987        })
7988        .collect()
7989    }
7990
7991    /// Reads the token type at an absolute token-stream index without moving
7992    /// the parser's stream cursor. The fast recognizer probes lookahead at
7993    /// every state visit, so avoiding the seek round-trip is a measurable
7994    /// hot-path win on long inputs.
7995    fn token_type_at(&mut self, index: usize) -> i32 {
7996        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
7997            self.input.fill();
7998        }
7999        self.input.token_type_at_index(index)
8000    }
8001
8002    /// Returns the cached `state_expected_symbols` set for an ATN state.
8003    ///
8004    /// The fast recognizer consults this set on every state visit through
8005    /// `next_recovery_context`; the underlying DFS is a pure function of the
8006    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
8007    ///
8008    /// Caching is layered through `intern_recovery_symbols` so two ATN states
8009    /// with the same expected-symbol set share one `Rc`. That invariant is
8010    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
8011    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
8012    /// always interned before it ends up in a key.
8013    fn cached_state_expected_symbols(
8014        &mut self,
8015        atn: &Atn,
8016        state_number: usize,
8017    ) -> Rc<BTreeSet<i32>> {
8018        if let Some(cached) = self.state_expected_cache.get(&state_number) {
8019            return Rc::clone(cached);
8020        }
8021        let symbols = state_expected_symbols(atn, state_number);
8022        let entry = self.intern_recovery_symbols(symbols);
8023        self.state_expected_cache
8024            .insert(state_number, Rc::clone(&entry));
8025        entry
8026    }
8027
8028    fn cached_state_expected_token_set(
8029        &mut self,
8030        atn: &Atn,
8031        state_number: usize,
8032    ) -> Rc<TokenBitSet> {
8033        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
8034            return Rc::clone(cached);
8035        }
8036        // Purely a function of the ATN, so back the per-parser cache with the
8037        // thread-shared one — fresh parser instances (one per parse in
8038        // generated usage) start warm instead of rewalking the ATN.
8039        let symbols = with_shared_atn_caches(atn, |cache| {
8040            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
8041                return Rc::clone(cached);
8042            }
8043            let symbols = Rc::new(state_expected_token_set(atn, state_number));
8044            cache
8045                .state_expected_tokens
8046                .insert(state_number, Rc::clone(&symbols));
8047            symbols
8048        });
8049        self.state_expected_token_cache
8050            .insert(state_number, Rc::clone(&symbols));
8051        symbols
8052    }
8053
8054    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
8055        if self.rule_stop_reach_cache.len() <= state_number {
8056            self.rule_stop_reach_cache
8057                .resize_with(atn.states().len().max(state_number + 1), || None);
8058        }
8059        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
8060            return reaches;
8061        }
8062        let reaches = with_shared_atn_caches(atn, |cache| {
8063            *cache
8064                .rule_stop_reach
8065                .entry(state_number)
8066                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
8067        });
8068        self.rule_stop_reach_cache[state_number] = Some(reaches);
8069        reaches
8070    }
8071
8072    /// Returns the parser's empty `recovery_symbols` singleton so callers can
8073    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
8074    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
8075        Rc::clone(&self.empty_recovery_symbols)
8076    }
8077
8078    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
8079    /// recognizer can hash and compare keys by pointer.
8080    ///
8081    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
8082    /// come from this method or the empty singleton; otherwise two
8083    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
8084    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
8085    /// recognition coordinates.
8086    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
8087        if set.is_empty() {
8088            return Rc::clone(&self.empty_recovery_symbols);
8089        }
8090        let candidate = Rc::new(set);
8091        match self.recovery_symbols_intern.get(&candidate) {
8092            Some(existing) => Rc::clone(existing),
8093            None => {
8094                self.recovery_symbols_intern
8095                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
8096                candidate
8097            }
8098        }
8099    }
8100
8101    /// Returns the cached look-1 entry for a decision state, computing it on
8102    /// first use. Multi-alternative states are visited many times during
8103    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
8104    /// hash lookup per visit.
8105    fn cached_decision_lookahead(
8106        &mut self,
8107        atn: &Atn,
8108        state: &AtnState,
8109        rule_stop_state: usize,
8110    ) -> Rc<DecisionLookahead> {
8111        // Hit the parser-instance cache first. Decision lookahead is purely
8112        // a function of the ATN/state, so on a warm cache we skip the
8113        // thread-local + RefCell + HashMap-entry dance through
8114        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
8115        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
8116        // hashmap-entry overhead in profiles.
8117        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number) {
8118            return Rc::clone(cached);
8119        }
8120        let entry = with_shared_atn_caches(atn, |cache| {
8121            if let Some(cached) = cache.decision_lookahead.get(&state.state_number) {
8122                return Rc::clone(cached);
8123            }
8124            let mut entry = DecisionLookahead {
8125                transitions: Vec::with_capacity(state.transitions.len()),
8126            };
8127            for transition in &state.transitions {
8128                entry.transitions.push(transition_first_set(
8129                    atn,
8130                    transition,
8131                    rule_stop_state,
8132                    &mut cache.first_set,
8133                ));
8134            }
8135            let entry = Rc::new(entry);
8136            cache
8137                .decision_lookahead
8138                .insert(state.state_number, Rc::clone(&entry));
8139            entry
8140        });
8141        self.decision_lookahead_cache
8142            .insert(state.state_number, Rc::clone(&entry));
8143        entry
8144    }
8145
8146    fn cached_rule_first_set(
8147        &mut self,
8148        atn: &Atn,
8149        target: usize,
8150        child_stop: usize,
8151    ) -> Rc<FirstSet> {
8152        if self.rule_first_set_cache.len() <= target {
8153            self.rule_first_set_cache
8154                .resize_with(atn.states().len().max(target + 1), || None);
8155        }
8156        if let Some(cached) = self
8157            .rule_first_set_cache
8158            .get(target)
8159            .and_then(Option::as_ref)
8160        {
8161            return Rc::clone(cached);
8162        }
8163        let first = with_shared_first_set_cache(atn, |cache| {
8164            rule_first_set(atn, target, child_stop, cache)
8165        });
8166        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
8167        first
8168    }
8169
8170    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
8171        if self.empty_cycle_cache.len() <= state_number {
8172            self.empty_cycle_cache
8173                .resize_with(atn.states().len().max(state_number + 1), || None);
8174        }
8175        if let Some(cached) = self.empty_cycle_cache[state_number] {
8176            return cached;
8177        }
8178        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
8179        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
8180        self.empty_cycle_cache[state_number] = Some(result);
8181        result
8182    }
8183
8184    fn empty_path_reaches_state(
8185        &mut self,
8186        atn: &Atn,
8187        state_number: usize,
8188        target_state: usize,
8189        visited: &mut FxHashSet<usize>,
8190    ) -> bool {
8191        if !visited.insert(state_number) {
8192            return false;
8193        }
8194        let Some(state) = atn.state(state_number) else {
8195            return false;
8196        };
8197        for transition in &state.transitions {
8198            match transition {
8199                Transition::Atom { .. }
8200                | Transition::Range { .. }
8201                | Transition::Set { .. }
8202                | Transition::NotSet { .. }
8203                | Transition::Wildcard { .. } => {}
8204                Transition::Rule {
8205                    target,
8206                    rule_index,
8207                    follow_state,
8208                    ..
8209                } => {
8210                    if *target == target_state
8211                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
8212                    {
8213                        return true;
8214                    }
8215                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
8216                    else {
8217                        continue;
8218                    };
8219                    if self
8220                        .cached_rule_first_set(atn, *target, child_stop)
8221                        .nullable
8222                        && (*follow_state == target_state
8223                            || self.empty_path_reaches_state(
8224                                atn,
8225                                *follow_state,
8226                                target_state,
8227                                visited,
8228                            ))
8229                    {
8230                        return true;
8231                    }
8232                }
8233                Transition::Epsilon { target }
8234                | Transition::Predicate { target, .. }
8235                | Transition::Action { target, .. }
8236                | Transition::Precedence { target, .. } => {
8237                    if *target == target_state
8238                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
8239                    {
8240                        return true;
8241                    }
8242                }
8243            }
8244        }
8245        false
8246    }
8247
8248    /// Decides whether a clean one-outcome entry is worth storing in the full
8249    /// outcome memo table for this parse.
8250    fn should_memoize_single_outcome(&mut self, key: &FastRecognizeKey) -> bool {
8251        match self.single_outcome_memo_mode {
8252            SingleOutcomeMemoMode::Promote => true,
8253            SingleOutcomeMemoMode::Sparse => false,
8254            SingleOutcomeMemoMode::Probe => {
8255                self.single_outcome_probe_samples += 1;
8256                if !self.single_outcome_probe_seen.insert(key.clone()) {
8257                    self.single_outcome_probe_repeats += 1;
8258                }
8259                if self.single_outcome_probe_repeats >= CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
8260                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Promote;
8261                    self.single_outcome_probe_seen.clear();
8262                    return true;
8263                }
8264                if self.single_outcome_probe_samples >= CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT {
8265                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Sparse;
8266                    self.single_outcome_probe_seen.clear();
8267                    return false;
8268                }
8269                true
8270            }
8271        }
8272    }
8273
8274    /// Clones the visible token at an absolute token-stream index.
8275    fn token_at(&mut self, index: usize) -> Option<CommonToken> {
8276        self.input.get(index).cloned()
8277    }
8278
8279    /// Clones the shared token handle at an absolute token-stream index.
8280    fn token_ref_at(&mut self, index: usize) -> Option<TokenRef> {
8281        self.input.get_ref(index)
8282    }
8283
8284    /// Normalizes the current token-stream cursor to the next parser-visible
8285    /// token before capturing a rule start boundary.
8286    fn current_visible_index(&mut self) -> usize {
8287        let index = self.input.index();
8288        self.input.seek(index);
8289        self.input.index()
8290    }
8291
8292    /// Reports whether a child rule reached EOF cleanly while also recording
8293    /// an EOF expectation from a longer path inside that child.
8294    fn child_expected_reaches_clean_eof(
8295        &mut self,
8296        children: &[RecognizeOutcome],
8297        expected: &ExpectedTokens,
8298    ) -> bool {
8299        let Some(index) = expected.index else {
8300            return false;
8301        };
8302        self.token_type_at(index) == TOKEN_EOF
8303            && children
8304                .iter()
8305                .any(|child| child.diagnostics.is_empty() && child.index == index)
8306    }
8307
8308    /// Finds the previous token visible to the parser before `index`.
8309    ///
8310    /// The token stream cursor skips hidden-channel tokens, so subtracting one
8311    /// from a visible-token index can point at whitespace. Parser intervals use
8312    /// this helper to stop at the previous visible token while preserving hidden
8313    /// text inside the rendered interval.
8314    fn previous_token_index(&mut self, index: usize) -> Option<usize> {
8315        self.input.previous_visible_token_index(index)
8316    }
8317
8318    /// Returns the token-stream index used as a rule stop boundary.
8319    ///
8320    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
8321    /// stop at `index` rather than at the previous visible token.
8322    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
8323        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
8324            Some(index)
8325        } else {
8326            self.previous_token_index(index)
8327        }
8328    }
8329
8330    /// Stop-token index for a rule's `@after` action, matching the boundary that
8331    /// `finish_rule` records on the rule context.
8332    ///
8333    /// A rule that matched EOF leaves the cursor parked on the EOF token
8334    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
8335    /// the current index rather than the previous visible token. Without this,
8336    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
8337    /// report the token before EOF (or `None` for empty input), diverging from
8338    /// the rule context that `finish_rule` builds.
8339    ///
8340    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
8341    /// rule ends right before EOF without matching it (the cursor is parked on
8342    /// EOF, but the rule did not consume it). Prefer
8343    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
8344    /// rule context already recorded with the real flag. Kept for callers without
8345    /// the rule tree in hand.
8346    #[must_use]
8347    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
8348        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
8349        self.rule_stop_token_index(current_index, consumed_eof)
8350    }
8351
8352    /// Stop-token index for a rule's `@after` action, taken from the stop token
8353    /// the rule context already recorded.
8354    ///
8355    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
8356    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
8357    /// the rule context — even when the rule ends immediately before EOF without
8358    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
8359    /// to the cursor-based inference only when the tree carries no rule stop.
8360    #[must_use]
8361    pub fn after_action_stop_index_for_tree(
8362        &mut self,
8363        tree: &ParseTree,
8364        current_index: usize,
8365    ) -> Option<usize> {
8366        if let ParseTree::Rule(rule) = tree {
8367            if let Some(stop) = rule.context().stop() {
8368                let token_index = stop.token_index();
8369                if token_index >= 0 {
8370                    return Some(token_index.unsigned_abs());
8371                }
8372            }
8373        }
8374        self.after_action_stop_index(current_index)
8375    }
8376
8377    /// Start-token index for a rule's `@after` action, taken from the start token
8378    /// the rule context already recorded.
8379    ///
8380    /// `enter_rule` sets the rule context start to the first visible token (it
8381    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
8382    /// `$text` in an `@after` action aligned with the rule context — even when the
8383    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
8384    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
8385    /// the tree carries no rule start.
8386    #[must_use]
8387    pub fn after_action_start_index_for_tree(
8388        &self,
8389        tree: &ParseTree,
8390        fallback_index: usize,
8391    ) -> usize {
8392        if let ParseTree::Rule(rule) = tree {
8393            if let Some(start) = rule.context().start() {
8394                let token_index = start.token_index();
8395                if token_index >= 0 {
8396                    return token_index.unsigned_abs();
8397                }
8398            }
8399        }
8400        fallback_index
8401    }
8402
8403    /// Returns the rule stop token for a selected parse path.
8404    ///
8405    /// EOF transitions do not advance the token-stream cursor, so an EOF match
8406    /// must use the current token rather than the previous visible token.
8407    fn rule_stop_token_ref(&mut self, index: usize, consumed_eof: bool) -> Option<TokenRef> {
8408        self.rule_stop_token_index(index, consumed_eof)
8409            .and_then(|token_index| self.token_ref_at(token_index))
8410    }
8411
8412    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
8413    ///
8414    /// Generated Java reports the failed-predicate message at the current
8415    /// lookahead, then consumes until rule recovery can resume. The metadata
8416    /// runtime models the same visible tree shape by keeping skipped tokens as
8417    /// error nodes and returning from the active rule at EOF.
8418    fn predicate_failure_recovery(
8419        &mut self,
8420        request: PredicateFailureRecovery<'_>,
8421    ) -> RecognizeOutcome {
8422        let PredicateFailureRecovery {
8423            rule_index,
8424            index,
8425            message,
8426            member_values,
8427            return_values,
8428            rule_alt_number,
8429        } = request;
8430        let rule_name = self
8431            .rule_names()
8432            .get(rule_index)
8433            .map_or_else(|| rule_index.to_string(), Clone::clone);
8434        let diagnostic = diagnostic_for_token(
8435            self.token_at(index).as_ref(),
8436            format!("rule {rule_name} {message}"),
8437        );
8438        let mut nodes = Vec::new();
8439        let mut next_index = index;
8440        loop {
8441            let symbol = self.token_type_at(next_index);
8442            if symbol == TOKEN_EOF {
8443                break;
8444            }
8445            nodes.push(RecognizedNode::ErrorToken { index: next_index });
8446            let after = self.consume_index(next_index, symbol);
8447            if after == next_index {
8448                break;
8449            }
8450            next_index = after;
8451        }
8452        RecognizeOutcome {
8453            index: next_index,
8454            consumed_eof: false,
8455            alt_number: rule_alt_number,
8456            member_values,
8457            return_values,
8458            diagnostics: vec![diagnostic],
8459            decisions: Vec::new(),
8460            actions: Vec::new(),
8461            nodes,
8462        }
8463    }
8464
8465    /// Evaluates a user hook for a predicate coordinate that has no generated
8466    /// runtime table entry.
8467    fn parser_semantic_hook_result(
8468        &mut self,
8469        request: ParserSemanticHookRequest<'_>,
8470    ) -> Option<bool> {
8471        let ParserSemanticHookRequest {
8472            index,
8473            rule_index,
8474            pred_index,
8475            context,
8476            local_int_arg,
8477            member_values,
8478        } = request;
8479        let rule_name = self.rule_names().get(rule_index).cloned();
8480        self.input.seek(index);
8481        let input = &mut self.input;
8482        let semantic_hooks = &mut self.semantic_hooks;
8483        let mut ctx = ParserSemCtx {
8484            input,
8485            rule_index,
8486            coordinate_index: pred_index,
8487            rule_name,
8488            context,
8489            tree: None,
8490            local_int_arg,
8491            member_values,
8492            action: None,
8493        };
8494        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
8495    }
8496
8497    /// Re-inserts unknown-predicate coordinates recorded before a nested
8498    /// interpreted recognition, preserving order and skipping any the nested
8499    /// call already recorded, so a generated parent's fail-loud coordinates
8500    /// survive descending into an interpreted child.
8501    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
8502        if prior.is_empty() {
8503            return;
8504        }
8505        let mut merged = prior;
8506        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
8507            if !merged.contains(&coordinate) {
8508                merged.push(coordinate);
8509            }
8510        }
8511        self.unknown_predicate_hits = merged;
8512    }
8513
8514    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
8515    /// that has no entry in the generated predicate table.
8516    ///
8517    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
8518    /// the guarded path is abandoned; the parse entry surfaces the recorded
8519    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
8520    /// because a parse that consulted an unknown predicate is unreliable no
8521    /// matter which paths were ultimately selected.
8522    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
8523        apply_unknown_predicate_policy(
8524            self.unknown_predicate_policy,
8525            rule_index,
8526            pred_index,
8527            &mut self.unknown_predicate_hits,
8528        )
8529    }
8530
8531    /// Builds the fail-loud error for unknown predicate coordinates recorded
8532    /// by the current parse, if any.
8533    fn unknown_semantic_error(&self) -> Option<AntlrError> {
8534        use std::fmt::Write as _;
8535        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
8536            return None;
8537        }
8538        let mut message = String::new();
8539        for (rule_index, pred_index) in &self.unknown_predicate_hits {
8540            if !message.is_empty() {
8541                message.push_str("; ");
8542            }
8543            let _ = match self.rule_names().get(*rule_index) {
8544                Some(rule_name) => write!(
8545                    message,
8546                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
8547                ),
8548                None => write!(
8549                    message,
8550                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
8551                ),
8552            };
8553        }
8554        for (rule_index, source_state) in &self.unhandled_action_hits {
8555            if !message.is_empty() {
8556                message.push_str("; ");
8557            }
8558            let _ = match self.rule_names().get(*rule_index) {
8559                Some(rule_name) => write!(
8560                    message,
8561                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
8562                ),
8563                None => write!(
8564                    message,
8565                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
8566                ),
8567            };
8568        }
8569        Some(AntlrError::Unsupported(message))
8570    }
8571
8572    /// Evaluates one lowered predicate expression at the requested input
8573    /// position.
8574    ///
8575    /// This sits in the prediction hot loop, so the context borrows the
8576    /// speculative member state read-only and the rule name by reference —
8577    /// no per-evaluation allocation. Only the hook escape path materializes
8578    /// owned copies, and only when a hook is actually consulted.
8579    fn parser_semir_predicate_matches(
8580        &mut self,
8581        semantics: &ParserSemantics,
8582        predicate: &ParserSemanticPredicate,
8583        request: ParserSemanticHookRequest<'_>,
8584    ) -> bool {
8585        self.input.seek(request.index);
8586        let rule_name = self
8587            .data
8588            .rule_names()
8589            .get(request.rule_index)
8590            .map(String::as_str);
8591        let unknown_predicate_policy = self.unknown_predicate_policy;
8592        let mut ctx = ParserSemIrCtx {
8593            input: &mut self.input,
8594            semantic_hooks: &mut self.semantic_hooks,
8595            rule_index: request.rule_index,
8596            coordinate_index: request.pred_index,
8597            rule_name,
8598            context: request.context,
8599            local_int_arg: request.local_int_arg,
8600            member_values: request.member_values,
8601            invoked_predicates: &mut self.invoked_predicates,
8602            unknown_predicate_policy,
8603            unknown_predicate_hits: &mut self.unknown_predicate_hits,
8604        };
8605        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
8606    }
8607
8608    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
8609        let PredicateEval {
8610            index,
8611            rule_index,
8612            pred_index,
8613            predicates,
8614            semantics,
8615            context,
8616            local_int_arg,
8617            member_values,
8618        } = eval;
8619        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
8620            semantics
8621                .predicates
8622                .iter()
8623                .find(|predicate| {
8624                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
8625                })
8626                .map(|predicate| (semantics, predicate))
8627        }) {
8628            return self.parser_semir_predicate_matches(
8629                semantics,
8630                predicate,
8631                ParserSemanticHookRequest {
8632                    index,
8633                    rule_index,
8634                    pred_index,
8635                    context,
8636                    local_int_arg,
8637                    member_values,
8638                },
8639            );
8640        }
8641        let Some((_, _, predicate)) = predicates
8642            .iter()
8643            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
8644        else {
8645            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
8646                index,
8647                rule_index,
8648                pred_index,
8649                context,
8650                local_int_arg,
8651                member_values,
8652            }) {
8653                return result;
8654            }
8655            return self.unknown_predicate_result(rule_index, pred_index);
8656        };
8657        self.input.seek(index);
8658        match predicate {
8659            ParserPredicate::True => true,
8660            ParserPredicate::False => false,
8661            ParserPredicate::FalseWithMessage { .. } => false,
8662            ParserPredicate::Invoke { value } => {
8663                let key = (rule_index, pred_index);
8664                if !self.invoked_predicates.contains(&key) {
8665                    self.invoked_predicates.push(key);
8666                    use std::io::Write as _;
8667                    let mut stdout = std::io::stdout().lock();
8668                    let _ = writeln!(stdout, "eval={value}");
8669                }
8670                *value
8671            }
8672            ParserPredicate::LookaheadTextEquals { offset, text } => {
8673                self.input.lt(*offset).and_then(Token::text) == Some(*text)
8674            }
8675            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
8676                self.la(*offset) != *token_type
8677            }
8678            ParserPredicate::TokenPairAdjacent => {
8679                let Some(first) = self.input.lt(-2).map(Token::token_index) else {
8680                    return false;
8681                };
8682                let Some(second) = self.input.lt(-1).map(Token::token_index) else {
8683                    return false;
8684                };
8685                first + 1 == second
8686            }
8687            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
8688                .and_then(|context| {
8689                    context.children().iter().find_map(|child| match child {
8690                        ParseTree::Rule(rule) if rule.context().rule_index() == *rule_index => {
8691                            Some(child.text())
8692                        }
8693                        ParseTree::Rule(_) | ParseTree::Terminal(_) | ParseTree::Error(_) => None,
8694                    })
8695                })
8696                .is_none_or(|actual| actual != *text),
8697            ParserPredicate::LocalIntEquals { value } => {
8698                local_int_arg.is_none_or(|(_, actual)| actual == *value)
8699            }
8700            ParserPredicate::LocalIntLessOrEqual { value } => {
8701                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
8702            }
8703            ParserPredicate::MemberModuloEquals {
8704                member,
8705                modulus,
8706                value,
8707                equals,
8708            } => {
8709                if *modulus == 0 {
8710                    return false;
8711                }
8712                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
8713                (actual == *value) == *equals
8714            }
8715            ParserPredicate::MemberEquals {
8716                member,
8717                value,
8718                equals,
8719            } => {
8720                let actual = member_values.get(member).copied().unwrap_or_default();
8721                (actual == *value) == *equals
8722            }
8723        }
8724    }
8725
8726    /// Returns a generated fail-option message for a predicate coordinate.
8727    fn parser_predicate_failure_message(
8728        &self,
8729        rule_index: usize,
8730        pred_index: usize,
8731        predicates: &[(usize, usize, ParserPredicate)],
8732    ) -> Option<&'static str> {
8733        predicates
8734            .iter()
8735            .find_map(|(rule, pred, predicate)| match predicate {
8736                ParserPredicate::FalseWithMessage { message }
8737                    if *rule == rule_index && *pred == pred_index =>
8738                {
8739                    Some(*message)
8740                }
8741                _ => None,
8742            })
8743    }
8744
8745    /// Returns a generated fail-option message for a `SemIR` predicate
8746    /// coordinate.
8747    pub fn parser_semantic_ir_predicate_failure_message(
8748        &self,
8749        rule_index: usize,
8750        pred_index: usize,
8751        semantics: &ParserSemantics,
8752    ) -> Option<&'static str> {
8753        semantics
8754            .predicates
8755            .iter()
8756            .find(|predicate| {
8757                predicate.rule_index == rule_index && predicate.pred_index == pred_index
8758            })
8759            .and_then(|predicate| predicate.failure_message)
8760    }
8761
8762    /// Returns the token-stream index after consuming `symbol` at `index`.
8763    ///
8764    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
8765    /// index stable and rely on `consumed_eof` to record that EOF was matched.
8766    /// The parser's stream cursor is left untouched: speculative recognition
8767    /// reads ahead by absolute index, so paying for `seek` on every visited
8768    /// state would dominate the hot path. Real consumption is committed by
8769    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
8770    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
8771        if symbol == TOKEN_EOF {
8772            return index;
8773        }
8774        self.input.next_visible_after(index)
8775    }
8776
8777    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
8778    /// decision that failed after consuming a shared prefix.
8779    fn no_viable_alternative(
8780        &mut self,
8781        start_index: usize,
8782        error_index: usize,
8783    ) -> ParserDiagnostic {
8784        let text = display_input_text(&self.input.text(start_index, error_index));
8785        diagnostic_for_token(
8786            self.token_at(error_index).as_ref(),
8787            format!("no viable alternative at input '{text}'"),
8788        )
8789    }
8790
8791    /// Selects the diagnostic for a failed consuming transition after all
8792    /// recovery repairs have been ruled out.
8793    fn recovery_failure_diagnostic(
8794        &mut self,
8795        index: usize,
8796        decision_start_index: Option<usize>,
8797        expected_symbols: &BTreeSet<i32>,
8798    ) -> ParserDiagnostic {
8799        if expected_symbols.len() > 1 {
8800            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
8801                return self.no_viable_alternative(decision_start, index);
8802            }
8803        }
8804        diagnostic_for_token(
8805            self.token_at(index).as_ref(),
8806            format!(
8807                "mismatched input {} expecting {}",
8808                self.token_at(index)
8809                    .as_ref()
8810                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8811                self.expected_symbols_display(expected_symbols)
8812            ),
8813        )
8814    }
8815
8816    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
8817    /// instead of inserting missing tokens in the caller.
8818    fn eof_rule_recovery_diagnostic(
8819        &mut self,
8820        index: usize,
8821        expected_symbols: &BTreeSet<i32>,
8822        expected: &ExpectedTokens,
8823    ) -> ParserDiagnostic {
8824        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
8825            &expected.symbols
8826        } else {
8827            expected_symbols
8828        };
8829        diagnostic_for_token(
8830            self.token_at(index).as_ref(),
8831            format!(
8832                "mismatched input {} expecting {}",
8833                self.token_at(index)
8834                    .as_ref()
8835                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8836                self.expected_symbols_display(symbols)
8837            ),
8838        )
8839    }
8840
8841    /// Returns token text for a buffered token interval used by generated
8842    /// `$text` actions.
8843    ///
8844    /// ANTLR treats EOF as a range boundary rather than printable input text,
8845    /// even when an action interval explicitly stops at the EOF token.
8846    pub fn text_interval(&mut self, start: usize, stop: Option<usize>) -> String {
8847        let Some(stop) = stop else {
8848            return String::new();
8849        };
8850        let stop = if self
8851            .token_at(stop)
8852            .is_some_and(|token| token.token_type() == TOKEN_EOF)
8853        {
8854            let Some(previous) = self.previous_token_index(stop) else {
8855                return String::new();
8856            };
8857            previous
8858        } else {
8859            stop
8860        };
8861        self.input.text(start, stop)
8862    }
8863
8864    /// Resets per-parse prediction diagnostics while keeping the parser-level
8865    /// reporting flag configured by generated harness code.
8866    fn clear_prediction_diagnostics(&mut self) {
8867        self.prediction_diagnostics.clear();
8868        self.reported_prediction_diagnostics.clear();
8869    }
8870
8871    /// Drops every per-parse cache that depends on ATN identity or pins
8872    /// recovery-symbol allocations.
8873    ///
8874    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
8875    /// same parser instance can legally be driven against different grammars
8876    /// in sequence. The four caches reset here are keyed by raw ATN
8877    /// coordinates (state numbers, rule indexes) and would silently hand back
8878    /// entries from a previous ATN if reused — pruning lookahead against the
8879    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
8880    /// for the rest of the process. Clearing them on every parse entry keeps
8881    /// the perf wins (caches still amortize within one parse) without making
8882    /// long-lived parsers leak memory or surface stale ATN data:
8883    ///
8884    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
8885    ///   functions of the ATN's state graph.
8886    /// * `state_expected_cache`, `state_expected_token_cache`,
8887    ///   `rule_stop_reach_cache`, and
8888    ///   `recovery_symbols_intern` together form
8889    ///   the identity invariant that lets `FastRecognizeKey` hash
8890    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
8891    ///   so a stale interned `Rc` cannot outlive its map entry.
8892    fn reset_per_parse_caches(&mut self) {
8893        self.rule_first_set_cache.clear();
8894        self.decision_lookahead_cache.clear();
8895        self.ll1_decision_cache.clear();
8896        self.empty_cycle_cache.clear();
8897        self.rule_stop_reach_cache.clear();
8898        self.single_outcome_memo_mode = SingleOutcomeMemoMode::Probe;
8899        self.single_outcome_probe_seen.clear();
8900        self.single_outcome_probe_samples = 0;
8901        self.single_outcome_probe_repeats = 0;
8902        self.recovery_symbols_intern.clear();
8903        self.state_expected_cache.clear();
8904        self.state_expected_token_cache.clear();
8905    }
8906
8907    /// Buffers ANTLR-style diagnostic-listener messages for decision states
8908    /// where multiple clean alternatives survive full-context recognition.
8909    fn record_prediction_diagnostics(
8910        &mut self,
8911        atn: &Atn,
8912        state: &AtnState,
8913        start_index: usize,
8914        outcomes: &[RecognizeOutcome],
8915    ) {
8916        if !self.report_diagnostic_errors || state.transitions.len() < 2 {
8917            return;
8918        }
8919        let Some(decision) = atn
8920            .decision_to_state()
8921            .iter()
8922            .position(|state_number| *state_number == state.state_number)
8923        else {
8924            return;
8925        };
8926        let Some(rule_index) = state.rule_index else {
8927            return;
8928        };
8929        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
8930        for outcome in outcomes
8931            .iter()
8932            .filter(|outcome| outcome.diagnostics.is_empty())
8933        {
8934            let Some(alt) = outcome.decisions.first() else {
8935                continue;
8936            };
8937            alts_by_end
8938                .entry(outcome.index)
8939                .or_default()
8940                .insert(alt + 1);
8941        }
8942        let Some((&end_index, ambig_alts)) = alts_by_end
8943            .iter()
8944            .filter(|(_, alts)| alts.len() > 1)
8945            .max_by_key(|(end, _)| *end)
8946        else {
8947            return;
8948        };
8949        let rule_name = self
8950            .rule_names()
8951            .get(rule_index)
8952            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
8953        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
8954        let input = display_input_text(&self.input.text(start_index, stop_index));
8955        let alts = ambig_alts
8956            .iter()
8957            .map(usize::to_string)
8958            .collect::<Vec<_>>()
8959            .join(", ");
8960        let key = (decision, start_index, format!("{alts}:{input}"));
8961        if !self.reported_prediction_diagnostics.insert(key) {
8962            return;
8963        }
8964        let start_token = self.token_at(start_index);
8965        let stop_token = self.token_at(stop_index);
8966        self.prediction_diagnostics.push(diagnostic_for_token(
8967            start_token.as_ref(),
8968            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
8969        ));
8970        self.prediction_diagnostics.push(diagnostic_for_token(
8971            stop_token.as_ref(),
8972            format!(
8973                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
8974            ),
8975        ));
8976    }
8977
8978    /// Formats the tokens expected from an ATN state using ANTLR display names.
8979    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
8980        expected_symbols_display(
8981            &state_expected_symbols(atn, state_number),
8982            self.vocabulary(),
8983        )
8984    }
8985
8986    /// Expected-token set at the parser's current ATN state — ANTLR's
8987    /// `getExpectedTokens()`. Generated recognizers expose this as
8988    /// `self.expected_tokens()` for embedded test actions
8989    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
8990    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
8991        let state = usize::try_from(self.data().state()).unwrap_or(0);
8992        ExpectedTokenSet {
8993            symbols: state_expected_symbols(atn, state),
8994        }
8995    }
8996
8997    /// Enables the bail error strategy: the first syntax error aborts the
8998    /// parse instead of recovering.
8999    pub const fn set_bail_on_error(&mut self, bail: bool) {
9000        self.bail_on_error = bail;
9001    }
9002
9003    /// Whether the bail error strategy is active.
9004    #[must_use]
9005    pub const fn bail_on_error(&self) -> bool {
9006        self.bail_on_error
9007    }
9008
9009    /// Names of the rules on the live invocation stack, current rule first —
9010    /// ANTLR's `getRuleInvocationStack()`.
9011    pub fn rule_invocation_stack(&self) -> Vec<String> {
9012        self.rule_context_stack
9013            .iter()
9014            .rev()
9015            .map(|frame| {
9016                self.data()
9017                    .rule_names()
9018                    .get(frame.rule_index)
9019                    .cloned()
9020                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
9021            })
9022            .collect()
9023    }
9024
9025    /// Formats a buffered token in ANTLR's diagnostic token display form.
9026    pub fn token_display_at(&mut self, index: usize) -> Option<String> {
9027        self.token_at(index).map(|token| format!("{token}"))
9028    }
9029
9030    /// Converts a recognized internal node into a public parse-tree node.
9031    fn recognized_node_tree(
9032        &mut self,
9033        node: &RecognizedNode,
9034        track_alt_numbers: bool,
9035    ) -> Result<ParseTree, AntlrError> {
9036        match node {
9037            RecognizedNode::Token { index } => {
9038                let token = self
9039                    .input
9040                    .get_ref(*index)
9041                    .ok_or_else(|| AntlrError::ParserError {
9042                        line: 0,
9043                        column: 0,
9044                        message: format!("missing token at index {index}"),
9045                    })?;
9046                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
9047            }
9048            RecognizedNode::ErrorToken { index } => {
9049                let token = self
9050                    .input
9051                    .get_ref(*index)
9052                    .ok_or_else(|| AntlrError::ParserError {
9053                        line: 0,
9054                        column: 0,
9055                        message: format!("missing error token at index {index}"),
9056                    })?;
9057                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
9058            }
9059            RecognizedNode::MissingToken {
9060                token_type,
9061                at_index,
9062                text,
9063            } => {
9064                let current = self.token_at(*at_index);
9065                let token = CommonToken::new(*token_type)
9066                    .with_text(text.as_str())
9067                    .with_span(usize::MAX, usize::MAX)
9068                    .with_position(
9069                        current.as_ref().map(Token::line).unwrap_or_default(),
9070                        current.as_ref().map(Token::column).unwrap_or_default(),
9071                    );
9072                Ok(ParseTree::Error(ErrorNode::new(token)))
9073            }
9074            RecognizedNode::Rule {
9075                rule_index,
9076                invoking_state,
9077                alt_number,
9078                start_index,
9079                stop_index,
9080                return_values,
9081                children,
9082            } => {
9083                let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
9084                if track_alt_numbers {
9085                    context.set_alt_number(*alt_number);
9086                }
9087                for (name, value) in return_values {
9088                    context.set_int_return(name.clone(), *value);
9089                }
9090                if let Some(token) = self.token_ref_at(*start_index) {
9091                    context.set_start_ref(token);
9092                }
9093                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
9094                    context.set_stop_ref(token);
9095                }
9096                for child in children {
9097                    context.add_child(self.recognized_node_tree(child, track_alt_numbers)?);
9098                }
9099                Ok(self.rule_node(context))
9100            }
9101            RecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
9102                format!("unfolded left-recursive boundary for rule {rule_index}"),
9103            )),
9104        }
9105    }
9106}
9107
9108impl<S, H> DirectAdaptiveParser<'_, '_, S, H>
9109where
9110    S: TokenSource,
9111    H: SemanticHooks,
9112{
9113    fn parse_rule(
9114        &mut self,
9115        rule_index: usize,
9116        invoking_state: isize,
9117        precedence: i32,
9118    ) -> DirectAdaptiveParseResult<ParseTree> {
9119        let start_state = *self.atn.rule_to_start_state().get(rule_index).ok_or(
9120            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
9121        )?;
9122        let stop_state = *self
9123            .atn
9124            .rule_to_stop_state()
9125            .get(rule_index)
9126            .filter(|state| **state != usize::MAX)
9127            .ok_or(DirectAdaptiveParseControl::Fallback(
9128                DirectAdaptiveFallback::MissingAtn,
9129            ))?;
9130        let start_index = self.parser.current_visible_index();
9131        let mut children = Vec::new();
9132        let mut state_number = start_state;
9133        let mut consumed_eof = false;
9134        while state_number != stop_state {
9135            self.step()?;
9136            let (transition, boundary) = self.next_transition(state_number, precedence)?;
9137            if boundary.is_some() {
9138                return Err(DirectAdaptiveParseControl::Fallback(
9139                    DirectAdaptiveFallback::LeftRecursiveBoundary,
9140                ));
9141            }
9142            match transition {
9143                Transition::Epsilon { target } => {
9144                    state_number = target;
9145                }
9146                Transition::Precedence {
9147                    target,
9148                    precedence: transition_precedence,
9149                } => {
9150                    if transition_precedence < precedence {
9151                        return Err(DirectAdaptiveParseControl::Fallback(
9152                            DirectAdaptiveFallback::Precedence,
9153                        ));
9154                    }
9155                    state_number = target;
9156                }
9157                Transition::Rule {
9158                    rule_index,
9159                    follow_state,
9160                    precedence: rule_precedence,
9161                    ..
9162                } => {
9163                    let child = self.parse_rule(
9164                        rule_index,
9165                        invoking_state_number(state_number),
9166                        rule_precedence,
9167                    )?;
9168                    if self.parser.build_parse_trees {
9169                        children.push(child);
9170                    }
9171                    state_number = follow_state;
9172                }
9173                Transition::Atom { .. }
9174                | Transition::Range { .. }
9175                | Transition::Set { .. }
9176                | Transition::NotSet { .. }
9177                | Transition::Wildcard { .. } => {
9178                    let (matched_eof, child) = self.consume_transition(&transition)?;
9179                    consumed_eof |= matched_eof;
9180                    if let Some(child) = child {
9181                        children.push(child);
9182                    }
9183                    state_number = transition.target();
9184                }
9185                Transition::Predicate { .. } => {
9186                    return Err(DirectAdaptiveParseControl::Fallback(
9187                        DirectAdaptiveFallback::Predicate,
9188                    ));
9189                }
9190                Transition::Action { .. } => {
9191                    return Err(DirectAdaptiveParseControl::Fallback(
9192                        DirectAdaptiveFallback::Action,
9193                    ));
9194                }
9195            }
9196        }
9197
9198        let mut context = ParserRuleContext::with_child_capacity(
9199            rule_index,
9200            invoking_state,
9201            if self.parser.build_parse_trees {
9202                children.len()
9203            } else {
9204                0
9205            },
9206        );
9207        if let Some(token) = self.parser.token_ref_at(start_index) {
9208            context.set_start_ref(token);
9209        }
9210        let stop_index = self
9211            .parser
9212            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
9213        if let Some(token) = stop_index.and_then(|index| self.parser.token_ref_at(index)) {
9214            context.set_stop_ref(token);
9215        }
9216        if self.parser.build_parse_trees {
9217            for child in children {
9218                context.add_child(child);
9219            }
9220        }
9221        Ok(self.parser.rule_node(context))
9222    }
9223
9224    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
9225        self.steps += 1;
9226        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
9227            return Err(DirectAdaptiveParseControl::Fallback(
9228                DirectAdaptiveFallback::StepLimit,
9229            ));
9230        }
9231        Ok(())
9232    }
9233
9234    fn next_transition(
9235        &mut self,
9236        state_number: usize,
9237        precedence: i32,
9238    ) -> DirectAdaptiveParseResult<(Transition, Option<usize>)> {
9239        let state = self
9240            .atn
9241            .state(state_number)
9242            .ok_or(DirectAdaptiveParseControl::Fallback(
9243                DirectAdaptiveFallback::MissingAtn,
9244            ))?;
9245        if state.is_rule_stop() {
9246            return Err(DirectAdaptiveParseControl::Fallback(
9247                DirectAdaptiveFallback::RuleStop,
9248            ));
9249        }
9250        let transition_index =
9251            self.transition_index(state_number, state.transitions.len(), precedence)?;
9252        let transition = state.transitions.get(transition_index).cloned().ok_or(
9253            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
9254        )?;
9255        let boundary = match &transition {
9256            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
9257                left_recursive_boundary(self.atn, state, *target)
9258            }
9259            _ => None,
9260        };
9261        Ok((transition, boundary))
9262    }
9263
9264    fn transition_index(
9265        &mut self,
9266        state_number: usize,
9267        transition_count: usize,
9268        precedence: i32,
9269    ) -> DirectAdaptiveParseResult<usize> {
9270        match transition_count {
9271            0 => Err(DirectAdaptiveParseControl::Fallback(
9272                DirectAdaptiveFallback::NoTransition,
9273            )),
9274            1 => Ok(0),
9275            _ => {
9276                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
9277                    return Ok(alt);
9278                }
9279                let decision = self
9280                    .decision_by_state
9281                    .get(state_number)
9282                    .and_then(|decision| *decision)
9283                    .ok_or(DirectAdaptiveParseControl::Fallback(
9284                        DirectAdaptiveFallback::UnknownDecision,
9285                    ))?;
9286                let prediction = self
9287                    .simulator
9288                    .adaptive_predict_stream_info_with_precedence(
9289                        decision,
9290                        direct_precedence(precedence),
9291                        &mut self.parser.input,
9292                    )
9293                    .map_err(|_| {
9294                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
9295                    })?;
9296                if prediction.has_semantic_context {
9297                    return Err(DirectAdaptiveParseControl::Fallback(
9298                        DirectAdaptiveFallback::SemanticContext,
9299                    ));
9300                }
9301                prediction
9302                    .alt
9303                    .checked_sub(1)
9304                    .filter(|index| *index < transition_count)
9305                    .ok_or(DirectAdaptiveParseControl::Fallback(
9306                        DirectAdaptiveFallback::InvalidAlt,
9307                    ))
9308            }
9309        }
9310    }
9311
9312    fn ll1_transition_index(
9313        &mut self,
9314        state_number: usize,
9315        transition_count: usize,
9316    ) -> DirectAdaptiveParseResult<Option<usize>> {
9317        let state = self
9318            .atn
9319            .state(state_number)
9320            .ok_or(DirectAdaptiveParseControl::Fallback(
9321                DirectAdaptiveFallback::MissingAtn,
9322            ))?;
9323        if state.precedence_rule_decision {
9324            return Ok(None);
9325        }
9326        let Some(rule_stop) = state
9327            .rule_index
9328            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index).copied())
9329        else {
9330            return Ok(None);
9331        };
9332        let symbol = self.parser.input.la_token(1);
9333        let entry = self
9334            .parser
9335            .cached_decision_lookahead(self.atn, state, rule_stop);
9336        Ok(ll1_greedy_alt(&entry, symbol, state.non_greedy).filter(|alt| *alt < transition_count))
9337    }
9338
9339    fn consume_transition(
9340        &mut self,
9341        transition: &Transition,
9342    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
9343        let symbol = self.parser.input.la_token(1);
9344        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
9345            return Err(DirectAdaptiveParseControl::Fallback(
9346                DirectAdaptiveFallback::TokenMismatch,
9347            ));
9348        }
9349        let token = self
9350            .parser
9351            .input
9352            .lt_ref(1)
9353            .ok_or(DirectAdaptiveParseControl::Fallback(
9354                DirectAdaptiveFallback::TokenMismatch,
9355            ))?;
9356        let matched_eof = symbol == TOKEN_EOF;
9357        if !matched_eof {
9358            self.parser.consume();
9359        }
9360        let child = self
9361            .parser
9362            .build_parse_trees
9363            .then(|| ParseTree::Terminal(TerminalNode::from_ref(token)));
9364        Ok((matched_eof, child))
9365    }
9366}
9367
9368/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
9369/// transformed left-recursive rule.
9370fn left_recursive_boundary(atn: &Atn, state: &AtnState, target: usize) -> Option<usize> {
9371    if !state.precedence_rule_decision {
9372        return None;
9373    }
9374    let target_state = atn.state(target)?;
9375    if target_state.kind == AtnStateKind::LoopEnd {
9376        return None;
9377    }
9378    state.rule_index
9379}
9380
9381/// Selects the first outer alternative observed for a rule path.
9382///
9383/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
9384/// outer decision. The metadata recognizer only needs this when a generated
9385/// grammar opts into that target template; otherwise the value remains `0` and
9386/// parse-tree rendering is unchanged.
9387const fn next_alt_number(
9388    state: &AtnState,
9389    transition_index: usize,
9390    current_alt_number: usize,
9391    track_alt_numbers: bool,
9392) -> usize {
9393    if !track_alt_numbers || current_alt_number != 0 || state.transitions.len() <= 1 {
9394        return current_alt_number;
9395    }
9396    if matches!(
9397        state.kind,
9398        AtnStateKind::Basic
9399            | AtnStateKind::BlockStart
9400            | AtnStateKind::PlusBlockStart
9401            | AtnStateKind::StarBlockStart
9402            | AtnStateKind::StarLoopEntry
9403    ) && !state.precedence_rule_decision
9404    {
9405        return transition_index + 1;
9406    }
9407    current_alt_number
9408}
9409
9410/// Folds boundary markers emitted at precedence-loop entries into nested rule
9411/// nodes, matching ANTLR's recursive-context parse-tree shape.
9412fn fold_left_recursive_boundaries(nodes: Vec<RecognizedNode>) -> Vec<RecognizedNode> {
9413    let mut folded = Vec::new();
9414    for node in nodes {
9415        match node {
9416            RecognizedNode::LeftRecursiveBoundary { rule_index } => {
9417                if !folded.is_empty() {
9418                    let children = std::mem::take(&mut folded);
9419                    let start_index = recognized_nodes_start_index(&children).unwrap_or_default();
9420                    let stop_index = recognized_nodes_stop_index(&children);
9421                    folded.push(RecognizedNode::Rule {
9422                        rule_index,
9423                        invoking_state: -1,
9424                        alt_number: 0,
9425                        start_index,
9426                        stop_index,
9427                        return_values: BTreeMap::new(),
9428                        children,
9429                    });
9430                }
9431            }
9432            node => folded.push(node),
9433        }
9434    }
9435    folded
9436}
9437
9438/// Mirrors [`fold_left_recursive_boundaries`] for [`FastRecognizedNode`].
9439fn fold_fast_left_recursive_boundaries(
9440    nodes: Vec<Rc<FastRecognizedNode>>,
9441) -> Vec<Rc<FastRecognizedNode>> {
9442    // Most rule invocations have no left-recursive boundaries, so skip the
9443    // fold work entirely when none are present. The boundary marker is only
9444    // emitted at precedence-rule loop entries, which are rare relative to
9445    // every rule call the recognizer fans out.
9446    if !nodes.iter().any(|node| {
9447        matches!(
9448            node.as_ref(),
9449            FastRecognizedNode::LeftRecursiveBoundary { .. }
9450        )
9451    }) {
9452        return nodes;
9453    }
9454    let mut folded: Vec<Rc<FastRecognizedNode>> = Vec::with_capacity(nodes.len());
9455    for node in nodes {
9456        match node.as_ref() {
9457            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
9458                if !folded.is_empty() {
9459                    let children = std::mem::take(&mut folded);
9460                    let start_index =
9461                        fast_recognized_nodes_start_index(&children).unwrap_or_default();
9462                    let stop_index = fast_recognized_nodes_stop_index(&children);
9463                    folded.push(Rc::new(FastRecognizedNode::Rule {
9464                        rule_index: *rule_index,
9465                        invoking_state: -1,
9466                        start_index,
9467                        stop_index,
9468                        children: NodeList::from_vec(children),
9469                    }));
9470                }
9471            }
9472            _ => folded.push(node),
9473        }
9474    }
9475    folded
9476}
9477
9478fn fast_node_has_left_recursive_boundary(node: &FastRecognizedNode) -> bool {
9479    match node {
9480        FastRecognizedNode::LeftRecursiveBoundary { .. } => true,
9481        FastRecognizedNode::Rule { children, .. } => children.has_left_recursive_boundary(),
9482        FastRecognizedNode::Token { .. }
9483        | FastRecognizedNode::ErrorToken { .. }
9484        | FastRecognizedNode::MissingToken { .. } => false,
9485    }
9486}
9487
9488fn fast_recognized_nodes_start_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
9489    nodes
9490        .iter()
9491        .find_map(|node| fast_recognized_node_start_index(node.as_ref()))
9492}
9493
9494const fn fast_recognized_node_start_index(node: &FastRecognizedNode) -> Option<usize> {
9495    match node {
9496        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9497            Some(*index)
9498        }
9499        FastRecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
9500        FastRecognizedNode::Rule { start_index, .. } => Some(*start_index),
9501        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9502    }
9503}
9504
9505const fn fast_recognized_node_span(node: &FastRecognizedNode) -> Option<(usize, Option<usize>)> {
9506    match node {
9507        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9508            Some((*index, Some(*index)))
9509        }
9510        FastRecognizedNode::MissingToken { at_index, .. } => Some((*at_index, None)),
9511        FastRecognizedNode::Rule {
9512            start_index,
9513            stop_index,
9514            ..
9515        } => Some((*start_index, *stop_index)),
9516        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9517    }
9518}
9519
9520fn fast_recognized_nodes_stop_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
9521    nodes
9522        .iter()
9523        .rev()
9524        .find_map(|node| fast_recognized_node_stop_index(node.as_ref()))
9525}
9526
9527const fn fast_recognized_node_stop_index(node: &FastRecognizedNode) -> Option<usize> {
9528    match node {
9529        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9530            Some(*index)
9531        }
9532        FastRecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
9533        FastRecognizedNode::Rule { stop_index, .. } => *stop_index,
9534        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9535    }
9536}
9537
9538fn recognized_nodes_start_index(nodes: &[RecognizedNode]) -> Option<usize> {
9539    nodes.iter().find_map(recognized_node_start_index)
9540}
9541
9542const fn recognized_node_start_index(node: &RecognizedNode) -> Option<usize> {
9543    match node {
9544        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
9545        RecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
9546        RecognizedNode::Rule { start_index, .. } => Some(*start_index),
9547        RecognizedNode::LeftRecursiveBoundary { .. } => None,
9548    }
9549}
9550
9551fn recognized_nodes_stop_index(nodes: &[RecognizedNode]) -> Option<usize> {
9552    nodes.iter().rev().find_map(recognized_node_stop_index)
9553}
9554
9555/// Converts an ATN state number into the signed invoking-state slot used by
9556/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
9557fn invoking_state_number(state_number: usize) -> isize {
9558    isize::try_from(state_number).unwrap_or(isize::MAX)
9559}
9560
9561fn direct_precedence(precedence: i32) -> usize {
9562    usize::try_from(precedence.max(0)).unwrap_or_default()
9563}
9564
9565const fn recognized_node_stop_index(node: &RecognizedNode) -> Option<usize> {
9566    match node {
9567        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
9568        RecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
9569        RecognizedNode::Rule { stop_index, .. } => *stop_index,
9570        RecognizedNode::LeftRecursiveBoundary { .. } => None,
9571    }
9572}
9573
9574fn token_input_display(token: &impl Token) -> String {
9575    format!("'{}'", token.text().unwrap_or("<EOF>"))
9576}
9577
9578fn display_input_text(text: &str) -> String {
9579    let mut out = String::new();
9580    for ch in text.chars() {
9581        match ch {
9582            '\n' => out.push_str("\\n"),
9583            '\r' => out.push_str("\\r"),
9584            '\t' => out.push_str("\\t"),
9585            other => out.push(other),
9586        }
9587    }
9588    out
9589}
9590
9591fn diagnostic_for_token(token: Option<&impl Token>, message: String) -> ParserDiagnostic {
9592    ParserDiagnostic {
9593        line: token.map(Token::line).unwrap_or_default(),
9594        column: token.map(Token::column).unwrap_or_default(),
9595        message,
9596    }
9597}
9598
9599/// Emits parser diagnostics for the selected recovered parse path.
9600#[allow(clippy::print_stderr)]
9601fn report_parser_diagnostics(diagnostics: &[ParserDiagnostic]) {
9602    for diagnostic in diagnostics {
9603        eprintln!(
9604            "line {}:{} {}",
9605            diagnostic.line, diagnostic.column, diagnostic.message
9606        );
9607    }
9608}
9609
9610/// Emits generated parser diagnostics and lexer diagnostics in the same
9611/// source-position order as ANTLR's lazy token stream reports them.
9612#[allow(clippy::print_stderr)]
9613fn report_generated_diagnostics(
9614    parser_diagnostics: &[ParserDiagnostic],
9615    token_errors: &[TokenSourceError],
9616) {
9617    // Parser diagnostics keep their event order: Java's console and
9618    // DiagnosticErrorListener print reports as prediction produces them, so
9619    // `reportAttemptingFullContext` precedes `reportContextSensitivity` even
9620    // though the latter's position is earlier. Buffered token-source errors
9621    // interleave by source position — ANTLR's lazy token stream surfaces a
9622    // lexer error when the parser first fetches that token — and win ties.
9623    let mut token_iter = token_errors.iter().peekable();
9624    for diagnostic in parser_diagnostics {
9625        while let Some(error) = token_iter.peek() {
9626            if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
9627                eprintln!("line {}:{} {}", error.line, error.column, error.message);
9628                token_iter.next();
9629            } else {
9630                break;
9631            }
9632        }
9633        eprintln!(
9634            "line {}:{} {}",
9635            diagnostic.line, diagnostic.column, diagnostic.message
9636        );
9637    }
9638    for error in token_iter {
9639        eprintln!("line {}:{} {}", error.line, error.column, error.message);
9640    }
9641}
9642
9643/// Emits buffered token-source diagnostics after parser diagnostics that were
9644/// discovered while speculatively reading the same token stream.
9645#[allow(clippy::print_stderr)]
9646fn report_token_source_errors(errors: &[TokenSourceError]) {
9647    for error in errors {
9648        eprintln!("line {}:{} {}", error.line, error.column, error.message);
9649    }
9650}
9651
9652fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
9653    let items = symbols
9654        .iter()
9655        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
9656        .collect::<Vec<_>>();
9657    if let [single] = items.as_slice() {
9658        return single.clone();
9659    }
9660    format!("{{{}}}", items.join(", "))
9661}
9662
9663fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
9664    if symbol == TOKEN_EOF {
9665        return "<EOF>".to_owned();
9666    }
9667    vocabulary.display_name(symbol)
9668}
9669
9670fn is_caller_follow_boundary_text(text: &str) -> bool {
9671    text.chars().any(|ch| ch == ';' || ch == '\n')
9672        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
9673}
9674
9675fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
9676    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
9677}
9678
9679/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
9680/// Inline insertion in those precedence loops can synthesize a missing operand
9681/// before an operator and then block the legitimate loop-exit path.
9682fn state_is_left_recursive_rule(atn: &Atn, state: &AtnState) -> bool {
9683    let Some(rule_index) = state.rule_index else {
9684        return false;
9685    };
9686    atn.rule_to_start_state()
9687        .get(rule_index)
9688        .and_then(|state_number| atn.state(*state_number))
9689        .is_some_and(|rule_start| rule_start.left_recursive_rule)
9690}
9691
9692/// Picks the better of two `parse_atn_rule` passes (with and without the
9693/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
9694/// recovered one; among recovered outcomes the second pass is preferred
9695/// because the no-prefilter walk reaches ANTLR-style recovery inside child
9696/// rules. If both passes failed, the second pass's expected-token snapshot
9697/// is returned so the caller renders the same diagnostic ANTLR would.
9698fn select_better_top_outcome(
9699    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
9700    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
9701) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
9702    match (first, second) {
9703        (Ok(first), Ok(second)) => {
9704            if first.0.diagnostics.is_empty() {
9705                Ok(first)
9706            } else {
9707                Ok(second)
9708            }
9709        }
9710        (Ok(first), Err(_)) => Ok(first),
9711        (Err(_), Ok(second)) => Ok(second),
9712        (Err(_), Err(second_expected)) => Err(second_expected),
9713    }
9714}
9715
9716/// Chooses the outermost parse result that consumed the most input.
9717///
9718/// The recognizer intentionally keeps shorter endpoints available while walking
9719/// nested rule transitions so callers can satisfy following tokens such as
9720/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
9721fn select_best_fast_outcome(
9722    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
9723    prediction_mode: PredictionMode,
9724    caller_follow: Option<&TokenBitSet>,
9725    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
9726) -> Option<FastRecognizeOutcome> {
9727    let mut best = None;
9728    let mut best_caller_follow = None;
9729    for outcome in outcomes {
9730        if matches!(
9731            prediction_mode,
9732            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9733        ) && outcome.diagnostics.is_empty()
9734            && let Some(follow) = caller_follow
9735        {
9736            let (token_type, is_boundary, _) = token_info_at(outcome.index);
9737            if is_boundary && follow.contains(token_type) {
9738                let replace =
9739                    best_caller_follow
9740                        .as_ref()
9741                        .is_none_or(|existing: &FastRecognizeOutcome| {
9742                            (outcome.index, outcome.consumed_eof)
9743                                < (existing.index, existing.consumed_eof)
9744                        });
9745                if replace {
9746                    best_caller_follow = Some(outcome.clone());
9747                }
9748            }
9749        }
9750        let Some(existing) = best else {
9751            best = Some(outcome);
9752            continue;
9753        };
9754        let outcome_position = (outcome.index, outcome.consumed_eof);
9755        let best_position = (existing.index, existing.consumed_eof);
9756        let better = match prediction_mode {
9757            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
9758                outcome_position,
9759                &outcome.diagnostics,
9760                best_position,
9761                &existing.diagnostics,
9762            ),
9763            PredictionMode::Sll => outcome.index > existing.index,
9764        };
9765        best = Some(if better { outcome } else { existing });
9766    }
9767    let should_use_caller_follow =
9768        best_caller_follow
9769            .as_ref()
9770            .zip(best.as_ref())
9771            .is_some_and(|(candidate, selected)| {
9772                if !selected.diagnostics.is_empty() {
9773                    return true;
9774                }
9775                candidate.index < selected.index
9776                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
9777            });
9778    if should_use_caller_follow {
9779        best_caller_follow
9780    } else {
9781        best
9782    }
9783}
9784
9785fn select_best_outcome(
9786    outcomes: impl Iterator<Item = RecognizeOutcome>,
9787    prediction_mode: PredictionMode,
9788) -> Option<RecognizeOutcome> {
9789    let outcomes = outcomes.collect::<Vec<_>>();
9790    let prefer_first_tie = outcomes
9791        .iter()
9792        .any(|outcome| nodes_need_stable_tie(&outcome.nodes));
9793    outcomes.into_iter().reduce(|best, outcome| {
9794        let outcome_position = (outcome.index, outcome.consumed_eof);
9795        let best_position = (best.index, best.consumed_eof);
9796        let better = match prediction_mode {
9797            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
9798                outcome_is_better(
9799                    outcome_position,
9800                    &outcome.diagnostics,
9801                    best_position,
9802                    &best.diagnostics,
9803                ) || (!prefer_first_tie
9804                    && outcome_position == best_position
9805                    && outcome.diagnostics.len() == best.diagnostics.len()
9806                    && diagnostic_recovery_rank(&outcome.diagnostics)
9807                        == diagnostic_recovery_rank(&best.diagnostics)
9808                    && (outcome.decisions < best.decisions
9809                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
9810            }
9811            PredictionMode::Sll => {
9812                outcome_position > best_position
9813                    || (outcome_position == best_position
9814                        && !prefer_first_tie
9815                        && (outcome.decisions < best.decisions
9816                            || (outcome.decisions == best.decisions
9817                                && outcome_is_better(
9818                                    outcome_position,
9819                                    &outcome.diagnostics,
9820                                    best_position,
9821                                    &best.diagnostics,
9822                                ))))
9823            }
9824        };
9825        if better {
9826            return outcome;
9827        }
9828        best
9829    })
9830}
9831
9832/// Records the serialized transition order at parser decision states.
9833///
9834/// When two clean paths consume the same input, ANTLR's adaptive prediction
9835/// chooses by alternative order. Keeping this compact trace lets the metadata
9836/// recognizer distinguish greedy and non-greedy optional blocks without a full
9837/// prediction simulator.
9838fn transition_decision(
9839    atn: &Atn,
9840    state: &AtnState,
9841    transition_index: usize,
9842    predicates: &[(usize, usize, ParserPredicate)],
9843) -> Option<usize> {
9844    if state.transitions.len() <= 1
9845        || state.precedence_rule_decision
9846        || decision_reaches_unsupported_predicate(atn, state, predicates)
9847    {
9848        return None;
9849    }
9850    Some(transition_index)
9851}
9852
9853/// Reports whether a state should reset the active no-viable decision start.
9854///
9855/// Loop entry/back states are continuations of the surrounding adaptive
9856/// prediction; resetting at those states would turn LL-star failures back into
9857/// ordinary mismatches.
9858const fn starts_prediction_decision(state: &AtnState) -> bool {
9859    state.transitions.len() > 1
9860        && !matches!(
9861            state.kind,
9862            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
9863        )
9864}
9865
9866/// Marks a farthest expected-token set as no-viable when multiple alternatives
9867/// failed after the active decision had already consumed input.
9868fn record_no_viable_if_ambiguous(
9869    expected: &mut ExpectedTokens,
9870    decision_start_index: Option<usize>,
9871    index: usize,
9872) {
9873    if expected.index == Some(index) && expected.symbols.len() > 1 {
9874        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
9875            expected.record_no_viable(decision_start, index);
9876        }
9877    }
9878}
9879
9880/// Records a no-viable decision caused by a failed semantic predicate before
9881/// any consuming transition can contribute an expected-token set.
9882const fn record_predicate_no_viable(
9883    expected: &mut ExpectedTokens,
9884    decision_start_index: Option<usize>,
9885    index: usize,
9886) {
9887    if let Some(decision_start) = decision_start_index {
9888        expected.record_no_viable(decision_start, index);
9889    }
9890}
9891
9892/// Returns the active decision start only when the error is past that start.
9893const fn no_viable_decision_start(
9894    decision_start_index: Option<usize>,
9895    index: usize,
9896) -> Option<usize> {
9897    match decision_start_index {
9898        Some(start) if index > start => Some(start),
9899        _ => None,
9900    }
9901}
9902
9903/// Restores expected-token bookkeeping when a child rule found a clean
9904/// consuming path; failures in longer child alternatives should not pollute the
9905/// caller's final expectation set.
9906fn restore_expected(
9907    children: &[RecognizeOutcome],
9908    child_start_index: usize,
9909    expected: &mut ExpectedTokens,
9910    snapshot: ExpectedTokens,
9911    preserve_child_expected: bool,
9912) {
9913    if preserve_child_expected {
9914        return;
9915    }
9916    if children
9917        .iter()
9918        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
9919    {
9920        *expected = snapshot;
9921    }
9922}
9923
9924/// Reports whether a decision can reach a predicate the generator did not
9925/// translate. Static alternative order is unsafe for those context predicates.
9926fn decision_reaches_unsupported_predicate(
9927    atn: &Atn,
9928    state: &AtnState,
9929    predicates: &[(usize, usize, ParserPredicate)],
9930) -> bool {
9931    state.transitions.iter().any(|transition| {
9932        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
9933    })
9934}
9935
9936/// Walks epsilon-like edges from one transition to find unsupported predicates.
9937fn transition_reaches_unsupported_predicate(
9938    atn: &Atn,
9939    transition: &Transition,
9940    predicates: &[(usize, usize, ParserPredicate)],
9941    visited: &mut BTreeSet<usize>,
9942) -> bool {
9943    match transition {
9944        Transition::Predicate {
9945            rule_index,
9946            pred_index,
9947            ..
9948        } => !predicates
9949            .iter()
9950            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
9951        Transition::Epsilon { target }
9952        | Transition::Action { target, .. }
9953        | Transition::Rule { target, .. } => {
9954            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
9955        }
9956        Transition::Precedence { .. }
9957        | Transition::Atom { .. }
9958        | Transition::Range { .. }
9959        | Transition::Set { .. }
9960        | Transition::NotSet { .. }
9961        | Transition::Wildcard { .. } => false,
9962    }
9963}
9964
9965/// Finds an unsupported predicate reachable before a consuming transition.
9966fn state_reaches_unsupported_predicate(
9967    atn: &Atn,
9968    state_number: usize,
9969    predicates: &[(usize, usize, ParserPredicate)],
9970    visited: &mut BTreeSet<usize>,
9971) -> bool {
9972    if !visited.insert(state_number) {
9973        return false;
9974    }
9975    let Some(state) = atn.state(state_number) else {
9976        return false;
9977    };
9978    state.transitions.iter().any(|transition| {
9979        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
9980    })
9981}
9982
9983/// Adds a decision step to the front of an already-recognized suffix path.
9984fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
9985    if let Some(decision) = decision {
9986        outcome.decisions.insert(0, decision);
9987    }
9988}
9989
9990fn outcome_is_better(
9991    outcome_position: (usize, bool),
9992    outcome_diagnostics: &[ParserDiagnostic],
9993    best_position: (usize, bool),
9994    best_diagnostics: &[ParserDiagnostic],
9995) -> bool {
9996    outcome_position > best_position
9997        || (outcome_position == best_position
9998            && (outcome_diagnostics.len() < best_diagnostics.len()
9999                || (outcome_diagnostics.len() == best_diagnostics.len()
10000                    && diagnostic_recovery_rank(outcome_diagnostics)
10001                        < diagnostic_recovery_rank(best_diagnostics))))
10002}
10003
10004/// Ranks concrete recovery repairs ahead of generic non-EOF mismatch fallbacks
10005/// when speculative paths otherwise consume the same input.
10006fn diagnostic_recovery_rank(diagnostics: &[ParserDiagnostic]) -> usize {
10007    diagnostics
10008        .iter()
10009        .filter(|diagnostic| {
10010            diagnostic.message.starts_with("mismatched input ")
10011                && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
10012        })
10013        .count()
10014}
10015
10016fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
10017    if outcomes
10018        .iter()
10019        .any(|outcome| outcome.diagnostics.is_empty())
10020    {
10021        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
10022    }
10023}
10024
10025fn discard_recovered_outcomes_if_clean_path_exists(outcomes: &mut Vec<RecognizeOutcome>) {
10026    if outcomes.iter().any(outcome_has_rule_failure_diagnostic) {
10027        return;
10028    }
10029    if outcomes
10030        .iter()
10031        .any(|outcome| outcome.diagnostics.is_empty())
10032    {
10033        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
10034    }
10035}
10036
10037/// Reports whether a recovered outcome came from an explicit predicate
10038/// fail-option and therefore should compete with shorter clean loop exits.
10039fn outcome_has_rule_failure_diagnostic(outcome: &RecognizeOutcome) -> bool {
10040    outcome
10041        .diagnostics
10042        .iter()
10043        .any(|diagnostic| diagnostic.message.starts_with("rule "))
10044}
10045
10046/// Reports whether a candidate contains recursive tree structure where ANTLR's
10047/// first viable candidate preserves the correct left-recursive context shape.
10048fn nodes_need_stable_tie(nodes: &[RecognizedNode]) -> bool {
10049    nodes.iter().any(node_needs_stable_tie)
10050}
10051
10052fn node_needs_stable_tie(node: &RecognizedNode) -> bool {
10053    match node {
10054        RecognizedNode::Token { .. }
10055        | RecognizedNode::ErrorToken { .. }
10056        | RecognizedNode::MissingToken { .. } => false,
10057        RecognizedNode::LeftRecursiveBoundary { .. } => true,
10058        RecognizedNode::Rule {
10059            rule_index,
10060            children,
10061            ..
10062        } => children.iter().any(|child| {
10063            matches!(
10064                child,
10065                RecognizedNode::Rule {
10066                    rule_index: child_rule,
10067                    ..
10068                } if child_rule == rule_index
10069            ) || node_needs_stable_tie(child)
10070        }),
10071    }
10072}
10073
10074/// Removes equivalent endpoints before memoizing a state result while
10075/// preserving ATN transition-discovery order.
10076///
10077/// Outcomes are compared on observable recognition state — the input index,
10078/// EOF consumption, and diagnostics — without descending into the parse-tree
10079/// fragment carried by `nodes`. Two paths reaching the same point with
10080/// different node trees would otherwise prevent memoization from collapsing
10081/// equivalent suffixes and explode the speculative-path cache.
10082///
10083/// The first occurrence per recognition key wins, which matches ANTLR's
10084/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
10085/// transitions before loop-exit, so the first-discovered outcome carries the
10086/// greedy parse-tree fragment.
10087fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
10088    if outcomes.len() < 2 {
10089        return;
10090    }
10091    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
10092    outcomes.retain(|outcome| {
10093        seen.insert((
10094            outcome.index,
10095            outcome.consumed_eof,
10096            outcome.diagnostics.len(),
10097            diagnostic_recovery_rank(&outcome.diagnostics),
10098        ))
10099    });
10100}
10101
10102fn dedupe_clean_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
10103    if outcomes.len() < 2 {
10104        return;
10105    }
10106    // Most outcomes lists are 2-4 entries; an inline scan beats BTreeSet
10107    // here because BTreeSet's allocation + per-insert balancing dominates
10108    // O(log n) wins on tiny n. Retains the original order so callers that
10109    // depend on alt ordering (e.g. fast outcome selection) stay correct.
10110    //
10111    // Beyond the inline buffer we promote to a heap Vec so all kept entries
10112    // continue to participate in dedup — leaking duplicates here on
10113    // pathological grammars (e.g. ktor's deeply ambiguous Kotlin parse)
10114    // explodes the speculative cache one step up the recursion.
10115    let mut inline_keys: [(usize, bool); 8] = [(0, false); 8];
10116    let mut inline_len = 0_usize;
10117    let mut overflow: Vec<(usize, bool)> = Vec::new();
10118    outcomes.retain(|outcome| {
10119        let key = (outcome.index, outcome.consumed_eof);
10120        for &existing in &inline_keys[..inline_len] {
10121            if existing == key {
10122                return false;
10123            }
10124        }
10125        if !overflow.is_empty() {
10126            for &existing in &overflow {
10127                if existing == key {
10128                    return false;
10129                }
10130            }
10131        }
10132        if inline_len < inline_keys.len() {
10133            inline_keys[inline_len] = key;
10134            inline_len += 1;
10135        } else {
10136            overflow.push(key);
10137        }
10138        true
10139    });
10140}
10141
10142/// Sorts and removes equivalent endpoints, including their action traces.
10143fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>) {
10144    outcomes.sort_unstable();
10145    outcomes.dedup();
10146}
10147
10148impl<S, H> Recognizer for BaseParser<S, H>
10149where
10150    S: TokenSource,
10151    H: SemanticHooks,
10152{
10153    fn data(&self) -> &RecognizerData {
10154        &self.data
10155    }
10156
10157    fn data_mut(&mut self) -> &mut RecognizerData {
10158        &mut self.data
10159    }
10160}
10161
10162impl<S, H> Parser for BaseParser<S, H>
10163where
10164    S: TokenSource,
10165    H: SemanticHooks,
10166{
10167    fn build_parse_trees(&self) -> bool {
10168        self.build_parse_trees
10169    }
10170
10171    fn set_build_parse_trees(&mut self, build: bool) {
10172        self.build_parse_trees = build;
10173    }
10174
10175    fn number_of_syntax_errors(&self) -> usize {
10176        Self::number_of_syntax_errors(self)
10177    }
10178
10179    fn report_diagnostic_errors(&self) -> bool {
10180        self.report_diagnostic_errors
10181    }
10182
10183    fn set_report_diagnostic_errors(&mut self, report: bool) {
10184        self.report_diagnostic_errors = report;
10185    }
10186
10187    fn prediction_mode(&self) -> PredictionMode {
10188        self.prediction_mode
10189    }
10190
10191    fn set_prediction_mode(&mut self, mode: PredictionMode) {
10192        self.prediction_mode = mode;
10193    }
10194}
10195
10196#[cfg(test)]
10197mod tests {
10198    use super::*;
10199    use crate::atn::AtnType;
10200    use crate::atn::IntervalSet;
10201    use crate::atn::parser::{
10202        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
10203    };
10204    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
10205    use crate::token::{CommonToken, HIDDEN_CHANNEL, Token};
10206    use crate::token_stream::CommonTokenStream;
10207    use crate::vocabulary::Vocabulary;
10208
10209    #[test]
10210    fn fx_hasher_write_matches_typed_methods_for_full_words() {
10211        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
10212        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
10213        // fields) must hash the same as the typed methods, otherwise an
10214        // `FxHashMap` keyed on such a type silently disagrees with itself
10215        // depending on which entry point the caller used. Verify the
10216        // little-endian word equivalence this PR established.
10217        let value: u64 = 0x0102_0304_0506_0708;
10218        let mut typed = FxHasher::default();
10219        typed.write_u64(value);
10220        let mut bytewise = FxHasher::default();
10221        bytewise.write(&value.to_le_bytes());
10222        assert_eq!(typed.finish(), bytewise.finish());
10223    }
10224
10225    #[derive(Debug)]
10226    struct Source {
10227        tokens: Vec<CommonToken>,
10228        index: usize,
10229    }
10230
10231    impl TokenSource for Source {
10232        fn next_token(&mut self) -> CommonToken {
10233            let token = self
10234                .tokens
10235                .get(self.index)
10236                .cloned()
10237                .unwrap_or_else(|| CommonToken::eof("parser-test", self.index, 1, self.index));
10238            self.index += 1;
10239            token
10240        }
10241
10242        fn line(&self) -> usize {
10243            1
10244        }
10245
10246        fn column(&self) -> usize {
10247            self.index
10248        }
10249
10250        fn source_name(&self) -> &'static str {
10251            "parser-test"
10252        }
10253    }
10254
10255    fn mini_parser_data() -> RecognizerData {
10256        RecognizerData::new(
10257            "Mini.g4",
10258            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10259        )
10260        .with_rule_names(["s"])
10261    }
10262
10263    fn mini_parser(tokens: Vec<CommonToken>) -> BaseParser<Source> {
10264        let data = mini_parser_data();
10265        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
10266    }
10267
10268    fn mini_parser_with_hooks<H>(tokens: Vec<CommonToken>, hooks: H) -> BaseParser<Source, H>
10269    where
10270        H: SemanticHooks,
10271    {
10272        BaseParser::with_semantic_hooks(
10273            CommonTokenStream::new(Source { tokens, index: 0 }),
10274            mini_parser_data(),
10275            hooks,
10276        )
10277    }
10278
10279    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
10280        let mut atn = Atn::new(AtnType::Parser, 2);
10281        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10282        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
10283        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10284        let mut callee_start = AtnState::new(3, AtnStateKind::RuleStart).with_rule_index(1);
10285        callee_start.left_recursive_rule = true;
10286        atn.add_state(callee_start);
10287        let mut loop_entry = AtnState::new(4, AtnStateKind::StarLoopEntry).with_rule_index(1);
10288        loop_entry.precedence_rule_decision = true;
10289        atn.add_state(loop_entry);
10290        atn.add_state(AtnState::new(5, AtnStateKind::Basic).with_rule_index(1));
10291        atn.add_state(AtnState::new(6, AtnStateKind::Basic).with_rule_index(1));
10292        atn.add_state(AtnState::new(7, AtnStateKind::LoopEnd).with_rule_index(1));
10293        atn.add_state(AtnState::new(8, AtnStateKind::RuleStop).with_rule_index(1));
10294        atn.add_state(AtnState::new(9, AtnStateKind::RuleStop).with_rule_index(0));
10295        atn.set_rule_to_start_state(vec![0, 3]);
10296        atn.set_rule_to_stop_state(vec![9, 8]);
10297        atn.state_mut(1)
10298            .expect("caller invoking state")
10299            .add_transition(Transition::Rule {
10300                target: 3,
10301                rule_index: 1,
10302                follow_state: 2,
10303                precedence: 0,
10304            });
10305        atn.state_mut(2)
10306            .expect("caller follow")
10307            .add_transition(Transition::Atom {
10308                target: 9,
10309                label: caller_symbol,
10310            });
10311        atn.state_mut(4)
10312            .expect("precedence loop")
10313            .add_transition(Transition::Epsilon { target: 5 });
10314        atn.state_mut(4)
10315            .expect("precedence loop")
10316            .add_transition(Transition::Epsilon { target: 7 });
10317        atn.state_mut(5)
10318            .expect("precedence predicate")
10319            .add_transition(Transition::Precedence {
10320                target: 6,
10321                precedence: 1,
10322            });
10323        atn.state_mut(6)
10324            .expect("operator token")
10325            .add_transition(Transition::Atom {
10326                target: 4,
10327                label: 1,
10328            });
10329        atn.state_mut(7)
10330            .expect("loop exit")
10331            .add_transition(Transition::Epsilon { target: 8 });
10332        atn
10333    }
10334
10335    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
10336        let mut parser = mini_parser(vec![
10337            CommonToken::new(symbol).with_text("lookahead"),
10338            CommonToken::eof("parser-test", 1, 1, 1),
10339        ]);
10340        parser.rule_context_stack = vec![
10341            RuleContextFrame {
10342                rule_index: 0,
10343                invoking_state: -1,
10344            },
10345            RuleContextFrame {
10346                rule_index: 1,
10347                invoking_state: 1,
10348            },
10349        ];
10350        parser
10351    }
10352
10353    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
10354        let mut atn = Atn::new(AtnType::Parser, 2);
10355        for (state, kind, rule) in [
10356            (0, AtnStateKind::RuleStart, 0),
10357            (1, AtnStateKind::StarLoopEntry, 0),
10358            (2, AtnStateKind::Basic, 0),
10359            (3, AtnStateKind::Basic, 0),
10360            (4, AtnStateKind::Basic, 0),
10361            (5, AtnStateKind::LoopEnd, 0),
10362            (6, AtnStateKind::RuleStop, 0),
10363            (7, AtnStateKind::RuleStart, 1),
10364            (8, AtnStateKind::RuleStop, 1),
10365            (9, AtnStateKind::Basic, 1),
10366        ] {
10367            let mut atn_state = AtnState::new(state, kind).with_rule_index(rule);
10368            if state == 0 {
10369                atn_state.left_recursive_rule = true;
10370            } else if state == 1 {
10371                atn_state.precedence_rule_decision = true;
10372            }
10373            atn.add_state(atn_state);
10374        }
10375        atn.set_rule_to_start_state(vec![0, 7]);
10376        atn.set_rule_to_stop_state(vec![6, 8]);
10377        atn.state_mut(1)
10378            .expect("precedence loop")
10379            .add_transition(Transition::Epsilon { target: 2 });
10380        atn.state_mut(1)
10381            .expect("precedence loop")
10382            .add_transition(Transition::Epsilon { target: 5 });
10383        atn.state_mut(2)
10384            .expect("precedence predicate")
10385            .add_transition(Transition::Precedence {
10386                target: 3,
10387                precedence: 3,
10388            });
10389        atn.state_mut(3)
10390            .expect("nullable operator prefix")
10391            .add_transition(Transition::Rule {
10392                target: 7,
10393                rule_index: 1,
10394                follow_state: 4,
10395                precedence: 0,
10396            });
10397        atn.state_mut(4)
10398            .expect("operator token")
10399            .add_transition(Transition::Atom {
10400                target: 1,
10401                label: 1,
10402            });
10403        atn.state_mut(5)
10404            .expect("loop exit")
10405            .add_transition(Transition::Epsilon { target: 6 });
10406        atn.state_mut(7)
10407            .expect("nullable operator prefix")
10408            .add_transition(Transition::Precedence {
10409                target: 9,
10410                precedence: 1,
10411            });
10412        atn.state_mut(9)
10413            .expect("nullable operator prefix")
10414            .add_transition(Transition::Epsilon { target: 8 });
10415        atn
10416    }
10417
10418    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
10419        let mut atn = Atn::new(AtnType::Parser, 2);
10420        for (state, kind) in [
10421            (0, AtnStateKind::RuleStart),
10422            (1, AtnStateKind::StarLoopEntry),
10423            (2, AtnStateKind::Basic),
10424            (3, AtnStateKind::Basic),
10425            (4, AtnStateKind::Basic),
10426            (5, AtnStateKind::LoopEnd),
10427            (6, AtnStateKind::RuleStop),
10428        ] {
10429            let mut atn_state = AtnState::new(state, kind).with_rule_index(0);
10430            if state == 0 {
10431                atn_state.left_recursive_rule = true;
10432            } else if state == 1 {
10433                atn_state.precedence_rule_decision = true;
10434            }
10435            atn.add_state(atn_state);
10436        }
10437        atn.set_rule_to_start_state(vec![0]);
10438        atn.set_rule_to_stop_state(vec![6]);
10439        atn.state_mut(1)
10440            .expect("precedence loop")
10441            .add_transition(Transition::Epsilon { target: 2 });
10442        atn.state_mut(1)
10443            .expect("precedence loop")
10444            .add_transition(Transition::Epsilon { target: 5 });
10445        atn.state_mut(2)
10446            .expect("precedence predicate")
10447            .add_transition(Transition::Precedence {
10448                target: 3,
10449                precedence: 1,
10450            });
10451        atn.state_mut(3)
10452            .expect("semantic predicate")
10453            .add_transition(Transition::Predicate {
10454                target: 4,
10455                rule_index: 0,
10456                pred_index: 0,
10457                context_dependent: false,
10458            });
10459        atn.state_mut(4)
10460            .expect("operator token")
10461            .add_transition(Transition::Atom {
10462                target: 1,
10463                label: 1,
10464            });
10465        atn.state_mut(5)
10466            .expect("loop exit")
10467            .add_transition(Transition::Epsilon { target: 6 });
10468        atn
10469    }
10470
10471    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
10472        let mut atn = Atn::new(AtnType::Parser, 2);
10473        for (state, kind, rule) in [
10474            (0, AtnStateKind::RuleStart, 0),
10475            (1, AtnStateKind::Basic, 0),
10476            (2, AtnStateKind::Basic, 0),
10477            (3, AtnStateKind::Basic, 0),
10478            (4, AtnStateKind::RuleStop, 0),
10479            (5, AtnStateKind::RuleStart, 1),
10480            (6, AtnStateKind::StarLoopEntry, 1),
10481            (7, AtnStateKind::Basic, 1),
10482            (8, AtnStateKind::Basic, 1),
10483            (9, AtnStateKind::LoopEnd, 1),
10484            (10, AtnStateKind::RuleStop, 1),
10485            (11, AtnStateKind::RuleStart, 2),
10486            (12, AtnStateKind::RuleStop, 2),
10487        ] {
10488            let mut atn_state = AtnState::new(state, kind).with_rule_index(rule);
10489            if state == 5 {
10490                atn_state.left_recursive_rule = true;
10491            } else if state == 6 {
10492                atn_state.precedence_rule_decision = true;
10493            }
10494            atn.add_state(atn_state);
10495        }
10496        atn.set_rule_to_start_state(vec![0, 5, 11]);
10497        atn.set_rule_to_stop_state(vec![4, 10, 12]);
10498        atn.state_mut(0)
10499            .expect("caller start")
10500            .add_transition(Transition::Epsilon { target: 1 });
10501        atn.state_mut(1)
10502            .expect("caller invoking state")
10503            .add_transition(Transition::Rule {
10504                target: 5,
10505                rule_index: 1,
10506                follow_state: 2,
10507                precedence: 0,
10508            });
10509        atn.state_mut(2)
10510            .expect("nullable child invocation")
10511            .add_transition(Transition::Rule {
10512                target: 11,
10513                rule_index: 2,
10514                follow_state: 3,
10515                precedence: 0,
10516            });
10517        atn.state_mut(3)
10518            .expect("caller follow")
10519            .add_transition(Transition::Atom {
10520                target: 4,
10521                label: caller_symbol,
10522            });
10523        atn.state_mut(6)
10524            .expect("precedence loop")
10525            .add_transition(Transition::Epsilon { target: 7 });
10526        atn.state_mut(6)
10527            .expect("precedence loop")
10528            .add_transition(Transition::Epsilon { target: 9 });
10529        atn.state_mut(7)
10530            .expect("precedence predicate")
10531            .add_transition(Transition::Precedence {
10532                target: 8,
10533                precedence: 1,
10534            });
10535        atn.state_mut(8)
10536            .expect("operator token")
10537            .add_transition(Transition::Atom {
10538                target: 6,
10539                label: 1,
10540            });
10541        atn.state_mut(9)
10542            .expect("loop exit")
10543            .add_transition(Transition::Epsilon { target: 10 });
10544        atn.state_mut(11)
10545            .expect("nullable child")
10546            .add_transition(Transition::Epsilon { target: 12 });
10547        atn
10548    }
10549
10550    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
10551        let mut atn = Atn::new(AtnType::Parser, 2);
10552        for (state, kind, rule) in [
10553            (0, AtnStateKind::RuleStart, 0),
10554            (1, AtnStateKind::Basic, 0),
10555            (2, AtnStateKind::Basic, 0),
10556            (3, AtnStateKind::RuleStop, 0),
10557            (4, AtnStateKind::RuleStart, 1),
10558            (5, AtnStateKind::Basic, 1),
10559            (6, AtnStateKind::Basic, 1),
10560            (7, AtnStateKind::RuleStop, 1),
10561            (8, AtnStateKind::RuleStart, 2),
10562            (9, AtnStateKind::StarLoopEntry, 2),
10563            (10, AtnStateKind::Basic, 2),
10564            (11, AtnStateKind::Basic, 2),
10565            (12, AtnStateKind::LoopEnd, 2),
10566            (13, AtnStateKind::RuleStop, 2),
10567        ] {
10568            let mut atn_state = AtnState::new(state, kind).with_rule_index(rule);
10569            if state == 8 {
10570                atn_state.left_recursive_rule = true;
10571            } else if state == 9 {
10572                atn_state.precedence_rule_decision = true;
10573            }
10574            atn.add_state(atn_state);
10575        }
10576        atn.set_rule_to_start_state(vec![0, 4, 8]);
10577        atn.set_rule_to_stop_state(vec![3, 7, 13]);
10578        atn.state_mut(0)
10579            .expect("parent start")
10580            .add_transition(Transition::Epsilon { target: 1 });
10581        atn.state_mut(1)
10582            .expect("parent invocation")
10583            .add_transition(Transition::Rule {
10584                target: 4,
10585                rule_index: 1,
10586                follow_state: 2,
10587                precedence: 0,
10588            });
10589        atn.state_mut(2)
10590            .expect("parent follow")
10591            .add_transition(Transition::Atom {
10592                target: 3,
10593                label: caller_symbol,
10594            });
10595        atn.state_mut(4)
10596            .expect("caller start")
10597            .add_transition(Transition::Epsilon { target: 5 });
10598        atn.state_mut(5)
10599            .expect("caller invocation")
10600            .add_transition(Transition::Rule {
10601                target: 8,
10602                rule_index: 2,
10603                follow_state: 6,
10604                precedence: 0,
10605            });
10606        atn.state_mut(6)
10607            .expect("nullable caller return")
10608            .add_transition(Transition::Epsilon { target: 7 });
10609        atn.state_mut(9)
10610            .expect("precedence loop")
10611            .add_transition(Transition::Epsilon { target: 10 });
10612        atn.state_mut(9)
10613            .expect("precedence loop")
10614            .add_transition(Transition::Epsilon { target: 12 });
10615        atn.state_mut(10)
10616            .expect("precedence predicate")
10617            .add_transition(Transition::Precedence {
10618                target: 11,
10619                precedence: 1,
10620            });
10621        atn.state_mut(11)
10622            .expect("operator token")
10623            .add_transition(Transition::Atom {
10624                target: 9,
10625                label: 1,
10626            });
10627        atn.state_mut(12)
10628            .expect("loop exit")
10629            .add_transition(Transition::Epsilon { target: 13 });
10630        atn
10631    }
10632
10633    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
10634        let mut atn = Atn::new(AtnType::Parser, 2);
10635        for (state, kind, rule) in [
10636            (0, AtnStateKind::RuleStart, 0),
10637            (1, AtnStateKind::Basic, 0),
10638            (2, AtnStateKind::Basic, 0),
10639            (3, AtnStateKind::RuleStop, 0),
10640            (4, AtnStateKind::RuleStart, 1),
10641            (5, AtnStateKind::StarLoopEntry, 1),
10642            (6, AtnStateKind::Basic, 1),
10643            (7, AtnStateKind::Basic, 1),
10644            (8, AtnStateKind::Basic, 1),
10645            (9, AtnStateKind::Basic, 1),
10646            (10, AtnStateKind::LoopEnd, 1),
10647            (11, AtnStateKind::RuleStop, 1),
10648        ] {
10649            let mut atn_state = AtnState::new(state, kind).with_rule_index(rule);
10650            if state == 4 {
10651                atn_state.left_recursive_rule = true;
10652            } else if state == 5 {
10653                atn_state.precedence_rule_decision = true;
10654            }
10655            atn.add_state(atn_state);
10656        }
10657        atn.set_rule_to_start_state(vec![0, 4]);
10658        atn.set_rule_to_stop_state(vec![3, 11]);
10659        atn.state_mut(0)
10660            .expect("caller start")
10661            .add_transition(Transition::Epsilon { target: 1 });
10662        atn.state_mut(1)
10663            .expect("caller invocation")
10664            .add_transition(Transition::Rule {
10665                target: 4,
10666                rule_index: 1,
10667                follow_state: 2,
10668                precedence: 0,
10669            });
10670        atn.state_mut(2)
10671            .expect("caller follow")
10672            .add_transition(Transition::Atom {
10673                target: 3,
10674                label: caller_symbol,
10675            });
10676        atn.state_mut(5)
10677            .expect("precedence loop")
10678            .add_transition(Transition::Epsilon { target: 6 });
10679        atn.state_mut(5)
10680            .expect("precedence loop")
10681            .add_transition(Transition::Epsilon { target: 10 });
10682        atn.state_mut(6)
10683            .expect("precedence predicate")
10684            .add_transition(Transition::Precedence {
10685                target: 7,
10686                precedence: 1,
10687            });
10688        atn.state_mut(7)
10689            .expect("operator token")
10690            .add_transition(Transition::Atom {
10691                target: 8,
10692                label: 1,
10693            });
10694        atn.state_mut(8)
10695            .expect("recursive operand")
10696            .add_transition(Transition::Rule {
10697                target: 4,
10698                rule_index: 1,
10699                follow_state: 9,
10700                precedence: 2,
10701            });
10702        atn.state_mut(9)
10703            .expect("recursive operand follow")
10704            .add_transition(Transition::Epsilon { target: 5 });
10705        atn.state_mut(10)
10706            .expect("loop exit")
10707            .add_transition(Transition::Epsilon { target: 11 });
10708        atn
10709    }
10710
10711    #[test]
10712    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
10713        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
10714        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
10715
10716        let mut overlapping = parser_inside_left_recursive_callee(1);
10717        assert_eq!(
10718            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
10719            None
10720        );
10721
10722        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
10723        assert_eq!(
10724            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
10725            Some(true)
10726        );
10727
10728        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
10729        assert_eq!(
10730            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
10731            Some(false)
10732        );
10733
10734        assert_eq!(
10735            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
10736            Some(true),
10737            "overlap results must not leak across ATNs"
10738        );
10739    }
10740
10741    #[test]
10742    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
10743        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
10744        let mut parser = mini_parser(vec![
10745            CommonToken::new(1).with_text("operator"),
10746            CommonToken::eof("parser-test", 1, 1, 1),
10747        ]);
10748        parser.rule_context_stack = vec![RuleContextFrame {
10749            rule_index: 0,
10750            invoking_state: -1,
10751        }];
10752
10753        assert_eq!(
10754            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
10755            Some(true)
10756        );
10757        assert_eq!(
10758            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
10759            Some(true),
10760            "cached operator lookahead must preserve the nullable prefix return path"
10761        );
10762        assert_eq!(
10763            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
10764            Some(true),
10765            "the nullable child must use its rule-call precedence, not the caller precedence"
10766        );
10767    }
10768
10769    #[test]
10770    fn left_recursive_loop_defers_predicate_guarded_operator() {
10771        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
10772        let mut parser = mini_parser_with_hooks(
10773            vec![
10774                CommonToken::new(1).with_text("operator"),
10775                CommonToken::eof("parser-test", 1, 1, 1),
10776            ],
10777            RejectingPredicateHooks::default(),
10778        );
10779        parser.rule_context_stack = vec![RuleContextFrame {
10780            rule_index: 0,
10781            invoking_state: -1,
10782        }];
10783
10784        assert_eq!(
10785            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
10786            None,
10787            "a false predicate must be evaluated before entering the operator alternative"
10788        );
10789        assert_eq!(
10790            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
10791            None,
10792            "cached predicate-dependent lookahead must keep deferring"
10793        );
10794    }
10795
10796    #[test]
10797    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
10798        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
10799        let mut parser = parser_inside_left_recursive_callee(1);
10800
10801        assert_eq!(
10802            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
10803            None
10804        );
10805        assert_eq!(
10806            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
10807            None,
10808            "the cached overlap must preserve the nullable child return path"
10809        );
10810    }
10811
10812    #[test]
10813    fn left_recursive_loop_defers_through_nullable_parent_return() {
10814        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
10815        let mut parser = mini_parser(vec![
10816            CommonToken::new(1).with_text("lookahead"),
10817            CommonToken::eof("parser-test", 1, 1, 1),
10818        ]);
10819        parser.rule_context_stack = vec![
10820            RuleContextFrame {
10821                rule_index: 0,
10822                invoking_state: -1,
10823            },
10824            RuleContextFrame {
10825                rule_index: 1,
10826                invoking_state: 1,
10827            },
10828            RuleContextFrame {
10829                rule_index: 2,
10830                invoking_state: 5,
10831            },
10832        ];
10833
10834        assert_eq!(
10835            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
10836            None,
10837            "a nullable caller must unwind to its parent's consuming follow path"
10838        );
10839        assert_eq!(
10840            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
10841            None,
10842            "the caller-overlap cache must not retain a false negative"
10843        );
10844    }
10845
10846    #[test]
10847    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
10848        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
10849        let mut parser = mini_parser(vec![
10850            CommonToken::new(1).with_text("lookahead"),
10851            CommonToken::eof("parser-test", 1, 1, 1),
10852        ]);
10853        parser.rule_context_stack = vec![
10854            RuleContextFrame {
10855                rule_index: 0,
10856                invoking_state: -1,
10857            },
10858            RuleContextFrame {
10859                rule_index: 1,
10860                invoking_state: 1,
10861            },
10862            RuleContextFrame {
10863                rule_index: 1,
10864                invoking_state: 8,
10865            },
10866        ];
10867
10868        assert_eq!(
10869            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
10870            None,
10871            "a recursive operand return must preserve its parent caller context"
10872        );
10873        assert_eq!(
10874            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
10875            None,
10876            "the caller-overlap cache must preserve the loop-boundary return"
10877        );
10878    }
10879
10880    fn token_then_eof_atn() -> Atn {
10881        AtnDeserializer::new(&SerializedAtn::from_i32(&[
10882            4, 1, 2, // version, parser, max token type
10883            3, // states
10884            2, 0, // rule start
10885            1, 0, // basic
10886            7, 0, // rule stop
10887            0, // non-greedy states
10888            0, // precedence states
10889            1, // rules
10890            0, // rule 0 start
10891            0, // modes
10892            0, // sets
10893            2, // transitions
10894            0, 1, 5, 1, 0, 0, // match token 1
10895            1, 2, 5, -1, 0, 0, // match EOF
10896            0, // decisions
10897        ]))
10898        .deserialize()
10899        .expect("artificial parser ATN should deserialize")
10900    }
10901
10902    fn eof_then_action_atn() -> Atn {
10903        AtnDeserializer::new(&SerializedAtn::from_i32(&[
10904            4, 1, 1, // version, parser, max token type
10905            3, // states
10906            2, 0, // rule start
10907            1, 0, // basic
10908            7, 0, // rule stop
10909            0, // non-greedy states
10910            0, // precedence states
10911            1, // rules
10912            0, // rule 0 start
10913            0, // modes
10914            0, // sets
10915            2, // transitions
10916            0, 1, 5, -1, 0, 0, // match EOF
10917            1, 2, 6, 0, 0, 0, // parser action
10918            0, // decisions
10919        ]))
10920        .deserialize()
10921        .expect("artificial parser ATN should deserialize")
10922    }
10923
10924    fn noop_action_then_token_then_eof_atn() -> Atn {
10925        AtnDeserializer::new(&SerializedAtn::from_i32(&[
10926            4, 1, 2, // version, parser, max token type
10927            4, // states
10928            2, 0, // rule start
10929            1, 0, // basic
10930            1, 0, // basic
10931            7, 0, // rule stop
10932            0, // non-greedy states
10933            0, // precedence states
10934            1, // rules
10935            0, // rule 0 start
10936            0, // modes
10937            0, // sets
10938            3, // transitions
10939            0, 1, 6, 0, -1, 0, // no-op parser action
10940            1, 2, 5, 1, 0, 0, // match token 1
10941            2, 3, 5, -1, 0, 0, // match EOF
10942            0, // decisions
10943        ]))
10944        .deserialize()
10945        .expect("artificial no-op action ATN should deserialize")
10946    }
10947
10948    fn two_alt_decision_atn() -> Atn {
10949        let mut atn = Atn::new(AtnType::Parser, 2);
10950        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10951        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
10952        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10953        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
10954        atn.add_state(AtnState::new(4, AtnStateKind::BlockEnd).with_rule_index(0));
10955        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
10956        atn.set_rule_to_start_state(vec![0]);
10957        atn.set_rule_to_stop_state(vec![5]);
10958        atn.add_decision_state(1);
10959        atn.state_mut(0)
10960            .expect("state 0")
10961            .add_transition(Transition::Epsilon { target: 1 });
10962        atn.state_mut(1)
10963            .expect("state 1")
10964            .add_transition(Transition::Atom {
10965                target: 2,
10966                label: 1,
10967            });
10968        atn.state_mut(1)
10969            .expect("state 1")
10970            .add_transition(Transition::Atom {
10971                target: 3,
10972                label: 2,
10973            });
10974        atn.state_mut(2)
10975            .expect("state 2")
10976            .add_transition(Transition::Epsilon { target: 4 });
10977        atn.state_mut(3)
10978            .expect("state 3")
10979            .add_transition(Transition::Epsilon { target: 4 });
10980        atn.state_mut(4)
10981            .expect("state 4")
10982            .add_transition(Transition::Epsilon { target: 5 });
10983        atn
10984    }
10985
10986    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
10987    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
10988    fn optional_then_b_eof_atn() -> Atn {
10989        let mut atn = Atn::new(AtnType::Parser, 3);
10990        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10991        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
10992        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10993        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
10994        atn.add_state(AtnState::new(4, AtnStateKind::Basic).with_rule_index(0));
10995        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
10996        atn.set_rule_to_start_state(vec![0]);
10997        atn.set_rule_to_stop_state(vec![5]);
10998        atn.add_decision_state(1);
10999        atn.state_mut(0)
11000            .expect("state 0")
11001            .add_transition(Transition::Epsilon { target: 1 });
11002        // Optional block: match A then fall through, or skip straight to state 3.
11003        atn.state_mut(1)
11004            .expect("state 1")
11005            .add_transition(Transition::Atom {
11006                target: 3,
11007                label: 1,
11008            });
11009        atn.state_mut(1)
11010            .expect("state 1")
11011            .add_transition(Transition::Epsilon { target: 3 });
11012        // Match B, then EOF.
11013        atn.state_mut(3)
11014            .expect("state 3")
11015            .add_transition(Transition::Atom {
11016                target: 4,
11017                label: 2,
11018            });
11019        atn.state_mut(4)
11020            .expect("state 4")
11021            .add_transition(Transition::Atom {
11022                target: 5,
11023                label: TOKEN_EOF,
11024            });
11025        atn
11026    }
11027
11028    #[test]
11029    fn sync_decision_deletes_only_a_single_token() {
11030        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
11031        // expected. `(A)? B EOF` at the optional-block decision:
11032        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
11033        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
11034        //               without consuming and records the expected set for the
11035        //               subsequent mismatch (the parser must not over-consume both
11036        //               `C`s and accept the input).
11037        let atn = optional_then_b_eof_atn();
11038
11039        let mut single = mini_parser(vec![
11040            CommonToken::new(3).with_text("c"),
11041            CommonToken::new(2).with_text("b"),
11042            CommonToken::eof("parser-test", 1, 2, 2),
11043        ]);
11044        single.rule_context_stack = vec![RuleContextFrame {
11045            rule_index: 0,
11046            invoking_state: 0,
11047        }];
11048        let children = single
11049            .sync_decision(&atn, 1, true, false)
11050            .expect("single extraneous token recovers");
11051        assert_eq!(children.len(), 1);
11052        assert!(matches!(children[0], ParseTree::Error(_)));
11053        assert_eq!(single.number_of_syntax_errors(), 1);
11054        // Exactly one token consumed (the cursor now sits on `b`).
11055        assert_eq!(single.la(1), 2);
11056
11057        let mut double = mini_parser(vec![
11058            CommonToken::new(3).with_text("c"),
11059            CommonToken::new(3).with_text("c"),
11060            CommonToken::new(2).with_text("b"),
11061            CommonToken::eof("parser-test", 1, 3, 3),
11062        ]);
11063        double.rule_context_stack = vec![RuleContextFrame {
11064            rule_index: 0,
11065            invoking_state: 0,
11066        }];
11067        let result = double.sync_decision(&atn, 1, true, false);
11068        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
11069        // consume either `c`. It reports the mismatch at the first `c` (so the parser
11070        // does not over-consume both and accept the input). Nothing is consumed, so
11071        // the cursor still sits on the first `c` for rule-level recovery.
11072        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
11073        match error {
11074            AntlrError::ParserError { message, .. } => {
11075                assert!(message.starts_with("mismatched input"), "got: {message}");
11076            }
11077            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
11078        }
11079        assert_eq!(double.la(1), 3);
11080    }
11081
11082    /// The real serialized ATN that `antlr4-rust-gen` emits for
11083    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
11084    /// the loop is `EOF`. The loop decision is state 5.
11085    fn star_loop_then_eof_atn() -> Atn {
11086        AtnDeserializer::new(&SerializedAtn::from_i32(&[
11087            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,
11088            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,
11089            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,
11090            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
11091        ]))
11092        .deserialize()
11093        .expect("star-loop-then-EOF ATN should deserialize")
11094    }
11095
11096    /// ATN for `s : a+ Y ; a : X ;`.
11097    ///
11098    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
11099    /// `+` loop must not treat that zero-width child as a successful iteration
11100    /// and then re-enter the loop at the same token index.
11101    fn plus_loop_with_recovering_body_atn() -> Atn {
11102        let mut atn = Atn::new(AtnType::Parser, 2);
11103        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
11104        let mut loop_start = AtnState::new(1, AtnStateKind::PlusBlockStart).with_rule_index(0);
11105        loop_start.end_state = Some(3);
11106        atn.add_state(loop_start);
11107        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
11108        atn.add_state(AtnState::new(3, AtnStateKind::BlockEnd).with_rule_index(0));
11109        atn.add_state(AtnState::new(4, AtnStateKind::PlusLoopBack).with_rule_index(0));
11110        let mut loop_end = AtnState::new(5, AtnStateKind::LoopEnd).with_rule_index(0);
11111        loop_end.loop_back_state = Some(4);
11112        atn.add_state(loop_end);
11113        atn.add_state(AtnState::new(6, AtnStateKind::RuleStop).with_rule_index(0));
11114        atn.add_state(AtnState::new(7, AtnStateKind::RuleStart).with_rule_index(1));
11115        atn.add_state(AtnState::new(8, AtnStateKind::Basic).with_rule_index(1));
11116        atn.add_state(AtnState::new(9, AtnStateKind::RuleStop).with_rule_index(1));
11117        atn.set_rule_to_start_state(vec![0, 7]);
11118        atn.set_rule_to_stop_state(vec![6, 9]);
11119        atn.state_mut(0)
11120            .expect("state 0")
11121            .add_transition(Transition::Epsilon { target: 1 });
11122        atn.state_mut(1)
11123            .expect("state 1")
11124            .add_transition(Transition::Epsilon { target: 2 });
11125        atn.state_mut(2)
11126            .expect("state 2")
11127            .add_transition(Transition::Rule {
11128                target: 7,
11129                rule_index: 1,
11130                follow_state: 3,
11131                precedence: 0,
11132            });
11133        atn.state_mut(3)
11134            .expect("state 3")
11135            .add_transition(Transition::Epsilon { target: 4 });
11136        atn.state_mut(4)
11137            .expect("state 4")
11138            .add_transition(Transition::Epsilon { target: 1 });
11139        atn.state_mut(4)
11140            .expect("state 4")
11141            .add_transition(Transition::Epsilon { target: 5 });
11142        atn.state_mut(5)
11143            .expect("state 5")
11144            .add_transition(Transition::Atom {
11145                target: 6,
11146                label: 2,
11147            });
11148        atn.state_mut(7)
11149            .expect("state 7")
11150            .add_transition(Transition::Atom {
11151                target: 8,
11152                label: 1,
11153            });
11154        atn.state_mut(8)
11155            .expect("state 8")
11156            .add_transition(Transition::Epsilon { target: 9 });
11157        atn
11158    }
11159
11160    #[test]
11161    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
11162        let atn = plus_loop_with_recovering_body_atn();
11163        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11164
11165        let error = parser
11166            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11167            .expect_err("EOF recovery should report a bounded mismatch");
11168
11169        let AntlrError::ParserError { message, .. } = error else {
11170            panic!("expected ParserError, got {error:?}");
11171        };
11172        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
11173        assert_eq!(parser.number_of_syntax_errors(), 1);
11174        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
11175    }
11176
11177    #[test]
11178    fn sync_decision_deletes_token_before_eof_at_loop_back() {
11179        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
11180        // At the loop ENTRY (loop_back = false) a single unexpected token before
11181        // EOF is deleted as an error node (then the generated EOF match consumes
11182        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
11183        // EOF must be a valid scan-stop for this to fire.
11184        let atn = star_loop_then_eof_atn();
11185        let mut parser = mini_parser(vec![
11186            CommonToken::new(2).with_text("c"),
11187            CommonToken::eof("parser-test", 1, 1, 1),
11188        ]);
11189        parser.rule_context_stack = vec![RuleContextFrame {
11190            rule_index: 0,
11191            invoking_state: 0,
11192        }];
11193        let children = parser
11194            .sync_decision(&atn, 5, true, false)
11195            .expect("single token before EOF recovers");
11196        assert_eq!(children.len(), 1);
11197        assert!(matches!(children[0], ParseTree::Error(_)));
11198        assert_eq!(parser.number_of_syntax_errors(), 1);
11199        assert_eq!(
11200            parser.la(1),
11201            TOKEN_EOF,
11202            "EOF is left for the rule's EOF match"
11203        );
11204    }
11205
11206    #[test]
11207    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
11208        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
11209        // single-token deletion, which fails because LA(2) = `c` is not expected —
11210        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
11211        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
11212        let atn = star_loop_then_eof_atn();
11213        let mut parser = mini_parser(vec![
11214            CommonToken::new(2).with_text("c"),
11215            CommonToken::new(2).with_text("c"),
11216            CommonToken::eof("parser-test", 1, 2, 2),
11217        ]);
11218        parser.rule_context_stack = vec![RuleContextFrame {
11219            rule_index: 0,
11220            invoking_state: 0,
11221        }];
11222        let error = parser
11223            .sync_decision(&atn, 5, true, false)
11224            .expect_err("two tokens at the loop entry must not be deleted");
11225        match error {
11226            AntlrError::ParserError { message, .. } => {
11227                assert!(message.starts_with("mismatched input"), "got: {message}");
11228            }
11229            other => panic!("expected mismatched-input ParserError, got {other:?}"),
11230        }
11231        assert_eq!(
11232            parser.la(1),
11233            2,
11234            "nothing consumed; cursor still on first `c`"
11235        );
11236    }
11237
11238    #[test]
11239    fn sync_decision_consumes_until_eof_at_loop_back() {
11240        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
11241        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
11242        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
11243        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
11244        // post-`a` state directly: `c c <EOF>` with loop_back = true.
11245        let atn = star_loop_then_eof_atn();
11246        let mut parser = mini_parser(vec![
11247            CommonToken::new(2).with_text("c"),
11248            CommonToken::new(2).with_text("c"),
11249            CommonToken::eof("parser-test", 1, 2, 2),
11250        ]);
11251        parser.rule_context_stack = vec![RuleContextFrame {
11252            rule_index: 0,
11253            invoking_state: 0,
11254        }];
11255        let children = parser
11256            .sync_decision(&atn, 5, false, true)
11257            .expect("loop-back multi-token deletion recovers onto EOF");
11258        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
11259        assert!(children.iter().all(|c| matches!(c, ParseTree::Error(_))));
11260        assert_eq!(parser.number_of_syntax_errors(), 1);
11261        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
11262    }
11263
11264    fn predicate_after_token_atn() -> Atn {
11265        let mut atn = Atn::new(AtnType::Parser, 2);
11266        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
11267        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
11268        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
11269        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
11270        atn.add_state(AtnState::new(4, AtnStateKind::RuleStop).with_rule_index(0));
11271        atn.set_rule_to_start_state(vec![0]);
11272        atn.set_rule_to_stop_state(vec![4]);
11273        atn.state_mut(0)
11274            .expect("state 0")
11275            .add_transition(Transition::Atom {
11276                target: 1,
11277                label: 1,
11278            });
11279        atn.state_mut(1)
11280            .expect("state 1")
11281            .add_transition(Transition::Predicate {
11282                target: 2,
11283                rule_index: 0,
11284                pred_index: 0,
11285                context_dependent: false,
11286            });
11287        atn.state_mut(2)
11288            .expect("state 2")
11289            .add_transition(Transition::Atom {
11290                target: 3,
11291                label: 2,
11292            });
11293        atn.state_mut(3)
11294            .expect("state 3")
11295            .add_transition(Transition::Epsilon { target: 4 });
11296        atn
11297    }
11298
11299    fn nested_nullable_context_atn() -> Atn {
11300        let mut atn = Atn::new(AtnType::Parser, 1);
11301        for state_number in 0..=20 {
11302            let kind = match state_number {
11303                0 | 10 | 16 => AtnStateKind::RuleStart,
11304                9 | 15 | 20 => AtnStateKind::RuleStop,
11305                _ => AtnStateKind::Basic,
11306            };
11307            let rule_index = match state_number {
11308                0..=9 => 0,
11309                10..=15 => 1,
11310                _ => 2,
11311            };
11312            atn.add_state(AtnState::new(state_number, kind).with_rule_index(rule_index));
11313        }
11314        atn.set_rule_to_start_state(vec![0, 10, 16]);
11315        atn.set_rule_to_stop_state(vec![9, 15, 20]);
11316        atn.state_mut(1)
11317            .expect("state 1")
11318            .add_transition(Transition::Rule {
11319                target: 10,
11320                rule_index: 1,
11321                follow_state: 8,
11322                precedence: 0,
11323            });
11324        atn.state_mut(8)
11325            .expect("state 8")
11326            .add_transition(Transition::Atom {
11327                target: 9,
11328                label: 1,
11329            });
11330        atn.state_mut(8)
11331            .expect("state 8")
11332            .add_transition(Transition::Epsilon { target: 9 });
11333        atn.state_mut(2)
11334            .expect("state 2")
11335            .add_transition(Transition::Rule {
11336                target: 16,
11337                rule_index: 2,
11338                follow_state: 14,
11339                precedence: 0,
11340            });
11341        atn.state_mut(14)
11342            .expect("state 14")
11343            .add_transition(Transition::Epsilon { target: 15 });
11344        atn
11345    }
11346
11347    fn generated_match_recovery_atn() -> Atn {
11348        let mut atn = Atn::new(AtnType::Parser, 2);
11349        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
11350        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
11351        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
11352        atn.add_state(AtnState::new(3, AtnStateKind::RuleStop).with_rule_index(0));
11353        atn.add_state(AtnState::new(4, AtnStateKind::RuleStart).with_rule_index(1));
11354        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(1));
11355        atn.set_rule_to_start_state(vec![0, 4]);
11356        atn.set_rule_to_stop_state(vec![3, 5]);
11357        atn.state_mut(1)
11358            .expect("state 1")
11359            .add_transition(Transition::Rule {
11360                target: 4,
11361                rule_index: 1,
11362                follow_state: 2,
11363                precedence: 0,
11364            });
11365        atn.state_mut(2)
11366            .expect("state 2")
11367            .add_transition(Transition::Atom {
11368                target: 3,
11369                label: TOKEN_EOF,
11370            });
11371        atn
11372    }
11373
11374    fn complement_set_atn() -> Atn {
11375        let mut atn = Atn::new(AtnType::Parser, 1);
11376        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
11377        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
11378        atn.set_rule_to_start_state(vec![0]);
11379        atn.set_rule_to_stop_state(vec![1]);
11380        let mut excluded = IntervalSet::new();
11381        excluded.add(1);
11382        atn.state_mut(0)
11383            .expect("state 0")
11384            .add_transition(Transition::NotSet {
11385                target: 1,
11386                set: excluded,
11387            });
11388        atn
11389    }
11390
11391    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
11392    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
11393    fn wildcard_then_eof_atn() -> Atn {
11394        let mut atn = Atn::new(AtnType::Parser, 1);
11395        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
11396        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
11397        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
11398        atn.set_rule_to_start_state(vec![0]);
11399        atn.set_rule_to_stop_state(vec![1]);
11400        atn.state_mut(0)
11401            .expect("state 0")
11402            .add_transition(Transition::Wildcard { target: 2 });
11403        atn.state_mut(2)
11404            .expect("state 2")
11405            .add_transition(Transition::Atom {
11406                target: 1,
11407                label: TOKEN_EOF,
11408            });
11409        atn
11410    }
11411
11412    #[test]
11413    fn parser_matches_token_and_reports_mismatch() {
11414        let source = Source {
11415            tokens: vec![
11416                CommonToken::new(1).with_text("x"),
11417                CommonToken::eof("parser-test", 1, 1, 1),
11418            ],
11419            index: 0,
11420        };
11421        let data = RecognizerData::new(
11422            "Mini.g4",
11423            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
11424        );
11425        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
11426        assert_eq!(
11427            parser.match_token(1).expect("token 1 should match").text(),
11428            "x"
11429        );
11430        assert!(parser.match_token(1).is_err());
11431    }
11432
11433    #[test]
11434    fn parser_matches_token_sets() {
11435        let mut parser = mini_parser(vec![
11436            CommonToken::new(1).with_text("x"),
11437            CommonToken::eof("parser-test", 1, 1, 1),
11438        ]);
11439
11440        assert_eq!(
11441            parser
11442                .match_set(&[(1, 1), (3, 4)])
11443                .expect("token set should match")
11444                .text(),
11445            "x"
11446        );
11447        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
11448    }
11449
11450    #[test]
11451    fn generated_rule_api_tracks_state_and_precedence() {
11452        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11453
11454        let context = parser.enter_rule(7, 2);
11455        assert_eq!(context.rule_index(), 2);
11456        assert_eq!(parser.state(), 7);
11457        assert_eq!(
11458            parser.rule_context_stack,
11459            vec![RuleContextFrame {
11460                rule_index: 2,
11461                invoking_state: 7
11462            }]
11463        );
11464
11465        let recursive = parser.enter_recursion_rule(11, 3, 4);
11466        assert_eq!(recursive.rule_index(), 3);
11467        assert!(parser.precpred(4));
11468        assert!(parser.precpred(5));
11469        assert!(!parser.precpred(3));
11470
11471        let next = parser.push_new_recursion_context(13, 3);
11472        assert_eq!(next.invoking_state(), 13);
11473        parser.unroll_recursion_context();
11474        assert_eq!(parser.precedence_stack, vec![0]);
11475        assert_eq!(
11476            parser.rule_context_stack,
11477            vec![RuleContextFrame {
11478                rule_index: 2,
11479                invoking_state: 7
11480            }]
11481        );
11482
11483        parser.exit_rule();
11484        assert!(parser.rule_context_stack.is_empty());
11485    }
11486
11487    #[test]
11488    fn parser_predicates_support_token_adjacency() {
11489        let mut parser = mini_parser(vec![
11490            CommonToken::new(1).with_text("=").with_span(0, 0),
11491            CommonToken::new(1).with_text(">").with_span(1, 1),
11492            CommonToken::eof("parser-test", 2, 1, 2),
11493        ]);
11494        parser.consume();
11495        parser.consume();
11496
11497        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
11498
11499        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
11500
11501        let mut parser = mini_parser(vec![
11502            CommonToken::new(1).with_text("=").with_span(0, 0),
11503            CommonToken::new(1)
11504                .with_text(" ")
11505                .with_channel(HIDDEN_CHANNEL)
11506                .with_span(1, 1),
11507            CommonToken::new(1).with_text(">").with_span(2, 2),
11508            CommonToken::eof("parser-test", 3, 1, 3),
11509        ]);
11510        parser.consume();
11511        parser.consume();
11512
11513        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
11514    }
11515
11516    #[test]
11517    fn parser_predicates_support_context_child_text_checks() {
11518        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11519        let mut context = ParserRuleContext::new(1, 0);
11520        let mut child_context = ParserRuleContext::new(2, 0);
11521        child_context.add_child(ParseTree::Terminal(TerminalNode::new(
11522            CommonToken::new(1).with_text("var"),
11523        )));
11524        context.add_child(ParseTree::Rule(RuleNode::new(child_context)));
11525        let predicates = [(
11526            1,
11527            0,
11528            ParserPredicate::ContextChildRuleTextNotEquals {
11529                rule_index: 2,
11530                text: "var",
11531            },
11532        )];
11533
11534        assert!(
11535            !parser.parser_semantic_predicate_matches_with_context_and_local(
11536                &predicates,
11537                1,
11538                0,
11539                &context,
11540                0,
11541            )
11542        );
11543    }
11544
11545    #[test]
11546    fn context_expected_symbols_walks_nullable_parent_contexts() {
11547        let atn = nested_nullable_context_atn();
11548        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11549        parser.rule_context_stack = vec![
11550            RuleContextFrame {
11551                rule_index: 0,
11552                invoking_state: 0,
11553            },
11554            RuleContextFrame {
11555                rule_index: 1,
11556                invoking_state: 1,
11557            },
11558            RuleContextFrame {
11559                rule_index: 2,
11560                invoking_state: 2,
11561            },
11562        ];
11563
11564        let expected = parser.context_expected_symbols(&atn);
11565
11566        assert!(expected.contains(&1));
11567        assert!(expected.contains(&TOKEN_EOF));
11568    }
11569
11570    #[test]
11571    fn prediction_context_reuses_cached_stack_until_rule_stack_changes() {
11572        let atn = nested_nullable_context_atn();
11573        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11574        parser.rule_context_stack = vec![
11575            RuleContextFrame {
11576                rule_index: 0,
11577                invoking_state: 0,
11578            },
11579            RuleContextFrame {
11580                rule_index: 1,
11581                invoking_state: 1,
11582            },
11583            RuleContextFrame {
11584                rule_index: 2,
11585                invoking_state: 2,
11586            },
11587        ];
11588
11589        let first = parser.prediction_context(&atn);
11590        let second = parser.prediction_context(&atn);
11591        assert!(Rc::ptr_eq(&first, &second));
11592
11593        parser.exit_rule();
11594        let after_pop = parser.prediction_context(&atn);
11595        assert!(!Rc::ptr_eq(&first, &after_pop));
11596    }
11597
11598    #[test]
11599    fn generated_match_token_recovers_missing_token_from_context_follow() {
11600        let atn = generated_match_recovery_atn();
11601        let data = RecognizerData::new(
11602            "Mini.g4",
11603            Vocabulary::new(
11604                [None, Some("'X'"), Some("'Y'")],
11605                [None, Some("X"), Some("Y")],
11606                [None::<&str>, None, None],
11607            ),
11608        );
11609        let mut parser = BaseParser::new(
11610            CommonTokenStream::new(Source {
11611                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
11612                index: 0,
11613            }),
11614            data,
11615        );
11616        parser.rule_context_stack = vec![
11617            RuleContextFrame {
11618                rule_index: 0,
11619                invoking_state: 0,
11620            },
11621            RuleContextFrame {
11622                rule_index: 1,
11623                invoking_state: 1,
11624            },
11625        ];
11626        assert_eq!(parser.number_of_syntax_errors(), 0);
11627
11628        let node = parser
11629            .match_token_recovering(2, 5, &atn)
11630            .expect("generated match should insert missing token");
11631
11632        assert_eq!(node.children().len(), 1);
11633        assert_eq!(node.children()[0].text(), "<missing 'Y'>");
11634        assert_eq!(
11635            node.clone()
11636                .into_child_iter()
11637                .map(|child| child.text())
11638                .collect::<Vec<_>>(),
11639            ["<missing 'Y'>"]
11640        );
11641        // Single-token insertion synthesizes a missing token and consumes nothing,
11642        // so no EOF terminal is consumed even though lookahead is EOF.
11643        assert!(!node.consumed_eof());
11644        assert_eq!(parser.la(1), TOKEN_EOF);
11645        assert_eq!(parser.number_of_syntax_errors(), 1);
11646        assert_eq!(
11647            parser.generated_parser_diagnostics,
11648            [ParserDiagnostic {
11649                line: 1,
11650                column: 3,
11651                message: "missing 'Y' at '<EOF>'".to_owned(),
11652            }]
11653        );
11654    }
11655
11656    #[test]
11657    fn generated_match_token_counts_single_token_deletion_recovery() {
11658        let atn = generated_match_recovery_atn();
11659        let data = RecognizerData::new(
11660            "Mini.g4",
11661            Vocabulary::new(
11662                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
11663                [None, Some("X"), Some("Y"), Some("Z")],
11664                [None::<&str>, None, None, None],
11665            ),
11666        );
11667        let mut parser = BaseParser::new(
11668            CommonTokenStream::new(Source {
11669                tokens: vec![
11670                    CommonToken::new(3).with_text("z"),
11671                    CommonToken::new(2).with_text("y"),
11672                    CommonToken::eof("parser-test", 3, 1, 3),
11673                ],
11674                index: 0,
11675            }),
11676            data,
11677        );
11678
11679        let node = parser
11680            .match_token_recovering(2, 5, &atn)
11681            .expect("generated match should delete the extraneous token");
11682
11683        assert_eq!(node.children().len(), 2);
11684        assert!(matches!(node.children()[0], ParseTree::Error(_)));
11685        assert_eq!(node.children()[0].text(), "z");
11686        assert_eq!(node.children()[1].text(), "y");
11687        assert_eq!(
11688            node.into_child_iter()
11689                .map(|child| child.text())
11690                .collect::<Vec<_>>(),
11691            ["z", "y"]
11692        );
11693        assert_eq!(parser.number_of_syntax_errors(), 1);
11694    }
11695
11696    #[test]
11697    fn generated_match_token_iterates_single_success_without_a_children_vec() {
11698        let atn = generated_match_recovery_atn();
11699        let data = RecognizerData::new(
11700            "Mini.g4",
11701            Vocabulary::new(
11702                [None, Some("'X'"), Some("'Y'")],
11703                [None, Some("X"), Some("Y")],
11704                [None::<&str>, None, None],
11705            ),
11706        );
11707        let mut parser = BaseParser::new(
11708            CommonTokenStream::new(Source {
11709                tokens: vec![
11710                    CommonToken::new(2).with_text("y"),
11711                    CommonToken::eof("parser-test", 1, 1, 1),
11712                ],
11713                index: 0,
11714            }),
11715            data,
11716        );
11717
11718        let node = parser
11719            .match_token_recovering(2, 5, &atn)
11720            .expect("generated match should consume the expected token");
11721
11722        assert_eq!(
11723            node.into_child_iter()
11724                .map(|child| child.text())
11725                .collect::<Vec<_>>(),
11726            ["y"]
11727        );
11728        assert_eq!(parser.number_of_syntax_errors(), 0);
11729    }
11730
11731    #[test]
11732    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
11733        let atn = generated_match_recovery_atn();
11734        let data = RecognizerData::new(
11735            "Mini.g4",
11736            Vocabulary::new(
11737                [None, Some("'X'"), Some("'Y'")],
11738                [None, Some("X"), Some("Y")],
11739                [None::<&str>, None, None],
11740            ),
11741        );
11742        let mut parser = BaseParser::new(
11743            CommonTokenStream::new(Source {
11744                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
11745                index: 0,
11746            }),
11747            data,
11748        );
11749        parser.rule_context_stack = vec![
11750            RuleContextFrame {
11751                rule_index: 0,
11752                invoking_state: 0,
11753            },
11754            RuleContextFrame {
11755                rule_index: 1,
11756                invoking_state: 1,
11757            },
11758        ];
11759        let marker = parser.generated_diagnostics_checkpoint();
11760
11761        let _ = parser
11762            .match_token_recovering(2, 5, &atn)
11763            .expect("generated match should insert missing token");
11764        assert_eq!(parser.number_of_syntax_errors(), 1);
11765
11766        parser.restore_generated_diagnostics(marker);
11767
11768        assert_eq!(parser.number_of_syntax_errors(), 0);
11769        assert!(parser.generated_parser_diagnostics.is_empty());
11770    }
11771
11772    #[test]
11773    fn generated_prediction_diagnostics_use_adaptive_context() {
11774        let atn = two_alt_decision_atn();
11775        let data = RecognizerData::new(
11776            "Mini.g4",
11777            Vocabulary::new(
11778                [None, Some("'x'"), Some("'y'")],
11779                [None, Some("X"), Some("Y")],
11780                [None::<&str>, None, None],
11781            ),
11782        )
11783        .with_rule_names(["s"]);
11784        let mut parser = BaseParser::new(
11785            CommonTokenStream::new(Source {
11786                tokens: vec![
11787                    CommonToken::new(1)
11788                        .with_text("x")
11789                        .with_position(1, 0)
11790                        .with_span(0, 0),
11791                    CommonToken::new(2)
11792                        .with_text("y")
11793                        .with_position(1, 2)
11794                        .with_span(1, 1),
11795                    CommonToken::eof("parser-test", 2, 1, 3),
11796                ],
11797                index: 0,
11798            }),
11799            data,
11800        );
11801        parser.set_report_diagnostic_errors(true);
11802
11803        parser.record_generated_prediction_diagnostic(
11804            &atn,
11805            1,
11806            &ParserAtnPrediction {
11807                alt: 1,
11808                requires_full_context: true,
11809                has_semantic_context: false,
11810                diagnostic: Some(ParserAtnPredictionDiagnostic {
11811                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
11812                    start_index: 0,
11813                    sll_stop_index: 1,
11814                    ll_stop_index: 0,
11815                    conflicting_alts: vec![1, 2],
11816                    exact: false,
11817                }),
11818            },
11819        );
11820        // Ambiguities from the default LL prediction mode are non-exact, so —
11821        // matching Java's exactOnly DiagnosticErrorListener — only the
11822        // attempting-full-context line is reported. Exact-ambiguity mode
11823        // reports the ambiguity itself.
11824        parser.record_generated_prediction_diagnostic(
11825            &atn,
11826            1,
11827            &ParserAtnPrediction {
11828                alt: 1,
11829                requires_full_context: true,
11830                has_semantic_context: false,
11831                diagnostic: Some(ParserAtnPredictionDiagnostic {
11832                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
11833                    start_index: 0,
11834                    sll_stop_index: 1,
11835                    ll_stop_index: 1,
11836                    conflicting_alts: vec![1, 2],
11837                    exact: false,
11838                }),
11839            },
11840        );
11841
11842        assert_eq!(
11843            parser.generated_parser_diagnostics,
11844            [
11845                ParserDiagnostic {
11846                    line: 1,
11847                    column: 2,
11848                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
11849                },
11850                ParserDiagnostic {
11851                    line: 1,
11852                    column: 0,
11853                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
11854                },
11855                ParserDiagnostic {
11856                    line: 1,
11857                    column: 2,
11858                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
11859                },
11860            ]
11861        );
11862    }
11863
11864    #[test]
11865    fn generated_match_not_set_recovers_empty_complement_at_eof() {
11866        let atn = complement_set_atn();
11867        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11868        parser.rule_context_stack = vec![RuleContextFrame {
11869            rule_index: 0,
11870            invoking_state: 0,
11871        }];
11872
11873        let node = parser
11874            .match_not_set_recovering(&[(1, 1)], 1, 1, 1, &atn)
11875            .expect("empty complement should recover at EOF");
11876
11877        assert_eq!(node.children().len(), 1);
11878        // Recovery synthesizes a missing token without consuming EOF, so the
11879        // enclosing rule must not record EOF as its stop token.
11880        assert!(!node.consumed_eof());
11881        assert_eq!(parser.la(1), TOKEN_EOF);
11882        assert_eq!(
11883            parser.generated_parser_diagnostics,
11884            [ParserDiagnostic {
11885                line: 1,
11886                column: 1,
11887                message: "missing {} at '<EOF>'".to_owned(),
11888            }]
11889        );
11890    }
11891
11892    #[test]
11893    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
11894        // `start : . EOF ;` on empty input. The wildcard is modeled as an
11895        // empty-complement not-set; at EOF the follow state (the explicit EOF
11896        // match) expects EOF, so even in the start rule recovery must perform
11897        // single-token insertion (`<missing ...>`) rather than aborting — matching
11898        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
11899        let atn = wildcard_then_eof_atn();
11900        let data = RecognizerData::new(
11901            "Mini.g4",
11902            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
11903        );
11904        let mut parser = BaseParser::new(
11905            CommonTokenStream::new(Source {
11906                tokens: vec![CommonToken::eof("parser-test", 1, 1, 1)],
11907                index: 0,
11908            }),
11909            data,
11910        );
11911        parser.rule_context_stack = vec![RuleContextFrame {
11912            rule_index: 0,
11913            invoking_state: 0,
11914        }];
11915
11916        let node = parser
11917            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
11918            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
11919
11920        // A single `<missing ...>` error node is inserted; EOF is not consumed.
11921        assert_eq!(node.children().len(), 1);
11922        assert!(!node.consumed_eof());
11923        assert!(node.children()[0].text().starts_with("<missing"));
11924        assert_eq!(parser.la(1), TOKEN_EOF);
11925        assert_eq!(
11926            parser.generated_parser_diagnostics,
11927            [ParserDiagnostic {
11928                line: 1,
11929                column: 1,
11930                message: "missing 'x' at '<EOF>'".to_owned(),
11931            }]
11932        );
11933    }
11934
11935    #[test]
11936    fn generated_rule_recovery_consumes_to_parent_follow() {
11937        let atn = generated_match_recovery_atn();
11938        let data = RecognizerData::new(
11939            "Mini.g4",
11940            Vocabulary::new(
11941                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
11942                [None, Some("X"), Some("Y"), Some("Z")],
11943                [None::<&str>, None, None, None],
11944            ),
11945        );
11946        let mut parser = BaseParser::new(
11947            CommonTokenStream::new(Source {
11948                tokens: vec![
11949                    CommonToken::new(3).with_text("z"),
11950                    CommonToken::eof("parser-test", 1, 1, 1),
11951                ],
11952                index: 0,
11953            }),
11954            data,
11955        );
11956        let _parent = parser.enter_rule(0, 0);
11957        let marker = parser.push_invoking_state(1);
11958        let mut child = parser.enter_rule(4, 1);
11959        parser.discard_invoking_state(marker);
11960
11961        parser.recover_generated_rule(
11962            &mut child,
11963            &atn,
11964            AntlrError::ParserError {
11965                line: 1,
11966                column: 0,
11967                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
11968            },
11969        );
11970        let tree = parser.finish_rule(child, false);
11971
11972        assert_eq!(parser.la(1), TOKEN_EOF);
11973        assert_eq!(tree.to_string_tree_with_names(&["s", "a"]), "(a z)");
11974        assert_eq!(parser.number_of_syntax_errors(), 1);
11975        assert_eq!(
11976            parser.generated_parser_diagnostics,
11977            [ParserDiagnostic {
11978                line: 1,
11979                column: 0,
11980                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
11981            }]
11982        );
11983        parser.exit_rule();
11984    }
11985
11986    #[test]
11987    fn greedy_ll1_alt_handles_nullable_loop_exit() {
11988        let mut body_symbols = TokenBitSet::default();
11989        body_symbols.insert(1);
11990        let entry = DecisionLookahead {
11991            transitions: vec![
11992                TransitionLookSet {
11993                    symbols: body_symbols,
11994                    nullable: false,
11995                },
11996                TransitionLookSet {
11997                    symbols: TokenBitSet::default(),
11998                    nullable: true,
11999                },
12000            ],
12001        };
12002
12003        assert_eq!(ll1_unique_alt(&entry, 2), None);
12004        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
12005        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
12006        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
12007    }
12008
12009    #[test]
12010    fn single_outcome_memo_probe_selects_sparse_or_promote_mode() {
12011        let key = |state_number| FastRecognizeKey {
12012            state_number,
12013            stop_state: 10,
12014            index: state_number,
12015            rule_start_index: 0,
12016            decision_start_index: None,
12017            precedence: 0,
12018            recovery_symbols_id: 0,
12019            recovery_state: None,
12020        };
12021
12022        let mut sparse = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
12023        for state_number in 0..(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT - 1) {
12024            assert!(sparse.should_memoize_single_outcome(&key(state_number)));
12025        }
12026        assert!(!sparse.should_memoize_single_outcome(&key(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT)));
12027        assert_eq!(
12028            sparse.single_outcome_memo_mode,
12029            SingleOutcomeMemoMode::Sparse
12030        );
12031
12032        let mut promote = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
12033        let repeated = key(1);
12034        for _ in 0..=CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
12035            assert!(promote.should_memoize_single_outcome(&repeated));
12036        }
12037        assert_eq!(
12038            promote.single_outcome_memo_mode,
12039            SingleOutcomeMemoMode::Promote
12040        );
12041    }
12042
12043    #[test]
12044    fn clean_empty_multi_alt_outcomes_are_memoized() {
12045        let mut atn = Atn::new(AtnType::Parser, 2);
12046        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
12047        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
12048        atn.add_state(AtnState::new(2, AtnStateKind::RuleStop).with_rule_index(0));
12049        atn.set_rule_to_start_state(vec![0]);
12050        atn.set_rule_to_stop_state(vec![2]);
12051        atn.state_mut(0)
12052            .expect("state 0")
12053            .add_transition(Transition::Epsilon { target: 1 });
12054        atn.state_mut(1)
12055            .expect("state 1")
12056            .add_transition(Transition::Atom {
12057                target: 2,
12058                label: 1,
12059            });
12060        atn.state_mut(1)
12061            .expect("state 1")
12062            .add_transition(Transition::Atom {
12063                target: 2,
12064                label: 2,
12065            });
12066
12067        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
12068        parser.fast_recovery_enabled = false;
12069        let mut visiting = FxHashSet::default();
12070        let mut memo = FxHashMap::default();
12071        let mut expected = ExpectedTokens::default();
12072        let outcomes = parser.recognize_state_fast(
12073            &atn,
12074            FastRecognizeRequest {
12075                state_number: 1,
12076                stop_state: 2,
12077                index: 0,
12078                rule_start_index: 0,
12079                decision_start_index: None,
12080                precedence: 0,
12081                depth: 0,
12082                recovery_symbols: parser.empty_recovery_symbols(),
12083                recovery_state: None,
12084            },
12085            &mut visiting,
12086            &mut memo,
12087            &mut expected,
12088        );
12089
12090        assert!(outcomes.is_empty());
12091        assert_eq!(memo.len(), 1);
12092        assert!(memo.values().next().expect("memo entry").is_empty());
12093    }
12094
12095    #[test]
12096    fn wildcard_matches_non_eof_only() {
12097        let mut parser = mini_parser(vec![
12098            CommonToken::new(1).with_text("x"),
12099            CommonToken::eof("parser-test", 1, 1, 1),
12100        ]);
12101        assert_eq!(parser.match_wildcard().expect("wildcard").text(), "x");
12102        assert!(parser.match_wildcard().is_err());
12103    }
12104
12105    #[test]
12106    fn add_parse_child_records_match_even_without_tree_building() {
12107        // `sync_decision`'s "is the current context empty" flag must reflect real
12108        // matches, not parse-tree children: when `build_parse_trees(false)`,
12109        // `children` stays empty but `has_matched_child` must still flip so nested
12110        // recovery does not wrongly suppress single-token deletion.
12111        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
12112        let token = CommonToken::new(1).with_text("x");
12113
12114        parser.set_build_parse_trees(false);
12115        let mut ctx = ParserRuleContext::new(0, 0);
12116        assert!(!ctx.has_matched_child());
12117        parser.add_parse_child(
12118            &mut ctx,
12119            ParseTree::Terminal(TerminalNode::new(token.clone())),
12120        );
12121        // Tree building is off, so no child is stored...
12122        assert!(ctx.children().is_empty());
12123        // ...but the match is recorded, so the context is no longer "empty".
12124        assert!(ctx.has_matched_child());
12125
12126        // With tree building on, the child is stored and the match is recorded.
12127        parser.set_build_parse_trees(true);
12128        let mut ctx = ParserRuleContext::new(0, 0);
12129        parser.add_parse_child(&mut ctx, ParseTree::Terminal(TerminalNode::new(token)));
12130        assert_eq!(ctx.children().len(), 1);
12131        assert!(ctx.has_matched_child());
12132    }
12133
12134    #[test]
12135    fn parser_interprets_simple_atn_rule() {
12136        let atn = token_then_eof_atn();
12137        let mut parser = mini_parser(vec![
12138            CommonToken::new(1).with_text("x"),
12139            CommonToken::eof("parser-test", 1, 1, 1),
12140        ]);
12141
12142        let tree = parser
12143            .parse_atn_rule(&atn, 0)
12144            .expect("artificial parser rule should parse");
12145        assert_eq!(tree.text(), "x<EOF>");
12146        assert_eq!(parser.number_of_syntax_errors(), 0);
12147        assert_eq!(
12148            tree.first_rule_stop(0)
12149                .expect("rule should stop at EOF")
12150                .token_type(),
12151            TOKEN_EOF
12152        );
12153
12154        let mut parser = mini_parser(vec![
12155            CommonToken::new(1).with_text("x"),
12156            CommonToken::eof("parser-test", 1, 1, 1),
12157        ]);
12158        let (tree, actions) = parser
12159            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12160            .expect("runtime-option parser rule should parse");
12161        assert!(actions.is_empty());
12162        assert_eq!(
12163            tree.first_rule_stop(0)
12164                .expect("rule should stop at EOF")
12165                .token_type(),
12166            TOKEN_EOF
12167        );
12168    }
12169
12170    #[test]
12171    fn runtime_options_default_ignores_noop_action_transitions() {
12172        let atn = noop_action_then_token_then_eof_atn();
12173        let mut parser = mini_parser(vec![
12174            CommonToken::new(1).with_text("x"),
12175            CommonToken::eof("parser-test", 1, 1, 1),
12176        ]);
12177
12178        let (tree, actions) = parser
12179            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12180            .expect("no-op parser action should not force action replay");
12181
12182        assert_eq!(tree.text(), "x<EOF>");
12183        assert!(
12184            actions.is_empty(),
12185            "action_index=None transitions are ANTLR metadata, not replay actions"
12186        );
12187        assert_eq!(parser.number_of_syntax_errors(), 0);
12188    }
12189
12190    #[test]
12191    fn parser_exposes_buffered_token_stream_after_parse() {
12192        let atn = token_then_eof_atn();
12193        let mut parser = mini_parser(vec![
12194            CommonToken::new(1).with_text("x"),
12195            CommonToken::eof("parser-test", 1, 1, 1),
12196        ]);
12197
12198        let tree = parser
12199            .parse_atn_rule(&atn, 0)
12200            .expect("artificial parser rule should parse");
12201        assert_eq!(tree.text(), "x<EOF>");
12202
12203        let stream = parser.token_stream();
12204        let source_index_after_parse = stream.token_source().index;
12205        let buffered = stream.tokens();
12206        assert_eq!(buffered.len(), 2);
12207        assert_eq!(buffered[0].text(), "x");
12208        assert_eq!(buffered[0].token_index(), 0);
12209        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
12210        assert_eq!(stream.token_source().index, source_index_after_parse);
12211
12212        let stream = parser.into_token_stream();
12213        assert_eq!(stream.token_source().index, source_index_after_parse);
12214        assert_eq!(stream.tokens()[0].text(), "x");
12215        assert_eq!(stream.tokens()[1].token_type(), TOKEN_EOF);
12216    }
12217
12218    #[test]
12219    fn parser_syntax_error_count_tracks_interpreted_recovery() {
12220        let atn = token_then_eof_atn();
12221        let mut parser = mini_parser(vec![
12222            CommonToken::new(1).with_text("x"),
12223            CommonToken::new(2).with_text("y"),
12224            CommonToken::eof("parser-test", 2, 1, 2),
12225        ]);
12226
12227        let tree = parser
12228            .parse_atn_rule(&atn, 0)
12229            .expect("invalid token should recover into an error node");
12230
12231        assert_eq!(parser.number_of_syntax_errors(), 1);
12232        assert_eq!(
12233            tree.first_error_token()
12234                .expect("recovery should embed an error token")
12235                .text(),
12236            "y"
12237        );
12238    }
12239
12240    #[test]
12241    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
12242        let atn = token_then_eof_atn();
12243        let mut parser = mini_parser(vec![
12244            CommonToken::new(2).with_text("y"),
12245            CommonToken::eof("parser-test", 1, 1, 1),
12246        ]);
12247
12248        let error = parser
12249            .parse_atn_rule(&atn, 0)
12250            .expect_err("start-rule mismatch should remain a parser error");
12251
12252        assert_eq!(parser.number_of_syntax_errors(), 1);
12253        assert!(matches!(error, AntlrError::ParserError { .. }));
12254    }
12255
12256    #[test]
12257    fn adaptive_direct_rule_uses_simulator_decision() {
12258        let atn = two_alt_decision_atn();
12259        let mut simulator = ParserAtnSimulator::new(&atn);
12260        let mut parser = mini_parser(vec![
12261            CommonToken::new(2).with_text("y"),
12262            CommonToken::eof("parser-test", 1, 1, 1),
12263        ]);
12264
12265        let tree = parser
12266            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
12267            .expect("direct adaptive rule should parse");
12268
12269        assert_eq!(tree.text(), "y");
12270        assert_eq!(parser.input.index(), 1);
12271    }
12272
12273    #[test]
12274    fn adaptive_direct_rule_restores_input_on_fallback() {
12275        let atn = predicate_after_token_atn();
12276        let mut simulator = ParserAtnSimulator::new(&atn);
12277        let mut parser = mini_parser(vec![
12278            CommonToken::new(1).with_text("x"),
12279            CommonToken::new(2).with_text("y"),
12280            CommonToken::eof("parser-test", 2, 1, 2),
12281        ]);
12282
12283        let tree = parser
12284            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
12285            .expect("fallback recognizer should parse");
12286
12287        assert_eq!(tree.text(), "xy");
12288        assert_eq!(parser.input.index(), 2);
12289    }
12290
12291    #[test]
12292    fn unknown_predicate_policy_defaults_to_assume_true() {
12293        let atn = predicate_after_token_atn();
12294        let mut parser = mini_parser(vec![
12295            CommonToken::new(1).with_text("x"),
12296            CommonToken::new(2).with_text("y"),
12297            CommonToken::eof("parser-test", 2, 1, 2),
12298        ]);
12299
12300        let (tree, _) = parser
12301            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12302            .expect("unknown predicate should pass under the default policy");
12303
12304        assert_eq!(tree.text(), "xy");
12305        assert_eq!(parser.number_of_syntax_errors(), 0);
12306    }
12307
12308    #[test]
12309    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
12310        // A generated parent may record an unknown-predicate coordinate, then
12311        // descend into an interpreted child. The child's interpreter entry must
12312        // not wipe the parent's recorded hit before the top-level surfaces it.
12313        let atn = token_then_eof_atn();
12314        let mut parser = mini_parser(vec![
12315            CommonToken::new(1).with_text("x"),
12316            CommonToken::eof("parser-test", 1, 1, 1),
12317        ]);
12318
12319        // Simulate the parent having recorded a fail-loud coordinate.
12320        parser.unknown_predicate_hits.push((7, 3));
12321
12322        // Run an interpreted child parse that records no coordinate of its own.
12323        parser
12324            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12325            .expect("child rule parses");
12326
12327        // The parent's coordinate must still be present for the top-level entry.
12328        let error = parser
12329            .take_unknown_semantic_error()
12330            .expect("parent's recorded coordinate must survive the nested interpreted parse");
12331        let AntlrError::Unsupported(message) = error else {
12332            panic!("expected AntlrError::Unsupported, got {error:?}");
12333        };
12334        assert!(message.contains("pred_index=3"), "message: {message}");
12335    }
12336
12337    #[test]
12338    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
12339        let atn = predicate_after_token_atn();
12340        let mut parser = mini_parser(vec![
12341            CommonToken::new(1).with_text("x"),
12342            CommonToken::new(2).with_text("y"),
12343            CommonToken::eof("parser-test", 2, 1, 2),
12344        ]);
12345
12346        let result = parser.parse_atn_rule_with_runtime_options(
12347            &atn,
12348            0,
12349            ParserRuntimeOptions {
12350                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
12351                ..ParserRuntimeOptions::default()
12352            },
12353        );
12354
12355        assert!(
12356            result.is_err(),
12357            "the only path is predicate-guarded, so assume-false must fail the parse"
12358        );
12359    }
12360
12361    #[test]
12362    fn unknown_predicate_policy_error_names_the_coordinate() {
12363        let atn = predicate_after_token_atn();
12364        let mut parser = mini_parser(vec![
12365            CommonToken::new(1).with_text("x"),
12366            CommonToken::new(2).with_text("y"),
12367            CommonToken::eof("parser-test", 2, 1, 2),
12368        ]);
12369
12370        let error = parser
12371            .parse_atn_rule_with_runtime_options(
12372                &atn,
12373                0,
12374                ParserRuntimeOptions {
12375                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
12376                    ..ParserRuntimeOptions::default()
12377                },
12378            )
12379            .expect_err("evaluating an unknown predicate under Error policy must fail");
12380
12381        let AntlrError::Unsupported(message) = error else {
12382            panic!("expected AntlrError::Unsupported, got {error:?}");
12383        };
12384        assert!(
12385            message.contains("unsupported semantic predicate"),
12386            "message should name the failure class: {message}"
12387        );
12388        assert!(
12389            message.contains("pred_index=0"),
12390            "message should carry the coordinate: {message}"
12391        );
12392    }
12393
12394    #[test]
12395    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
12396        // A parser reused after a fail-loud parse must not carry the old
12397        // coordinates into a later parse. The fail-loud return keeps the hits
12398        // (so a generated parent can surface a recovered child's coordinate),
12399        // and the next parse's entry stashes/replaces them, so a subsequent
12400        // clean parse surfaces no stale error.
12401        let atn = predicate_after_token_atn();
12402        let mut parser = mini_parser(vec![
12403            CommonToken::new(1).with_text("x"),
12404            CommonToken::new(2).with_text("y"),
12405            CommonToken::eof("parser-test", 2, 1, 2),
12406        ]);
12407
12408        parser
12409            .parse_atn_rule_with_runtime_options(
12410                &atn,
12411                0,
12412                ParserRuntimeOptions {
12413                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
12414                    ..ParserRuntimeOptions::default()
12415                },
12416            )
12417            .expect_err("first parse fails loud under the Error policy");
12418
12419        // The failed parse kept its coordinate on the parser (so a generated
12420        // parent could surface a recovered child). A top-level reuse resets the
12421        // hits — generated parsers call `reset_unknown_semantic_hits` at their
12422        // public entry; direct interpreter-API callers do the same.
12423        parser.reset_unknown_semantic_hits();
12424        assert!(
12425            parser.take_unknown_semantic_error().is_none(),
12426            "reset must drop stale unknown-predicate coordinates before a reused parse"
12427        );
12428    }
12429
12430    #[derive(Debug, Default)]
12431    struct RecordingHooks {
12432        predicates: Vec<(usize, usize, usize, Option<String>)>,
12433        actions: Vec<(usize, String, Option<String>)>,
12434    }
12435
12436    impl SemanticHooks for RecordingHooks {
12437        fn sempred<S>(
12438            &mut self,
12439            ctx: &mut ParserSemCtx<'_, S>,
12440            rule_index: usize,
12441            pred_index: usize,
12442        ) -> Option<bool>
12443        where
12444            S: TokenSource,
12445        {
12446            self.predicates.push((
12447                ctx.input_index(),
12448                rule_index,
12449                pred_index,
12450                ctx.token_text(1).map(str::to_owned),
12451            ));
12452            Some(true)
12453        }
12454
12455        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
12456        where
12457            S: TokenSource,
12458        {
12459            self.actions.push((
12460                action.source_state(),
12461                ctx.action_text(),
12462                ctx.rule_name().map(str::to_owned),
12463            ));
12464            true
12465        }
12466    }
12467
12468    #[derive(Debug, Default)]
12469    struct RejectingPredicateHooks {
12470        predicates: Vec<(usize, usize, usize, Option<String>)>,
12471    }
12472
12473    impl SemanticHooks for RejectingPredicateHooks {
12474        fn sempred<S>(
12475            &mut self,
12476            ctx: &mut ParserSemCtx<'_, S>,
12477            rule_index: usize,
12478            pred_index: usize,
12479        ) -> Option<bool>
12480        where
12481            S: TokenSource,
12482        {
12483            self.predicates.push((
12484                ctx.input_index(),
12485                rule_index,
12486                pred_index,
12487                ctx.token_text(1).map(str::to_owned),
12488            ));
12489            Some(false)
12490        }
12491    }
12492
12493    #[test]
12494    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
12495        let atn = predicate_after_token_atn();
12496        let mut parser = mini_parser_with_hooks(
12497            vec![
12498                CommonToken::new(1).with_text("x"),
12499                CommonToken::new(2).with_text("y"),
12500                CommonToken::eof("parser-test", 2, 1, 2),
12501            ],
12502            RecordingHooks::default(),
12503        );
12504
12505        let (tree, _) = parser
12506            .parse_atn_rule_with_runtime_options(
12507                &atn,
12508                0,
12509                ParserRuntimeOptions {
12510                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
12511                    ..ParserRuntimeOptions::default()
12512                },
12513            )
12514            .expect("hook supplies the missing predicate result");
12515
12516        assert_eq!(tree.text(), "xy");
12517        assert_eq!(
12518            parser.semantic_hooks.predicates,
12519            vec![(1, 0, 0, Some("y".to_owned()))]
12520        );
12521    }
12522
12523    #[test]
12524    fn runtime_options_default_preserves_semantic_hook_predicates() {
12525        let atn = predicate_after_token_atn();
12526        let mut parser = mini_parser_with_hooks(
12527            vec![
12528                CommonToken::new(1).with_text("x"),
12529                CommonToken::new(2).with_text("y"),
12530                CommonToken::eof("parser-test", 2, 1, 2),
12531            ],
12532            RejectingPredicateHooks::default(),
12533        );
12534
12535        let result =
12536            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
12537
12538        assert!(
12539            result.is_err(),
12540            "default runtime options must not bypass semantic hooks for predicate ATNs"
12541        );
12542        assert_eq!(
12543            parser.semantic_hooks.predicates,
12544            vec![(1, 0, 0, Some("y".to_owned()))]
12545        );
12546    }
12547
12548    #[test]
12549    fn semantic_hook_handles_committed_parser_action() {
12550        let atn = token_then_eof_atn();
12551        let mut parser = mini_parser_with_hooks(
12552            vec![
12553                CommonToken::new(1).with_text("x"),
12554                CommonToken::eof("parser-test", 1, 1, 1),
12555            ],
12556            RecordingHooks::default(),
12557        );
12558        let (tree, _) = parser
12559            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12560            .expect("rule parses before action hook is tested");
12561
12562        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
12563        assert_eq!(
12564            parser.semantic_hooks.actions,
12565            vec![(42, "x".to_owned(), Some("s".to_owned()))]
12566        );
12567    }
12568
12569    #[test]
12570    fn unhandled_committed_action_fails_loud_under_error_policy() {
12571        // An action offered to the hook that no hook handles (returns false)
12572        // must be recorded and surfaced as `AntlrError::Unsupported` under the
12573        // Error policy, so a `hook`-disposed action is not silently dropped.
12574        let mut parser =
12575            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
12576        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
12577        let tree = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, -1)));
12578
12579        // DecliningHooks::action returns false (unhandled).
12580        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
12581
12582        let error = parser
12583            .take_unknown_semantic_error()
12584            .expect("an unhandled committed action under Error policy must fail loud");
12585        let AntlrError::Unsupported(message) = error else {
12586            panic!("expected AntlrError::Unsupported, got {error:?}");
12587        };
12588        assert!(
12589            message.contains("unhandled semantic action") && message.contains("state=42"),
12590            "message should name the dropped action coordinate: {message}"
12591        );
12592
12593        // Under the default (assume-true) policy the same miss is not recorded.
12594        let mut lenient =
12595            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
12596        let tree = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, -1)));
12597        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
12598        assert!(lenient.take_unknown_semantic_error().is_none());
12599    }
12600
12601    #[test]
12602    fn translated_predicate_is_unaffected_by_error_policy() {
12603        let atn = predicate_after_token_atn();
12604        let mut parser = mini_parser(vec![
12605            CommonToken::new(1).with_text("x"),
12606            CommonToken::new(2).with_text("y"),
12607            CommonToken::eof("parser-test", 2, 1, 2),
12608        ]);
12609
12610        let (tree, _) = parser
12611            .parse_atn_rule_with_runtime_options(
12612                &atn,
12613                0,
12614                ParserRuntimeOptions {
12615                    predicates: &[(0, 0, ParserPredicate::True)],
12616                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
12617                    ..ParserRuntimeOptions::default()
12618                },
12619            )
12620            .expect("a predicate covered by the table is not an unknown coordinate");
12621
12622        assert_eq!(tree.text(), "xy");
12623    }
12624
12625    /// Hooks that decline (`None`) must fall through to the configured policy
12626    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
12627    /// legacy table path. Regression for the `unwrap_or(false)` that silently
12628    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
12629    fn hook_predicate_semantics() -> ParserSemantics {
12630        let mut ir = SemIr::new();
12631        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
12632        ParserSemantics {
12633            ir,
12634            predicates: vec![ParserSemanticPredicate {
12635                rule_index: 0,
12636                pred_index: 0,
12637                expr,
12638                failure_message: None,
12639            }],
12640            actions: Vec::new(),
12641        }
12642    }
12643
12644    #[derive(Debug, Default)]
12645    struct DecliningHooks;
12646
12647    impl SemanticHooks for DecliningHooks {}
12648
12649    #[test]
12650    fn semir_hook_none_falls_through_to_assume_true() {
12651        let atn = predicate_after_token_atn();
12652        let semantics = hook_predicate_semantics();
12653        let mut parser = mini_parser_with_hooks(
12654            vec![
12655                CommonToken::new(1).with_text("x"),
12656                CommonToken::new(2).with_text("y"),
12657                CommonToken::eof("parser-test", 2, 1, 2),
12658            ],
12659            DecliningHooks,
12660        );
12661
12662        let (tree, _) = parser
12663            .parse_atn_rule_with_runtime_options(
12664                &atn,
12665                0,
12666                ParserRuntimeOptions {
12667                    semantics: Some(&semantics),
12668                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
12669                    ..ParserRuntimeOptions::default()
12670                },
12671            )
12672            .expect("a declined SemIR hook must pass under assume-true");
12673
12674        assert_eq!(tree.text(), "xy");
12675    }
12676
12677    #[test]
12678    fn semir_hook_none_falls_through_to_assume_false() {
12679        let atn = predicate_after_token_atn();
12680        let semantics = hook_predicate_semantics();
12681        let mut parser = mini_parser_with_hooks(
12682            vec![
12683                CommonToken::new(1).with_text("x"),
12684                CommonToken::new(2).with_text("y"),
12685                CommonToken::eof("parser-test", 2, 1, 2),
12686            ],
12687            DecliningHooks,
12688        );
12689
12690        let result = parser.parse_atn_rule_with_runtime_options(
12691            &atn,
12692            0,
12693            ParserRuntimeOptions {
12694                semantics: Some(&semantics),
12695                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
12696                ..ParserRuntimeOptions::default()
12697            },
12698        );
12699
12700        assert!(
12701            result.is_err(),
12702            "a declined SemIR hook must fail the only guarded path under assume-false"
12703        );
12704    }
12705
12706    #[test]
12707    fn semir_hook_none_records_coordinate_under_error_policy() {
12708        let atn = predicate_after_token_atn();
12709        let semantics = hook_predicate_semantics();
12710        let mut parser = mini_parser_with_hooks(
12711            vec![
12712                CommonToken::new(1).with_text("x"),
12713                CommonToken::new(2).with_text("y"),
12714                CommonToken::eof("parser-test", 2, 1, 2),
12715            ],
12716            DecliningHooks,
12717        );
12718
12719        let error = parser
12720            .parse_atn_rule_with_runtime_options(
12721                &atn,
12722                0,
12723                ParserRuntimeOptions {
12724                    semantics: Some(&semantics),
12725                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
12726                    ..ParserRuntimeOptions::default()
12727                },
12728            )
12729            .expect_err("a declined SemIR hook under Error policy must fail the parse");
12730
12731        let AntlrError::Unsupported(message) = error else {
12732            panic!("expected AntlrError::Unsupported, got {error:?}");
12733        };
12734        assert!(
12735            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
12736            "message should name the unresolved coordinate: {message}"
12737        );
12738    }
12739
12740    #[test]
12741    fn generated_direct_predicate_honors_installed_policy() {
12742        // The generated recursive-descent path calls
12743        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
12744        // going through `ParserRuntimeOptions`, so the policy must be installed
12745        // via `set_unknown_predicate_policy` (as the generated constructor now
12746        // does). A declining hook must then honor it rather than the default.
12747        let semantics = hook_predicate_semantics();
12748        let context = ParserRuleContext::new(0, -1);
12749
12750        let mut assume_true =
12751            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
12752        assert!(
12753            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
12754                &semantics, 0, 0, &context, 0
12755            ),
12756            "default AssumeTrue accepts a declined hook"
12757        );
12758        assert!(assume_true.take_unknown_semantic_error().is_none());
12759
12760        let mut error_policy =
12761            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
12762        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
12763        assert!(
12764            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
12765                &semantics, 0, 0, &context, 0
12766            ),
12767            "Error policy rejects a declined hook on the generated-direct path"
12768        );
12769        let error = error_policy
12770            .take_unknown_semantic_error()
12771            .expect("Error policy records the unresolved coordinate for the generated path");
12772        let AntlrError::Unsupported(message) = error else {
12773            panic!("expected AntlrError::Unsupported, got {error:?}");
12774        };
12775        assert!(message.contains("pred_index=0"), "message: {message}");
12776    }
12777
12778    #[test]
12779    fn parser_rule_start_skips_leading_hidden_tokens() {
12780        let atn = token_then_eof_atn();
12781        let mut parser = mini_parser(vec![
12782            CommonToken::new(99)
12783                .with_text(" ")
12784                .with_channel(HIDDEN_CHANNEL),
12785            CommonToken::new(1).with_text("x"),
12786            CommonToken::eof("parser-test", 2, 1, 2),
12787        ]);
12788
12789        let tree = parser
12790            .parse_atn_rule(&atn, 0)
12791            .expect("artificial parser rule should parse");
12792        let Some(ParseTree::Rule(rule)) = tree.first_rule(0) else {
12793            panic!("rule node should be present");
12794        };
12795        assert_eq!(
12796            rule.context()
12797                .start()
12798                .expect("rule should have a start token")
12799                .token_type(),
12800            1
12801        );
12802    }
12803
12804    #[test]
12805    fn parser_action_after_eof_stops_at_eof_token() {
12806        let atn = eof_then_action_atn();
12807        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
12808
12809        let (_, actions) = parser
12810            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
12811            .expect("EOF action rule should parse");
12812
12813        assert_eq!(actions.len(), 1);
12814        assert_eq!(actions[0].stop_index(), Some(0));
12815        assert_eq!(
12816            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
12817            ""
12818        );
12819    }
12820
12821    #[test]
12822    fn after_action_stop_uses_rule_context_stop_not_cursor() {
12823        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
12824        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
12825        // but the rule did not consume it. The @after stop must follow the rule
12826        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
12827        let mut id = CommonToken::new(1).with_text("x");
12828        id.set_token_index(0);
12829        let mut eof = CommonToken::eof("parser-test", 1, 1, 1);
12830        eof.set_token_index(1);
12831        let mut parser = mini_parser(vec![id.clone(), eof]);
12832        // Advance the cursor onto EOF, as it would be after `a` matched ID.
12833        parser.consume();
12834        assert_eq!(parser.la(1), TOKEN_EOF);
12835
12836        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
12837        // exactly what finish_rule(consumed_eof = false) records.
12838        let mut ctx = ParserRuleContext::new(0, 0);
12839        ctx.set_stop(id);
12840        let tree = ParseTree::Rule(RuleNode::new(ctx));
12841
12842        let current_index = parser.input.index();
12843        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
12844        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
12845        // ...but the tree-aware helper follows the rule context stop (ID).
12846        assert_eq!(
12847            parser.after_action_stop_index_for_tree(&tree, current_index),
12848            Some(0)
12849        );
12850    }
12851
12852    #[test]
12853    fn after_action_start_uses_rule_context_start_not_cursor() {
12854        // A rule that begins after leading hidden-channel tokens: the rule context
12855        // start (set by `enter_rule`) is the first visible token, not the raw cursor
12856        // that may still point at the hidden prefix. The @after start must follow
12857        // the context start so `$start`/`$text` excludes the hidden prefix.
12858        let parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
12859        let mut id = CommonToken::new(1).with_text("x");
12860        // The first visible token sits at stream index 2 (after two hidden tokens).
12861        id.set_token_index(2);
12862
12863        let mut ctx = ParserRuleContext::new(0, 0);
12864        ctx.set_start(id);
12865        let tree = ParseTree::Rule(RuleNode::new(ctx));
12866
12867        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
12868        // ...but the tree-aware helper follows the rule context start (index 2).
12869        assert_eq!(parser.after_action_start_index_for_tree(&tree, 0), 2);
12870
12871        // With no rule start recorded, it falls back to the provided index.
12872        let empty = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, 0)));
12873        assert_eq!(parser.after_action_start_index_for_tree(&empty, 7), 7);
12874    }
12875
12876    #[test]
12877    fn fast_outcome_selection_respects_sll_tie_order() {
12878        let first = FastRecognizeOutcome {
12879            index: 1,
12880            consumed_eof: false,
12881            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
12882                line: 1,
12883                column: 0,
12884                message: "mismatched input 'x'".to_owned(),
12885            }]),
12886            nodes: NodeList::new(),
12887        };
12888        let second = FastRecognizeOutcome {
12889            index: first.index,
12890            consumed_eof: first.consumed_eof,
12891            diagnostics: FastDiagnostics::new(),
12892            nodes: NodeList::new(),
12893        };
12894
12895        let selected = select_best_fast_outcome(
12896            [first.clone(), second.clone()].into_iter(),
12897            PredictionMode::Sll,
12898            None,
12899            |_| panic!("caller-follow token probe should not run"),
12900        )
12901        .expect("one outcome should be selected");
12902        assert_eq!(selected.diagnostics.len(), 1);
12903        let eof_second = FastRecognizeOutcome {
12904            index: second.index,
12905            consumed_eof: true,
12906            diagnostics: FastDiagnostics::new(),
12907            nodes: NodeList::new(),
12908        };
12909        let selected = select_best_fast_outcome(
12910            [first.clone(), eof_second].into_iter(),
12911            PredictionMode::Sll,
12912            None,
12913            |_| panic!("caller-follow token probe should not run"),
12914        )
12915        .expect("one outcome should be selected");
12916        assert!(!selected.consumed_eof);
12917        let selected = select_best_fast_outcome(
12918            [first, second].into_iter(),
12919            PredictionMode::Ll,
12920            None,
12921            |_| panic!("caller-follow token probe should not run"),
12922        )
12923        .expect("one outcome should be selected");
12924        assert!(selected.diagnostics.is_empty());
12925    }
12926
12927    #[test]
12928    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
12929        let first = FastRecognizeOutcome {
12930            index: 3,
12931            consumed_eof: false,
12932            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
12933                line: 1,
12934                column: 0,
12935                message: "mismatched input 'x' expecting 'a'".to_owned(),
12936            }]),
12937            nodes: NodeList::new(),
12938        };
12939        let same_rank = FastRecognizeOutcome {
12940            index: first.index,
12941            consumed_eof: first.consumed_eof,
12942            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
12943                line: 1,
12944                column: 0,
12945                message: "mismatched input 'x' expecting 'b'".to_owned(),
12946            }]),
12947            nodes: NodeList::new(),
12948        };
12949        let better_rank = FastRecognizeOutcome {
12950            index: first.index,
12951            consumed_eof: first.consumed_eof,
12952            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
12953                line: 1,
12954                column: 0,
12955                message: "missing 'a' at 'x'".to_owned(),
12956            }]),
12957            nodes: NodeList::new(),
12958        };
12959        let mut outcomes = vec![first, same_rank, better_rank];
12960
12961        dedupe_fast_outcomes(&mut outcomes);
12962
12963        assert_eq!(outcomes.len(), 2);
12964        assert_eq!(
12965            outcomes[0].diagnostics[0].message,
12966            "mismatched input 'x' expecting 'a'"
12967        );
12968        assert_eq!(outcomes[1].diagnostics[0].message, "missing 'a' at 'x'");
12969    }
12970
12971    #[test]
12972    fn fast_outcome_selection_prefers_generated_caller_follow() {
12973        let earlier = FastRecognizeOutcome {
12974            index: 7,
12975            consumed_eof: false,
12976            diagnostics: FastDiagnostics::new(),
12977            nodes: NodeList::new(),
12978        };
12979        let later = FastRecognizeOutcome {
12980            index: 8,
12981            consumed_eof: false,
12982            diagnostics: FastDiagnostics::new(),
12983            nodes: NodeList::new(),
12984        };
12985        let mut follow = TokenBitSet::default();
12986        follow.insert(5);
12987
12988        let selected = select_best_fast_outcome(
12989            [later.clone(), earlier.clone()].into_iter(),
12990            PredictionMode::Ll,
12991            Some(&follow),
12992            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
12993        )
12994        .expect("one outcome should be selected");
12995        assert_eq!(selected.index, 7);
12996
12997        let selected = select_best_fast_outcome(
12998            [later.clone(), earlier.clone()].into_iter(),
12999            PredictionMode::Ll,
13000            Some(&follow),
13001            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
13002        )
13003        .expect("one outcome should be selected");
13004        assert_eq!(selected.index, 8);
13005
13006        let indented_next_statement = FastRecognizeOutcome {
13007            index: 9,
13008            consumed_eof: false,
13009            diagnostics: FastDiagnostics::new(),
13010            nodes: NodeList::new(),
13011        };
13012        let selected = select_best_fast_outcome(
13013            [indented_next_statement, earlier.clone()].into_iter(),
13014            PredictionMode::Ll,
13015            Some(&follow),
13016            |index| {
13017                let is_boundary = index == 7;
13018                let is_boundary_gap = matches!(index, 7 | 8);
13019                (
13020                    if index == 7 { 5 } else { TOKEN_EOF },
13021                    is_boundary,
13022                    is_boundary_gap,
13023                )
13024            },
13025        )
13026        .expect("one outcome should be selected");
13027        assert_eq!(selected.index, 7);
13028
13029        let continuation = FastRecognizeOutcome {
13030            index: 10,
13031            consumed_eof: false,
13032            diagnostics: FastDiagnostics::new(),
13033            nodes: NodeList::new(),
13034        };
13035        let selected = select_best_fast_outcome(
13036            [continuation, earlier.clone()].into_iter(),
13037            PredictionMode::Ll,
13038            Some(&follow),
13039            |index| {
13040                let is_boundary = matches!(index, 7 | 9);
13041                (
13042                    if index == 7 { 5 } else { TOKEN_EOF },
13043                    is_boundary,
13044                    is_boundary,
13045                )
13046            },
13047        )
13048        .expect("one outcome should be selected");
13049        assert_eq!(selected.index, 10);
13050
13051        let selected = select_best_fast_outcome(
13052            [earlier, later].into_iter(),
13053            PredictionMode::Sll,
13054            Some(&follow),
13055            |_| panic!("caller-follow token probe should not run in SLL mode"),
13056        )
13057        .expect("one outcome should be selected");
13058        assert_eq!(selected.index, 8);
13059    }
13060
13061    #[test]
13062    fn caller_follow_boundary_text_requires_separator_shape() {
13063        assert!(is_caller_follow_boundary_text(";"));
13064        assert!(is_caller_follow_boundary_text("\n"));
13065        assert!(is_caller_follow_boundary_text("\r\n  "));
13066        assert!(is_caller_follow_boundary_text(";\n"));
13067        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
13068        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
13069        assert!(!is_caller_follow_boundary_text("identifier"));
13070        assert!(is_caller_follow_boundary_gap_text(" \t "));
13071        assert!(is_caller_follow_boundary_gap_text("\n  "));
13072        assert!(is_caller_follow_boundary_gap_text(";\t"));
13073        assert!(!is_caller_follow_boundary_gap_text(
13074            "\"\"\"line1\nline2\"\"\""
13075        ));
13076        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
13077    }
13078
13079    #[test]
13080    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
13081        let mut parser = mini_parser(vec![
13082            CommonToken::new(5).with_text("\n"),
13083            CommonToken::new(6)
13084                .with_text("// comment\n")
13085                .with_channel(HIDDEN_CHANNEL),
13086            CommonToken::new(1).with_text("x"),
13087            CommonToken::eof("parser-test", 1, 2, 0),
13088        ]);
13089
13090        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
13091        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
13092        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
13093    }
13094
13095    #[test]
13096    fn caller_follow_token_info_uses_stream_visible_channel() {
13097        let source = Source {
13098            tokens: vec![
13099                CommonToken::new(5).with_text("\n").with_channel(2),
13100                CommonToken::new(1).with_text("x").with_channel(2),
13101                CommonToken::new(6)
13102                    .with_text("// comment\n")
13103                    .with_channel(HIDDEN_CHANNEL),
13104                CommonToken::eof("parser-test", 1, 2, 0),
13105            ],
13106            index: 0,
13107        };
13108        let data = RecognizerData::new(
13109            "Mini.g4",
13110            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13111        );
13112        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
13113
13114        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
13115        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
13116        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
13117    }
13118
13119    #[test]
13120    fn reset_per_parse_caches_clears_state_expected_token_cache() {
13121        let atn = token_then_eof_atn();
13122        let mut parser = mini_parser(Vec::new());
13123
13124        let _ = parser.cached_state_expected_token_set(&atn, 0);
13125        assert!(!parser.state_expected_token_cache.is_empty());
13126
13127        parser.reset_per_parse_caches();
13128        assert!(parser.state_expected_token_cache.is_empty());
13129    }
13130
13131    #[test]
13132    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
13133        let source = Source {
13134            tokens: vec![
13135                CommonToken::new(1).with_text("x"),
13136                CommonToken::eof("parser-test", 1, 1, 1),
13137            ],
13138            index: 0,
13139        };
13140        let data = RecognizerData::new(
13141            "Mini.g4",
13142            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13143        );
13144        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
13145        let expected = ExpectedTokens {
13146            index: Some(0),
13147            symbols: BTreeSet::new(),
13148            no_viable: None,
13149        };
13150
13151        let (_, message) = parser.expected_error_message(0, 0, &expected);
13152
13153        assert_eq!(message, "mismatched input 'x'");
13154    }
13155
13156    #[test]
13157    fn eof_rule_stop_index_points_at_eof_token() {
13158        let source = Source {
13159            tokens: vec![
13160                CommonToken::new(1).with_text("x"),
13161                CommonToken::eof("parser-test", 1, 1, 1),
13162            ],
13163            index: 0,
13164        };
13165        let data = RecognizerData::new(
13166            "Mini.g4",
13167            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13168        );
13169        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
13170
13171        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
13172        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
13173    }
13174
13175    #[test]
13176    fn generated_parser_action_uses_current_rule_stop_boundary() {
13177        let mut parser = mini_parser(vec![
13178            CommonToken::new(1).with_text("x"),
13179            CommonToken::eof("parser-test", 1, 1, 1),
13180        ]);
13181
13182        parser.match_token(1).expect("token should match");
13183        let action = parser.parser_action_at_current(7, 0, 0, false);
13184        assert_eq!(action.source_state(), 7);
13185        assert_eq!(action.rule_index(), 0);
13186        assert_eq!(action.start_index(), 0);
13187        assert_eq!(action.stop_index(), Some(0));
13188
13189        parser.match_eof().expect("EOF should match");
13190        let action = parser.parser_action_at_current(8, 0, 0, true);
13191        assert_eq!(action.stop_index(), Some(1));
13192    }
13193
13194    #[test]
13195    fn folds_left_recursive_boundary_into_rule_node() {
13196        let nodes = fold_left_recursive_boundaries(vec![
13197            RecognizedNode::Token { index: 0 },
13198            RecognizedNode::LeftRecursiveBoundary { rule_index: 1 },
13199            RecognizedNode::Token { index: 1 },
13200        ]);
13201
13202        assert_eq!(
13203            nodes,
13204            vec![
13205                RecognizedNode::Rule {
13206                    rule_index: 1,
13207                    invoking_state: -1,
13208                    alt_number: 0,
13209                    start_index: 0,
13210                    stop_index: Some(0),
13211                    return_values: BTreeMap::new(),
13212                    children: vec![RecognizedNode::Token { index: 0 }],
13213                },
13214                RecognizedNode::Token { index: 1 },
13215            ]
13216        );
13217    }
13218
13219    #[test]
13220    fn outcome_ties_keep_later_non_recursive_alternative() {
13221        let first = RecognizeOutcome {
13222            index: 1,
13223            consumed_eof: false,
13224            alt_number: 0,
13225            member_values: BTreeMap::new(),
13226            return_values: BTreeMap::new(),
13227            diagnostics: Vec::new(),
13228            decisions: Vec::new(),
13229            actions: vec![ParserAction::new(1, 0, 0, None)],
13230            nodes: vec![RecognizedNode::Token { index: 0 }],
13231        };
13232        let second = RecognizeOutcome {
13233            actions: vec![ParserAction::new(2, 0, 0, None)],
13234            ..first.clone()
13235        };
13236
13237        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
13238            .expect("one outcome should be selected");
13239        assert_eq!(selected.actions[0].source_state(), 2);
13240    }
13241
13242    #[test]
13243    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
13244        let first = RecognizeOutcome {
13245            index: 1,
13246            consumed_eof: false,
13247            alt_number: 0,
13248            member_values: BTreeMap::new(),
13249            return_values: BTreeMap::new(),
13250            diagnostics: Vec::new(),
13251            decisions: Vec::new(),
13252            actions: vec![ParserAction::new(1, 0, 0, None)],
13253            nodes: vec![RecognizedNode::Token { index: 0 }],
13254        };
13255        let second = RecognizeOutcome {
13256            actions: vec![
13257                ParserAction::new(2, 0, 0, None),
13258                ParserAction::new(3, 0, 0, None),
13259            ],
13260            ..first.clone()
13261        };
13262
13263        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll)
13264            .expect("one outcome should be selected");
13265        assert_eq!(selected.actions.len(), 2);
13266    }
13267
13268    #[test]
13269    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
13270        let first = RecognizeOutcome {
13271            index: 7,
13272            consumed_eof: false,
13273            alt_number: 0,
13274            member_values: BTreeMap::new(),
13275            return_values: BTreeMap::new(),
13276            diagnostics: Vec::new(),
13277            decisions: vec![1, 0],
13278            actions: vec![
13279                ParserAction::new(23, 2, 2, Some(4)),
13280                ParserAction::new(23, 2, 0, Some(6)),
13281            ],
13282            nodes: vec![RecognizedNode::Token { index: 0 }],
13283        };
13284        let second = RecognizeOutcome {
13285            decisions: vec![0, 1],
13286            actions: vec![
13287                ParserAction::new(23, 2, 2, Some(6)),
13288                ParserAction::new(23, 2, 0, Some(6)),
13289            ],
13290            ..first.clone()
13291        };
13292
13293        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
13294            .expect("one outcome should be selected");
13295        assert_eq!(selected.actions[0].stop_index(), Some(6));
13296    }
13297
13298    #[test]
13299    fn outcome_ties_keep_first_recursive_tree_shape() {
13300        let recursive_nodes = vec![RecognizedNode::Rule {
13301            rule_index: 1,
13302            invoking_state: -1,
13303            alt_number: 0,
13304            start_index: 0,
13305            stop_index: Some(0),
13306            return_values: BTreeMap::new(),
13307            children: vec![RecognizedNode::Rule {
13308                rule_index: 1,
13309                invoking_state: -1,
13310                alt_number: 0,
13311                start_index: 0,
13312                stop_index: Some(0),
13313                return_values: BTreeMap::new(),
13314                children: vec![RecognizedNode::Token { index: 0 }],
13315            }],
13316        }];
13317        let first = RecognizeOutcome {
13318            index: 1,
13319            consumed_eof: false,
13320            alt_number: 0,
13321            member_values: BTreeMap::new(),
13322            return_values: BTreeMap::new(),
13323            diagnostics: Vec::new(),
13324            decisions: Vec::new(),
13325            actions: vec![ParserAction::new(1, 0, 0, None)],
13326            nodes: recursive_nodes.clone(),
13327        };
13328        let second = RecognizeOutcome {
13329            index: 1,
13330            consumed_eof: false,
13331            alt_number: 0,
13332            member_values: BTreeMap::new(),
13333            return_values: BTreeMap::new(),
13334            diagnostics: Vec::new(),
13335            decisions: Vec::new(),
13336            actions: vec![ParserAction::new(2, 0, 0, None)],
13337            nodes: recursive_nodes,
13338        };
13339
13340        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
13341            .expect("one outcome should be selected");
13342        assert_eq!(selected.actions[0].source_state(), 1);
13343    }
13344
13345    #[test]
13346    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
13347        let first_alt = RecognizeOutcome {
13348            index: 2,
13349            consumed_eof: true,
13350            alt_number: 0,
13351            member_values: BTreeMap::new(),
13352            return_values: BTreeMap::new(),
13353            diagnostics: vec![ParserDiagnostic {
13354                line: 1,
13355                column: 3,
13356                message: "missing 'Y' at '<EOF>'".to_owned(),
13357            }],
13358            decisions: vec![0],
13359            actions: vec![ParserAction::new(1, 0, 0, None)],
13360            nodes: vec![RecognizedNode::Token { index: 0 }],
13361        };
13362        let second_alt = RecognizeOutcome {
13363            diagnostics: Vec::new(),
13364            decisions: vec![1],
13365            actions: vec![ParserAction::new(2, 0, 0, None)],
13366            ..first_alt.clone()
13367        };
13368
13369        let selected =
13370            select_best_outcome([second_alt, first_alt].into_iter(), PredictionMode::Sll)
13371                .expect("one outcome should be selected");
13372        assert_eq!(selected.diagnostics.len(), 1);
13373        assert_eq!(selected.decisions, [0]);
13374    }
13375}