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