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        let needs_tree_retry = matches!(
4657            &first_pass,
4658            Ok((outcome, _)) if self.build_parse_trees && outcome.nodes.has_left_recursive_boundary()
4659        );
4660        let needs_retry = match &first_pass {
4661            // The FIRST-set prefilter trims speculative rule calls that can't
4662            // match the current lookahead — useful for perf on grammars with
4663            // many epsilon-reachable rules, but the trim also bypasses
4664            // single-token insertion / deletion recovery that ANTLR's
4665            // reference parser runs at the child rule's first consuming
4666            // transition. Retry without the prefilter whenever the first pass
4667            // either produced no outcome at all or produced a recovered
4668            // outcome (diagnostics non-empty), since the second pass might
4669            // surface a child-level recovery with cleaner diagnostics or
4670            // closer parity to ANTLR's tree shape. Left-recursive tree
4671            // boundaries also need the token-node pass; otherwise the fold has
4672            // no concrete left operand to wrap into ANTLR's recursive context.
4673            Err(_) => true,
4674            Ok((outcome, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
4675        };
4676        let (outcome, _expected) = if needs_retry {
4677            self.fast_first_set_prefilter = false;
4678            self.fast_recovery_enabled = false;
4679            let clean_retry = self.fast_recognize_top(atn, top_request);
4680            let clean_selected = if needs_tree_retry {
4681                match clean_retry {
4682                    ok @ Ok(_) => ok,
4683                    Err(_) => first_pass,
4684                }
4685            } else {
4686                select_better_top_outcome(first_pass, clean_retry)
4687            };
4688            let selected = if clean_selected.is_err()
4689                || matches!(&clean_selected, Ok((outcome, _)) if !outcome.diagnostics.is_empty())
4690            {
4691                self.fast_recovery_enabled = true;
4692                let recovery_retry = self.fast_recognize_top(atn, top_request);
4693                select_better_top_outcome(clean_selected, recovery_retry)
4694            } else {
4695                clean_selected
4696            };
4697            self.fast_first_set_prefilter = true;
4698            self.fast_recovery_enabled = true;
4699            selected.map_err(|expected| {
4700                let error = self.recognition_error(rule_index, start_index, &expected);
4701                self.record_syntax_errors(1);
4702                report_token_source_errors(&self.input.drain_source_errors());
4703                error
4704            })?
4705        } else {
4706            first_pass.expect("first_pass is Ok in the no-retry branch")
4707        };
4708        self.record_syntax_errors(outcome.diagnostics.len());
4709        report_parser_diagnostics(&self.prediction_diagnostics);
4710        report_parser_diagnostics(&outcome.diagnostics);
4711        report_token_source_errors(&self.input.drain_source_errors());
4712        let mut context = ParserRuleContext::with_child_capacity(
4713            rule_index,
4714            self.state(),
4715            if self.build_parse_trees {
4716                outcome.nodes.len()
4717            } else {
4718                0
4719            },
4720        );
4721        if let Some(token) = self.token_ref_at(start_index) {
4722            context.set_start_ref(token);
4723        }
4724        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
4725        if let Some(token) = stop_index.and_then(|token_index| self.token_ref_at(token_index)) {
4726            context.set_stop_ref(token);
4727        }
4728        if self.build_parse_trees {
4729            if outcome.nodes.has_left_recursive_boundary() {
4730                let folded = fold_fast_left_recursive_boundaries(outcome.nodes.to_vec());
4731                if folded.iter().any(|node| {
4732                    matches!(
4733                        node.as_ref(),
4734                        FastRecognizedNode::Token { .. }
4735                            | FastRecognizedNode::ErrorToken { .. }
4736                            | FastRecognizedNode::MissingToken { .. }
4737                    )
4738                }) {
4739                    for node in &folded {
4740                        context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
4741                    }
4742                } else {
4743                    self.add_fast_implicit_token_children(
4744                        &mut context,
4745                        start_index,
4746                        stop_index,
4747                        &folded,
4748                    )?;
4749                }
4750            } else if outcome.nodes.has_explicit_token_node() {
4751                for node in outcome.nodes.iter() {
4752                    context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
4753                }
4754            } else {
4755                self.add_fast_implicit_token_children_iter(
4756                    &mut context,
4757                    start_index,
4758                    stop_index,
4759                    outcome.nodes.iter(),
4760                )?;
4761            }
4762        }
4763        self.input.seek(outcome.index);
4764
4765        Ok(self.rule_node(context))
4766    }
4767
4768    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
4769        let invoking_state = self.pending_invoking_states.last().copied()?;
4770        let state_number = usize::try_from(invoking_state).ok()?;
4771        match atn.state(state_number)?.transitions.first()? {
4772            Transition::Rule { follow_state, .. } => Some(*follow_state),
4773            _ => None,
4774        }
4775    }
4776
4777    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
4778        // Generated callers own statement separators; leave them available when
4779        // an interpreted child rule can either stop before or consume one.
4780        let token_type = self.token_type_at(index);
4781        let visible_channel = self.input.channel();
4782        let token = self.token_at(index);
4783        let is_boundary = token
4784            .as_ref()
4785            .and_then(Token::text)
4786            .is_some_and(is_caller_follow_boundary_text);
4787        let is_boundary_gap = token.as_ref().is_some_and(|token| {
4788            token.channel() != visible_channel || is_caller_follow_boundary_gap_text(token.text())
4789        });
4790        (token_type, is_boundary, is_boundary_gap)
4791    }
4792
4793    /// Runs the fast recognizer once from the rule's start state and returns
4794    /// the best outcome or the per-attempt expected-token accumulator. The
4795    /// caller flips `fast_first_set_prefilter` between calls when a retry is
4796    /// needed, so the FIRST-set cache is left intact across both passes.
4797    fn fast_recognize_top(
4798        &mut self,
4799        atn: &Atn,
4800        request: FastRecognizeTopRequest,
4801    ) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
4802        let FastRecognizeTopRequest {
4803            start_state,
4804            stop_state,
4805            start_index,
4806            precedence,
4807            caller_follow_state,
4808        } = request;
4809        // `input.size()` is intentionally only the currently buffered token
4810        // count here. Do not restore an up-front fill just to size this map:
4811        // the fixed floor avoids small-input churn, and large inputs grow the
4812        // cache after the deferred-fill threshold without forcing startup
4813        // tokenization. The 8x multiplier matches the empirical
4814        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
4815        // Kotlin ~12× memo entries per token), so the table avoids one
4816        // rehash on the typical hot path.
4817        let memo_capacity = self.input.size().saturating_mul(8).clamp(65_536, 524_288);
4818        let mut visiting = FxHashSet::with_capacity_and_hasher(256, FxBuildHasher::default());
4819        let mut memo = FxHashMap::with_capacity_and_hasher(memo_capacity, FxBuildHasher::default());
4820        let mut expected = ExpectedTokens::default();
4821        let empty_recovery = self.empty_recovery_symbols();
4822        let outcomes = self.recognize_state_fast(
4823            atn,
4824            FastRecognizeRequest {
4825                state_number: start_state,
4826                stop_state,
4827                index: start_index,
4828                rule_start_index: start_index,
4829                decision_start_index: None,
4830                precedence,
4831                depth: 0,
4832                recovery_symbols: empty_recovery,
4833                recovery_state: None,
4834            },
4835            &mut visiting,
4836            &mut memo,
4837            &mut expected,
4838        );
4839        #[cfg(feature = "perf-counters")]
4840        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
4841            perf_counters::dump();
4842            perf_counters::reset();
4843        }
4844        let caller_follow =
4845            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
4846        match select_best_fast_outcome(
4847            outcomes.into_iter(),
4848            self.prediction_mode,
4849            caller_follow.as_deref(),
4850            |index| self.caller_follow_token_info(index),
4851        ) {
4852            Some(outcome) => Ok((outcome, expected)),
4853            None => Err(expected),
4854        }
4855    }
4856
4857    /// Converts a recognized fast-recognizer node into a public parse-tree
4858    /// node, mirroring [`Self::recognized_node_tree`] for the slow path.
4859    fn fast_recognized_node_tree(
4860        &mut self,
4861        node: &FastRecognizedNode,
4862    ) -> Result<ParseTree, AntlrError> {
4863        match node {
4864            FastRecognizedNode::Token { index } => {
4865                let token = self
4866                    .input
4867                    .get_ref(*index)
4868                    .ok_or_else(|| AntlrError::ParserError {
4869                        line: 0,
4870                        column: 0,
4871                        message: format!("missing token at index {index}"),
4872                    })?;
4873                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
4874            }
4875            FastRecognizedNode::ErrorToken { index } => {
4876                let token = self
4877                    .input
4878                    .get_ref(*index)
4879                    .ok_or_else(|| AntlrError::ParserError {
4880                        line: 0,
4881                        column: 0,
4882                        message: format!("missing error token at index {index}"),
4883                    })?;
4884                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
4885            }
4886            FastRecognizedNode::MissingToken {
4887                token_type,
4888                at_index,
4889                text,
4890            } => {
4891                let current = self.token_at(*at_index);
4892                let token = CommonToken::new(*token_type)
4893                    .with_text(text.as_str())
4894                    .with_span(usize::MAX, usize::MAX)
4895                    .with_position(
4896                        current.as_ref().map(Token::line).unwrap_or_default(),
4897                        current.as_ref().map(Token::column).unwrap_or_default(),
4898                    );
4899                Ok(ParseTree::Error(ErrorNode::new(token)))
4900            }
4901            FastRecognizedNode::Rule {
4902                rule_index,
4903                invoking_state,
4904                start_index,
4905                stop_index,
4906                children,
4907            } => {
4908                let mut context = ParserRuleContext::with_child_capacity(
4909                    *rule_index,
4910                    *invoking_state,
4911                    children.len(),
4912                );
4913                if let Some(token) = self.token_ref_at(*start_index) {
4914                    context.set_start_ref(token);
4915                }
4916                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
4917                    context.set_stop_ref(token);
4918                }
4919                if children.has_left_recursive_boundary() {
4920                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
4921                    for child in &folded {
4922                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
4923                    }
4924                } else {
4925                    for child in children.iter() {
4926                        context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
4927                    }
4928                }
4929                Ok(self.rule_node(context))
4930            }
4931            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
4932                Err(AntlrError::Unsupported(format!(
4933                    "unfolded left-recursive boundary for rule {rule_index}"
4934                )))
4935            }
4936        }
4937    }
4938
4939    fn fast_recognized_node_tree_with_implicit_tokens(
4940        &mut self,
4941        node: &FastRecognizedNode,
4942    ) -> Result<ParseTree, AntlrError> {
4943        match node {
4944            FastRecognizedNode::Rule {
4945                rule_index,
4946                invoking_state,
4947                start_index,
4948                stop_index,
4949                children,
4950            } => {
4951                let mut context = ParserRuleContext::with_child_capacity(
4952                    *rule_index,
4953                    *invoking_state,
4954                    children.len(),
4955                );
4956                if let Some(token) = self.token_ref_at(*start_index) {
4957                    context.set_start_ref(token);
4958                }
4959                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
4960                    context.set_stop_ref(token);
4961                }
4962                if children.has_left_recursive_boundary() {
4963                    let folded = fold_fast_left_recursive_boundaries(children.to_vec());
4964                    self.add_fast_implicit_token_children(
4965                        &mut context,
4966                        *start_index,
4967                        *stop_index,
4968                        &folded,
4969                    )?;
4970                } else {
4971                    self.add_fast_implicit_token_children_iter(
4972                        &mut context,
4973                        *start_index,
4974                        *stop_index,
4975                        children.iter(),
4976                    )?;
4977                }
4978                Ok(self.rule_node(context))
4979            }
4980            _ => self.fast_recognized_node_tree(node),
4981        }
4982    }
4983
4984    fn add_fast_implicit_token_children(
4985        &mut self,
4986        context: &mut ParserRuleContext,
4987        start_index: usize,
4988        stop_index: Option<usize>,
4989        children: &[Rc<FastRecognizedNode>],
4990    ) -> Result<(), AntlrError> {
4991        self.add_fast_implicit_token_children_iter(
4992            context,
4993            start_index,
4994            stop_index,
4995            children.iter(),
4996        )
4997    }
4998
4999    fn add_fast_implicit_token_children_iter<'a>(
5000        &mut self,
5001        context: &mut ParserRuleContext,
5002        start_index: usize,
5003        stop_index: Option<usize>,
5004        children: impl IntoIterator<Item = &'a Rc<FastRecognizedNode>>,
5005    ) -> Result<(), AntlrError> {
5006        let mut cursor = Some(start_index);
5007        for child in children {
5008            if let Some((child_start, child_stop)) = fast_recognized_node_span(child.as_ref()) {
5009                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
5010                context.add_child(
5011                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
5012                );
5013                if let Some(child_stop) = child_stop {
5014                    cursor = self.next_visible_after_token(child_stop);
5015                }
5016            } else {
5017                context.add_child(
5018                    self.fast_recognized_node_tree_with_implicit_tokens(child.as_ref())?,
5019                );
5020            }
5021        }
5022        if let Some(stop) = stop_index {
5023            self.add_visible_terminals_through(context, cursor, stop)?;
5024        }
5025        Ok(())
5026    }
5027
5028    fn add_visible_terminals_before(
5029        &mut self,
5030        context: &mut ParserRuleContext,
5031        cursor: &mut Option<usize>,
5032        before: usize,
5033    ) -> Result<(), AntlrError> {
5034        let Some(stop) = before.checked_sub(1) else {
5035            return Ok(());
5036        };
5037        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
5038        *cursor = next;
5039        Ok(())
5040    }
5041
5042    fn add_visible_terminals_through(
5043        &mut self,
5044        context: &mut ParserRuleContext,
5045        mut cursor: Option<usize>,
5046        stop: usize,
5047    ) -> Result<Option<usize>, AntlrError> {
5048        while let Some(index) = cursor {
5049            if index > stop {
5050                return Ok(Some(index));
5051            }
5052            let token = self
5053                .input
5054                .get_ref(index)
5055                .ok_or_else(|| AntlrError::ParserError {
5056                    line: 0,
5057                    column: 0,
5058                    message: format!("missing token at index {index}"),
5059                })?;
5060            let is_eof = token.token_type() == TOKEN_EOF;
5061            context.add_child(ParseTree::Terminal(TerminalNode::from_ref(token)));
5062            if is_eof {
5063                return Ok(None);
5064            }
5065            cursor = self.next_visible_after_token(index);
5066        }
5067        Ok(None)
5068    }
5069
5070    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
5071        let next = self.input.next_visible_after(index);
5072        (next != index).then_some(next)
5073    }
5074
5075    /// Parses a generated rule and returns semantic actions reached on the
5076    /// selected ATN path.
5077    ///
5078    /// This slower path preserves action ordering and token intervals for
5079    /// generated code that replays target-specific action templates after the
5080    /// recognizer has chosen one viable parse path.
5081    pub fn parse_atn_rule_with_actions(
5082        &mut self,
5083        atn: &Atn,
5084        rule_index: usize,
5085    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5086        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
5087    }
5088
5089    /// Parses a generated rule and emits ATN actions plus selected rule-init
5090    /// actions reached on the chosen path.
5091    ///
5092    /// Generated parsers use this when a grammar contains rule-level `@init`
5093    /// templates that must run for nested rule invocations. The runtime keeps
5094    /// the action list path-sensitive, so init templates are replayed only for
5095    /// rules that were actually entered by the selected parse.
5096    pub fn parse_atn_rule_with_action_inits(
5097        &mut self,
5098        atn: &Atn,
5099        rule_index: usize,
5100        init_action_rules: &[usize],
5101    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5102        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
5103    }
5104
5105    /// Parses a generated rule with optional semantic-action replay features.
5106    ///
5107    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
5108    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
5109    /// unchanged for grammars that do not request that target template.
5110    pub fn parse_atn_rule_with_action_options(
5111        &mut self,
5112        atn: &Atn,
5113        rule_index: usize,
5114        init_action_rules: &[usize],
5115        track_alt_numbers: bool,
5116    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5117        self.parse_atn_rule_with_runtime_options(
5118            atn,
5119            rule_index,
5120            ParserRuntimeOptions {
5121                init_action_rules,
5122                track_alt_numbers,
5123                ..ParserRuntimeOptions::default()
5124            },
5125        )
5126    }
5127
5128    /// Parses a generated rule with action replay and parser predicate support.
5129    ///
5130    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
5131    /// target-template predicate semantics emitted by the generator. Missing
5132    /// entries are treated as true so unsupported predicate-free grammars keep
5133    /// the previous unconditional transition behavior.
5134    pub fn parse_atn_rule_with_runtime_options(
5135        &mut self,
5136        atn: &Atn,
5137        rule_index: usize,
5138        options: ParserRuntimeOptions<'_>,
5139    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5140        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
5141    }
5142
5143    /// Parses a generated rule with action replay, parser predicate support,
5144    /// and an initial left-recursive precedence threshold.
5145    pub fn parse_atn_rule_with_runtime_options_and_precedence(
5146        &mut self,
5147        atn: &Atn,
5148        rule_index: usize,
5149        precedence: i32,
5150        options: ParserRuntimeOptions<'_>,
5151    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
5152        let ParserRuntimeOptions {
5153            init_action_rules,
5154            track_alt_numbers,
5155            predicates,
5156            semantics,
5157            rule_args,
5158            member_actions,
5159            return_actions,
5160            unknown_predicate_policy,
5161        } = options;
5162        if init_action_rules.is_empty()
5163            && !track_alt_numbers
5164            && predicates.is_empty()
5165            && semantics.is_none()
5166            && rule_args.is_empty()
5167            && member_actions.is_empty()
5168            && return_actions.is_empty()
5169            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
5170            && !atn_has_observable_action_transitions(atn)
5171            && (!self.semantic_hooks.observes_parser_predicates()
5172                || !atn_has_predicate_transitions(atn))
5173        {
5174            return self
5175                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
5176                .map(|tree| (tree, Vec::new()));
5177        }
5178        self.unknown_predicate_policy = unknown_predicate_policy;
5179        // A generated parent may have already recorded unknown-predicate
5180        // coordinates before descending into this (interpreted) child. Clearing
5181        // unconditionally would drop them before the parent's public entry
5182        // surfaces them, so stash and restore around this call: recognition sees
5183        // only the hits it records itself (so the fail-loud check below reflects
5184        // this rule), and the parent's prior hits are merged back afterward.
5185        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
5186        let start_state = atn
5187            .rule_to_start_state()
5188            .get(rule_index)
5189            .copied()
5190            .ok_or_else(|| {
5191                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
5192            })?;
5193        let stop_state = atn
5194            .rule_to_stop_state()
5195            .get(rule_index)
5196            .copied()
5197            .filter(|state| *state != usize::MAX)
5198            .ok_or_else(|| {
5199                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
5200            })?;
5201
5202        let start_index = self.current_visible_index();
5203        self.clear_prediction_diagnostics();
5204        self.reset_per_parse_caches();
5205        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
5206        let invoking_state = self.pending_invoking_states.pop();
5207        let local_int_arg = invoking_state
5208            .and_then(|state| usize::try_from(state).ok())
5209            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
5210        let mut visiting = BTreeSet::new();
5211        let mut memo = BTreeMap::new();
5212        let mut expected = ExpectedTokens::default();
5213        let member_values = self.int_members.clone();
5214        let return_values = BTreeMap::new();
5215        let outcomes = self.recognize_state(
5216            atn,
5217            RecognizeRequest {
5218                state_number: start_state,
5219                stop_state,
5220                index: start_index,
5221                rule_start_index: start_index,
5222                decision_start_index: None,
5223                init_action_rules: &init_action_rules,
5224                predicates,
5225                semantics,
5226                rule_args,
5227                member_actions,
5228                return_actions,
5229                local_int_arg,
5230                member_values,
5231                return_values,
5232                rule_alt_number: 0,
5233                track_alt_numbers,
5234                consumed_eof: false,
5235                precedence,
5236                depth: 0,
5237                recovery_symbols: BTreeSet::new(),
5238                recovery_state: None,
5239            },
5240            &mut visiting,
5241            &mut memo,
5242            &mut expected,
5243        );
5244        if let Some(error) = self.unknown_semantic_error() {
5245            report_token_source_errors(&self.input.drain_source_errors());
5246            // Keep the recorded coordinates: when this interpreted rule is a
5247            // child of a generated parent, the parent's catch block recovers an
5248            // ordinary `AntlrError` into a partial subtree, so the fail-loud
5249            // coordinate must survive on the parser for the top-level entry's
5250            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
5251            // is handled by clearing at the top-level generated entry instead.
5252            return Err(error);
5253        }
5254        // Recognition recorded no unresolved coordinate of its own; merge the
5255        // parent's prior hits back so its public entry can still surface them.
5256        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
5257        let Some(outcome) = select_best_outcome(outcomes.into_iter(), self.prediction_mode) else {
5258            let error = self.recognition_error(rule_index, start_index, &expected);
5259            self.record_syntax_errors(1);
5260            report_token_source_errors(&self.input.drain_source_errors());
5261            return Err(error);
5262        };
5263
5264        self.record_syntax_errors(outcome.diagnostics.len());
5265        report_parser_diagnostics(&self.prediction_diagnostics);
5266        report_parser_diagnostics(&outcome.diagnostics);
5267        report_token_source_errors(&self.input.drain_source_errors());
5268        let mut actions = outcome.actions;
5269        if init_action_rules.contains(&rule_index) {
5270            actions.insert(
5271                0,
5272                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
5273            );
5274        }
5275        let mut context =
5276            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
5277        if track_alt_numbers {
5278            context.set_alt_number(outcome.alt_number);
5279        }
5280        for (name, value) in outcome.return_values {
5281            context.set_int_return(name, value);
5282        }
5283        if let Some(token) = self.token_ref_at(start_index) {
5284            context.set_start_ref(token);
5285        }
5286        if let Some(token) = self.rule_stop_token_ref(outcome.index, outcome.consumed_eof) {
5287            context.set_stop_ref(token);
5288        }
5289        if self.build_parse_trees {
5290            let nodes = fold_left_recursive_boundaries(outcome.nodes);
5291            for node in &nodes {
5292                context.add_child(self.recognized_node_tree(node, track_alt_numbers)?);
5293            }
5294        }
5295        self.input.seek(outcome.index);
5296
5297        Ok((self.rule_node(context), actions))
5298    }
5299
5300    /// Temporary parser entry used by generated parser methods while the parser
5301    /// ATN simulator is being implemented.
5302    ///
5303    /// This keeps generated parser crates buildable and gives us a stable method
5304    /// surface for every grammar rule. It intentionally accepts all remaining
5305    /// tokens into one rule context; it is not the final parser semantics.
5306    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
5307        let mut context = ParserRuleContext::new(rule_index, self.state());
5308        while self.la(1) != TOKEN_EOF {
5309            let token_type = self.la(1);
5310            let child = self.match_token(token_type)?;
5311            if self.build_parse_trees {
5312                context.add_child(child);
5313            }
5314        }
5315        if self.build_parse_trees {
5316            context.add_child(self.match_eof()?);
5317        }
5318        Ok(self.rule_node(context))
5319    }
5320
5321    /// Builds the parser error reported when no ATN path can reach the active
5322    /// rule stop state.
5323    fn recognition_error(
5324        &mut self,
5325        rule_index: usize,
5326        start_index: usize,
5327        expected: &ExpectedTokens,
5328    ) -> AntlrError {
5329        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
5330        self.input.seek(index);
5331        let current = self.input.lt(1).cloned();
5332        let line = current.as_ref().map(Token::line).unwrap_or_default();
5333        let column = current.as_ref().map(Token::column).unwrap_or_default();
5334        AntlrError::ParserError {
5335            line,
5336            column,
5337            message,
5338        }
5339    }
5340
5341    /// Builds the token index and ANTLR-compatible message for a failed rule.
5342    fn expected_error_message(
5343        &mut self,
5344        rule_index: usize,
5345        start_index: usize,
5346        expected: &ExpectedTokens,
5347    ) -> (usize, String) {
5348        let index = expected
5349            .index
5350            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
5351            .unwrap_or_else(|| self.input.index());
5352        self.input.seek(index);
5353        let current = self.input.lt(1).cloned();
5354        let message = if expected
5355            .no_viable
5356            .as_ref()
5357            .is_some_and(|no_viable| no_viable.error_index == index)
5358        {
5359            let start = expected
5360                .no_viable
5361                .as_ref()
5362                .map_or(start_index, |no_viable| no_viable.start_index);
5363            let text = display_input_text(&self.input.text(start, index));
5364            format!("no viable alternative at input '{text}'")
5365        } else if expected.symbols.is_empty() {
5366            if expected.index.is_some() {
5367                let found = current
5368                    .as_ref()
5369                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
5370                if current
5371                    .as_ref()
5372                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
5373                {
5374                    format!(
5375                        "missing {} at {found}",
5376                        self.expected_symbols_display(&expected.symbols)
5377                    )
5378                } else {
5379                    format!("mismatched input {found}")
5380                }
5381            } else {
5382                format!("no viable alternative while parsing rule {rule_index}")
5383            }
5384        } else {
5385            format!(
5386                "mismatched input {} expecting {}",
5387                current
5388                    .as_ref()
5389                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5390                self.expected_symbols_display(&expected.symbols)
5391            )
5392        };
5393        (index, message)
5394    }
5395
5396    /// Converts a failed child rule into a recovered outcome so the parent can
5397    /// continue after reporting the child diagnostic.
5398    fn child_rule_failure_recovery(
5399        &mut self,
5400        rule_index: usize,
5401        start_index: usize,
5402        sync_symbols: &BTreeSet<i32>,
5403        member_values: BTreeMap<usize, i64>,
5404        expected: &ExpectedTokens,
5405    ) -> Option<RecognizeOutcome> {
5406        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
5407        let token = self.token_at(error_index);
5408        let mut next_index = error_index;
5409        loop {
5410            let symbol = self.token_type_at(next_index);
5411            if sync_symbols.contains(&symbol) {
5412                if next_index == error_index {
5413                    return None;
5414                }
5415                break;
5416            }
5417            if symbol == TOKEN_EOF {
5418                break;
5419            }
5420            let after = self.consume_index(next_index, symbol);
5421            if after == next_index {
5422                break;
5423            }
5424            next_index = after;
5425        }
5426        Some(RecognizeOutcome {
5427            index: next_index,
5428            consumed_eof: false,
5429            alt_number: 0,
5430            member_values,
5431            return_values: BTreeMap::new(),
5432            diagnostics: vec![diagnostic_for_token(token.as_ref(), message)],
5433            decisions: Vec::new(),
5434            actions: Vec::new(),
5435            nodes: vec![RecognizedNode::ErrorToken { index: error_index }],
5436        })
5437    }
5438
5439    /// Adapts the optional recovery result to the normal outcome list used by
5440    /// rule-call transitions.
5441    fn child_rule_failure_recovery_outcomes(
5442        &mut self,
5443        request: ChildRuleFailureRecovery<'_>,
5444    ) -> Vec<RecognizeOutcome> {
5445        let sync_symbols =
5446            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
5447        self.child_rule_failure_recovery(
5448            request.rule_index,
5449            request.start_index,
5450            &sync_symbols,
5451            request.member_values,
5452            request.expected,
5453        )
5454        .into_iter()
5455        .collect()
5456    }
5457
5458    /// Formats expected token types using ANTLR's single-token or set syntax.
5459    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
5460        expected_symbols_display(symbols, self.vocabulary())
5461    }
5462
5463    /// Returns the single-token deletion repair if the token after `index`
5464    /// satisfies the failed consuming transition.
5465    fn single_token_deletion(
5466        &mut self,
5467        transition: &Transition,
5468        index: usize,
5469        max_token_type: i32,
5470        expected_symbols: &BTreeSet<i32>,
5471    ) -> Option<(ParserDiagnostic, usize, i32)> {
5472        let current_symbol = self.token_type_at(index);
5473        if current_symbol == TOKEN_EOF {
5474            return None;
5475        }
5476        let next_index = self.consume_index(index, current_symbol);
5477        if next_index == index {
5478            return None;
5479        }
5480        let next_symbol = self.token_type_at(next_index);
5481        if !transition.matches(next_symbol, 1, max_token_type) {
5482            return None;
5483        }
5484        let transition_expected = transition_expected_symbols(transition, max_token_type);
5485        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
5486            &transition_expected
5487        } else {
5488            expected_symbols
5489        });
5490        let current = self.token_at(index);
5491        let message = format!(
5492            "extraneous input {} expecting {expected_display}",
5493            current
5494                .as_ref()
5495                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
5496        );
5497        Some((
5498            diagnostic_for_token(current.as_ref(), message),
5499            next_index,
5500            next_symbol,
5501        ))
5502    }
5503
5504    /// Returns the repair used when deleting the current token lets a recovery
5505    /// state continue with the following token.
5506    fn current_token_deletion(
5507        &mut self,
5508        index: usize,
5509        expected_symbols: &BTreeSet<i32>,
5510    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
5511        if expected_symbols.is_empty() {
5512            return None;
5513        }
5514        let current_symbol = self.token_type_at(index);
5515        if current_symbol == TOKEN_EOF {
5516            return None;
5517        }
5518        let current = self.token_at(index);
5519        let message = format!(
5520            "extraneous input {} expecting {}",
5521            current
5522                .as_ref()
5523                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
5524            self.expected_symbols_display(expected_symbols)
5525        );
5526        let diagnostic = diagnostic_for_token(current.as_ref(), message);
5527        let mut skipped = Vec::new();
5528        let mut cursor = index;
5529        loop {
5530            let symbol = self.token_type_at(cursor);
5531            if symbol == TOKEN_EOF {
5532                return None;
5533            }
5534            skipped.push(cursor);
5535            let next_index = self.consume_index(cursor, symbol);
5536            if next_index == cursor {
5537                return None;
5538            }
5539            let next_symbol = self.token_type_at(next_index);
5540            if expected_symbols.contains(&next_symbol) {
5541                return Some((diagnostic, next_index, skipped));
5542            }
5543            cursor = next_index;
5544        }
5545    }
5546
5547    /// Returns the single-token insertion repair for a failed consuming
5548    /// transition. The caller validates the repair by continuing from the
5549    /// transition target at the same input index.
5550    fn single_token_insertion(
5551        &mut self,
5552        transition: &Transition,
5553        index: usize,
5554        max_token_type: i32,
5555        expected_symbols: &BTreeSet<i32>,
5556        follow_symbols: &BTreeSet<i32>,
5557    ) -> Option<(ParserDiagnostic, i32, String)> {
5558        let current_symbol = self.token_type_at(index);
5559        if !follow_symbols.contains(&current_symbol) {
5560            return None;
5561        }
5562        let transition_expected = transition_expected_symbols(transition, max_token_type);
5563        let token_type = transition_expected.iter().next().copied()?;
5564        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
5565            &transition_expected
5566        } else {
5567            expected_symbols
5568        });
5569        let mut token_symbols = BTreeSet::new();
5570        token_symbols.insert(token_type);
5571        let missing_token_display = self.expected_symbols_display(&token_symbols);
5572        let current = self.token_at(index);
5573        let message = format!(
5574            "missing {expected_display} at {}",
5575            current
5576                .as_ref()
5577                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
5578        );
5579        let text = format!("<missing {missing_token_display}>");
5580        Some((
5581            diagnostic_for_token(current.as_ref(), message),
5582            token_type,
5583            text,
5584        ))
5585    }
5586
5587    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
5588    /// skip the unexpected current token when the following token satisfies the
5589    /// transition that failed.
5590    fn fast_single_token_deletion_recovery(
5591        &mut self,
5592        recovery: FastRecoveryRequest<'_, '_>,
5593    ) -> Vec<FastRecognizeOutcome> {
5594        let FastRecoveryRequest {
5595            atn,
5596            transition,
5597            expected_symbols,
5598            target,
5599            request,
5600            visiting,
5601            memo,
5602            expected,
5603        } = recovery;
5604        let FastRecognizeRequest {
5605            stop_state,
5606            index,
5607            rule_start_index,
5608            decision_start_index,
5609            precedence,
5610            depth,
5611            ..
5612        } = request;
5613        let Some((diagnostic, next_index, next_symbol)) =
5614            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
5615        else {
5616            return Vec::new();
5617        };
5618        let after_next = self.consume_index(next_index, next_symbol);
5619        let empty_recovery = self.empty_recovery_symbols();
5620        self.recognize_state_fast(
5621            atn,
5622            FastRecognizeRequest {
5623                state_number: target,
5624                stop_state,
5625                index: after_next,
5626                rule_start_index,
5627                decision_start_index,
5628                precedence,
5629                depth: depth + 1,
5630                recovery_symbols: empty_recovery,
5631                recovery_state: None,
5632            },
5633            visiting,
5634            memo,
5635            expected,
5636        )
5637        .into_iter()
5638        .map(|mut outcome| {
5639            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
5640            outcome.diagnostics.insert(0, diagnostic.clone());
5641            if self.fast_token_nodes_enabled {
5642                outcome
5643                    .nodes
5644                    .prepend(Rc::new(FastRecognizedNode::Token { index: next_index }));
5645                outcome
5646                    .nodes
5647                    .prepend(Rc::new(FastRecognizedNode::ErrorToken { index }));
5648            }
5649            outcome
5650        })
5651        .collect()
5652    }
5653
5654    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
5655    /// pretend the expected transition token was present and continue without
5656    /// consuming the current token.
5657    fn fast_single_token_insertion_recovery(
5658        &mut self,
5659        recovery: FastRecoveryRequest<'_, '_>,
5660    ) -> Vec<FastRecognizeOutcome> {
5661        let FastRecoveryRequest {
5662            atn,
5663            transition,
5664            expected_symbols,
5665            target,
5666            request,
5667            visiting,
5668            memo,
5669            expected,
5670        } = recovery;
5671        let FastRecognizeRequest {
5672            stop_state,
5673            index,
5674            rule_start_index,
5675            decision_start_index,
5676            precedence,
5677            depth,
5678            ..
5679        } = request;
5680        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
5681        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
5682            transition,
5683            index,
5684            atn.max_token_type(),
5685            &expected_symbols,
5686            &follow_symbols,
5687        ) else {
5688            return Vec::new();
5689        };
5690        let empty_recovery = self.empty_recovery_symbols();
5691        self.recognize_state_fast(
5692            atn,
5693            FastRecognizeRequest {
5694                state_number: target,
5695                stop_state,
5696                index,
5697                rule_start_index,
5698                decision_start_index,
5699                precedence,
5700                depth: depth + 1,
5701                recovery_symbols: empty_recovery,
5702                recovery_state: None,
5703            },
5704            visiting,
5705            memo,
5706            expected,
5707        )
5708        .into_iter()
5709        .map(|mut outcome| {
5710            outcome.diagnostics.insert(0, diagnostic.clone());
5711            outcome
5712                .nodes
5713                .prepend(Rc::new(FastRecognizedNode::MissingToken {
5714                    token_type,
5715                    at_index: index,
5716                    text: text.clone(),
5717                }));
5718            outcome
5719        })
5720        .collect()
5721    }
5722
5723    /// Retries the current fast-recognition state after deleting one
5724    /// unexpected token that precedes a valid loop or block continuation.
5725    fn fast_current_token_deletion_recovery(
5726        &mut self,
5727        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
5728    ) -> Vec<FastRecognizeOutcome> {
5729        let FastCurrentTokenDeletionRequest {
5730            atn,
5731            expected_symbols,
5732            mut request,
5733            visiting,
5734            memo,
5735            expected,
5736        } = recovery;
5737        if request.index == request.rule_start_index {
5738            return Vec::new();
5739        }
5740        let Some((diagnostic, next_index, skipped)) =
5741            self.current_token_deletion(request.index, &expected_symbols)
5742        else {
5743            return Vec::new();
5744        };
5745        request.state_number = request.recovery_state.unwrap_or(request.state_number);
5746        request.index = next_index;
5747        request.depth += 1;
5748        request.recovery_state = None;
5749        self.recognize_state_fast(atn, request, visiting, memo, expected)
5750            .into_iter()
5751            .map(|mut outcome| {
5752                outcome.diagnostics.insert(0, diagnostic.clone());
5753                for index in skipped.iter().rev() {
5754                    outcome
5755                        .nodes
5756                        .prepend(Rc::new(FastRecognizedNode::ErrorToken { index: *index }));
5757                }
5758                outcome
5759            })
5760            .collect()
5761    }
5762
5763    /// Converts a failed child rule into a recovered fast-recognizer outcome so
5764    /// the parent can keep its child rule context and continue at a sync token.
5765    fn fast_child_rule_failure_recovery(
5766        &mut self,
5767        rule_index: usize,
5768        start_index: usize,
5769        sync_symbols: &BTreeSet<i32>,
5770        expected: &ExpectedTokens,
5771    ) -> Option<FastRecognizeOutcome> {
5772        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
5773        let token = self.token_at(error_index);
5774        let mut next_index = error_index;
5775        loop {
5776            let symbol = self.token_type_at(next_index);
5777            if sync_symbols.contains(&symbol) {
5778                if next_index == error_index {
5779                    return None;
5780                }
5781                break;
5782            }
5783            if symbol == TOKEN_EOF {
5784                break;
5785            }
5786            let after = self.consume_index(next_index, symbol);
5787            if after == next_index {
5788                break;
5789            }
5790            next_index = after;
5791        }
5792        let mut diagnostics = FastDiagnostics::new();
5793        diagnostics.insert(0, diagnostic_for_token(token.as_ref(), message));
5794        let mut nodes = NodeList::new();
5795        if self.fast_token_nodes_enabled {
5796            nodes.prepend(Rc::new(FastRecognizedNode::ErrorToken {
5797                index: error_index,
5798            }));
5799        }
5800        Some(FastRecognizeOutcome {
5801            index: next_index,
5802            consumed_eof: false,
5803            diagnostics,
5804            nodes,
5805        })
5806    }
5807
5808    /// Adapts the optional child-rule recovery result to the fast-recognizer
5809    /// outcome list used by rule-call transitions.
5810    fn fast_child_rule_failure_recovery_outcomes(
5811        &mut self,
5812        request: FastChildRuleFailureRecoveryRequest<'_>,
5813    ) -> Vec<FastRecognizeOutcome> {
5814        let FastChildRuleFailureRecoveryRequest {
5815            atn,
5816            rule_index,
5817            start_index,
5818            follow_state,
5819            stop_state,
5820            expected,
5821        } = request;
5822        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
5823        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
5824            .into_iter()
5825            .collect()
5826    }
5827
5828    /// Attempts to reach `stop_state` from `state_number` without committing
5829    /// token consumption to the parser's public stream position.
5830    #[allow(clippy::too_many_lines)]
5831    fn recognize_state_fast(
5832        &mut self,
5833        atn: &Atn,
5834        request: FastRecognizeRequest,
5835        visiting: &mut FxHashSet<FastRecognizeKey>,
5836        memo: &mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
5837        expected: &mut ExpectedTokens,
5838    ) -> Vec<FastRecognizeOutcome> {
5839        #[cfg(feature = "perf-counters")]
5840        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
5841        let FastRecognizeRequest {
5842            mut state_number,
5843            stop_state,
5844            mut index,
5845            rule_start_index,
5846            decision_start_index,
5847            precedence,
5848            mut depth,
5849            recovery_symbols,
5850            recovery_state,
5851        } = request;
5852        // Walk straight-line epsilon chains in a loop instead of recursing
5853        // into `recognize_state_fast` for each intermediate state. ATN
5854        // serialization places long sequences of `BasicBlock` epsilon
5855        // transitions between decisions: turning that chain into a loop
5856        // collapses many recursive calls (and their memo lookups, vec
5857        // allocations, and visit-set churn) into a single function frame.
5858        // The loop exits as soon as we hit the original state's logic
5859        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
5860        // precedence) so existing fanout, recovery, and memoization still
5861        // apply unchanged.
5862        //
5863        // The inline case also handles single-atom-match states on the
5864        // happy-pass path: when the lone consuming transition matches the
5865        // current lookahead, advance the index and continue without paying
5866        // for a full `recognize_state_fast` recursion. We track tokens we
5867        // consumed inline in `inline_consumed_tokens` so they can be
5868        // prepended onto the eventual outcome list once we hit a state
5869        // whose handling falls outside this fast loop.
5870        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
5871        let mut inline_consumed_eof = false;
5872        loop {
5873            if depth > RECOGNITION_DEPTH_LIMIT {
5874                return Vec::new();
5875            }
5876            if state_number == stop_state {
5877                let mut nodes = NodeList::new();
5878                if self.fast_token_nodes_enabled {
5879                    for token_index in inline_consumed_tokens.iter().rev() {
5880                        nodes.prepend(Rc::new(FastRecognizedNode::Token {
5881                            index: *token_index,
5882                        }));
5883                    }
5884                }
5885                return vec![FastRecognizeOutcome {
5886                    index,
5887                    consumed_eof: inline_consumed_eof,
5888                    diagnostics: FastDiagnostics::new(),
5889                    nodes,
5890                }];
5891            }
5892            let Some(state) = atn.state(state_number) else {
5893                return Vec::new();
5894            };
5895            if state.transitions.len() == 1
5896                && !starts_prediction_decision(state)
5897                && !state.precedence_rule_decision
5898            {
5899                match &state.transitions[0] {
5900                    Transition::Epsilon { target }
5901                    | Transition::Predicate { target, .. }
5902                    | Transition::Action { target, .. }
5903                        if left_recursive_boundary(atn, state, *target).is_none() =>
5904                    {
5905                        #[cfg(feature = "perf-counters")]
5906                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
5907                        state_number = *target;
5908                        depth += 1;
5909                        continue;
5910                    }
5911                    Transition::Precedence {
5912                        target,
5913                        precedence: transition_precedence,
5914                    } if *transition_precedence >= precedence
5915                        && left_recursive_boundary(atn, state, *target).is_none() =>
5916                    {
5917                        #[cfg(feature = "perf-counters")]
5918                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
5919                        state_number = *target;
5920                        depth += 1;
5921                        continue;
5922                    }
5923                    // Single-atom / range / set / wildcard / not-set states
5924                    // are common (~17K of ~125K calls on C#) and almost
5925                    // always succeed in pass 1: no fanout, no recovery, no
5926                    // diagnostics. Inline the token match and continue
5927                    // walking instead of recursing — the recursive path
5928                    // would just allocate a Vec, build one outcome, prepend
5929                    // a Token node, and return. Skip pass 2 (recovery
5930                    // enabled): there the failure branch matters and the
5931                    // existing recursive code records expected symbols.
5932                    Transition::Atom { target, .. }
5933                    | Transition::Range { target, .. }
5934                    | Transition::Set { target, .. }
5935                    | Transition::NotSet { target, .. }
5936                    | Transition::Wildcard { target, .. }
5937                        if !self.fast_recovery_enabled =>
5938                    {
5939                        let symbol = self.token_type_at(index);
5940                        let transition = &state.transitions[0];
5941                        if transition.matches(symbol, 1, atn.max_token_type()) {
5942                            #[cfg(feature = "perf-counters")]
5943                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
5944                            if self.fast_token_nodes_enabled {
5945                                inline_consumed_tokens.push(index);
5946                            }
5947                            inline_consumed_eof |= symbol == TOKEN_EOF;
5948                            index = self.consume_index(index, symbol);
5949                            state_number = *target;
5950                            depth += 1;
5951                            continue;
5952                        }
5953                        // Fall through to break and let the regular
5954                        // body handle the no-match case (returns empty).
5955                    }
5956                    _ => {}
5957                }
5958            }
5959            break;
5960        }
5961        // If we collected token nodes inline but bail to the recursive
5962        // body (decision state, rule call, etc.), the outcomes returned
5963        // below will need those token nodes prepended.
5964        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
5965        let Some(state) = atn.state(state_number) else {
5966            return Vec::new();
5967        };
5968        let transition_count = state.transitions.len();
5969        let memo_lookup_enabled = self.fast_recovery_enabled || transition_count > 1;
5970        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
5971        // fields and the rule/decision boundary indices are pure plumbing —
5972        // they only affect the recovery branch and the no-viable diagnostic
5973        // recording, neither of which fires when recovery is off. Zeroing
5974        // them in the memo key collapses calls that visit the same
5975        // `(state, index)` from different rule-call sites onto one cache
5976        // entry, which is the dominant cost on large grammars (e.g. C#) where
5977        // many rules eventually delegate into the same `expression` /
5978        // `primary_expression` / `type` branches.
5979        let key = if self.fast_recovery_enabled {
5980            FastRecognizeKey {
5981                state_number,
5982                stop_state,
5983                index,
5984                rule_start_index,
5985                decision_start_index,
5986                precedence,
5987                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
5988                recovery_state,
5989            }
5990        } else {
5991            FastRecognizeKey {
5992                state_number,
5993                stop_state,
5994                index,
5995                rule_start_index: 0,
5996                decision_start_index: None,
5997                precedence,
5998                recovery_symbols_id: 0,
5999                recovery_state: None,
6000            }
6001        };
6002        if memo_lookup_enabled {
6003            if let Some(outcomes) = memo.get(&key) {
6004                #[cfg(feature = "perf-counters")]
6005                {
6006                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
6007                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
6008                }
6009                // Materialize a fresh `Vec` from the cached slice; the caller
6010                // mutates per-outcome state (eof flags, prepended nodes) so we
6011                // can't hand them the shared backing.
6012                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
6013                    let inline_eof = inline_consumed_eof;
6014                    let inline_tokens = &inline_consumed_tokens;
6015                    return outcomes
6016                        .iter()
6017                        .cloned()
6018                        .map(|mut outcome| {
6019                            if inline_eof {
6020                                outcome.consumed_eof = true;
6021                            }
6022                            if self.fast_token_nodes_enabled {
6023                                for token_index in inline_tokens.iter().rev() {
6024                                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
6025                                        index: *token_index,
6026                                    }));
6027                                }
6028                            }
6029                            outcome
6030                        })
6031                        .collect();
6032                }
6033                return outcomes.to_vec();
6034            }
6035            #[cfg(feature = "perf-counters")]
6036            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
6037        }
6038
6039        // Cycle detection: clean recognition keeps the narrow static cycle
6040        // guard used on hot paths. Recovery needs the broader epsilon-state
6041        // guard because an otherwise non-nullable loop body can recover as an
6042        // empty child at EOF and re-enter the loop at the same token.
6043        let needs_cycle_guard = if self.fast_recovery_enabled {
6044            state.transitions.iter().any(Transition::is_epsilon)
6045        } else {
6046            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
6047        };
6048        #[cfg(feature = "perf-counters")]
6049        if needs_cycle_guard {
6050            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
6051        } else {
6052            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
6053            match &state.transitions[0] {
6054                Transition::Rule { .. } => {
6055                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
6056                }
6057                Transition::Atom { .. }
6058                | Transition::Range { .. }
6059                | Transition::Set { .. }
6060                | Transition::NotSet { .. }
6061                | Transition::Wildcard { .. } => {
6062                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
6063                }
6064                _ => {
6065                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
6066                }
6067            }
6068        }
6069        let has_inserted_cycle_guard = if needs_cycle_guard {
6070            if !visiting.insert(key.clone()) {
6071                #[cfg(feature = "perf-counters")]
6072                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
6073                return Vec::new();
6074            }
6075            true
6076        } else {
6077            false
6078        };
6079        let next_decision_start_index = if starts_prediction_decision(state) {
6080            Some(index)
6081        } else {
6082            decision_start_index
6083        };
6084        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
6085            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
6086        } else {
6087            (Rc::clone(&recovery_symbols), recovery_state)
6088        };
6089
6090        // Lookahead-based pruning. At a multi-alternative state we cache the
6091        // look-1 set of every outgoing transition; on visit we keep only the
6092        // transitions whose look-1 can accept the current lookahead (or that
6093        // can be reached without consuming and so could legitimately match a
6094        // shorter input). This is the main speedup vs. blind speculative
6095        // recursion: it lets each visit fan out only to the alternatives that
6096        // could possibly contribute a clean parse, mirroring the SLL phase of
6097        // ANTLR's adaptive prediction.
6098        //
6099        // Pruning is skipped at:
6100        //   * rule-start states (a child rule call may need every internal
6101        //     transition to surface single-token recovery diagnostics that
6102        //     ANTLR's reference parser emits at the rule's first consuming
6103        //     transition; the FIRST-set retry path turns the prefilter off
6104        //     entirely so let's keep this lightweight too),
6105        //   * left-recursive precedence loops (the precedence transition's
6106        //     gating is dynamic),
6107        //   * states with too few alternatives to benefit.
6108        let transition_count = state.transitions.len();
6109        let lookahead_filter = if transition_count > 1
6110            && self.fast_first_set_prefilter
6111            && !state.precedence_rule_decision
6112            && (!self.fast_recovery_enabled || state.kind != AtnStateKind::RuleStart)
6113        {
6114            state
6115                .rule_index
6116                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index).copied())
6117                .map(|rule_stop| {
6118                    let symbol = self.token_type_at(index);
6119                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6120                    (symbol, entry)
6121                })
6122        } else {
6123            None
6124        };
6125        // LL(1) fast path: when the FIRST sets for the decision are disjoint
6126        // and none is nullable, the lookahead deterministically selects one
6127        // alternative. The recursive recognizer can then commit to that single
6128        // alt without iterating every transition through `should_skip_via_lookahead`
6129        // — saving (transition_count - 1) filter probes per visit.
6130        //
6131        // Result is cached per `(state, lookahead_token)` on the parser
6132        // instance, so subsequent visits skip the FIRST-set scan entirely.
6133        let ll1_only_alt: Option<usize> = if transition_count > 1
6134            && let Some((symbol, entry)) = lookahead_filter.as_ref()
6135        {
6136            let key = (state.state_number, *symbol);
6137            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
6138                cached
6139            } else {
6140                let result = ll1_unique_alt(entry, *symbol);
6141                self.ll1_decision_cache.insert(key, result);
6142                result
6143            }
6144        } else {
6145            None
6146        };
6147        let lookahead_filter = lookahead_filter.as_ref();
6148        // Pre-size only when we expect at least one outcome to land — most
6149        // single-transition fall-throughs (the loop above didn't catch
6150        // because they're atom/rule/predicate) push at most one entry, so
6151        // reserving one slot avoids a reallocation while keeping the
6152        // unused-slot waste at one element.
6153        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
6154        for (transition_index, transition) in state.transitions.iter().enumerate() {
6155            if let Some(alt) = ll1_only_alt {
6156                // LL(1) determinism: skip every alt except the chosen one.
6157                if alt != transition_index {
6158                    continue;
6159                }
6160            } else if should_skip_via_lookahead(
6161                transition,
6162                transition_index,
6163                lookahead_filter,
6164                index,
6165                self.fast_recovery_enabled,
6166                expected,
6167            ) {
6168                continue;
6169            }
6170            match transition {
6171                Transition::Epsilon { target }
6172                | Transition::Predicate { target, .. }
6173                | Transition::Action { target, .. } => {
6174                    #[cfg(feature = "perf-counters")]
6175                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
6176                    let boundary = left_recursive_boundary(atn, state, *target);
6177                    outcomes.extend(
6178                        self.recognize_state_fast(
6179                            atn,
6180                            FastRecognizeRequest {
6181                                state_number: *target,
6182                                stop_state,
6183                                index,
6184                                rule_start_index,
6185                                decision_start_index: next_decision_start_index,
6186                                precedence,
6187                                depth: depth + 1,
6188                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6189                                recovery_state: epsilon_recovery_state,
6190                            },
6191                            visiting,
6192                            memo,
6193                            expected,
6194                        )
6195                        .into_iter()
6196                        .map(|mut outcome| {
6197                            if let Some(rule_index) = boundary {
6198                                outcome.nodes.prepend(Rc::new(
6199                                    FastRecognizedNode::LeftRecursiveBoundary { rule_index },
6200                                ));
6201                            }
6202                            outcome
6203                        }),
6204                    );
6205                }
6206                Transition::Precedence {
6207                    target,
6208                    precedence: transition_precedence,
6209                } => {
6210                    if *transition_precedence >= precedence {
6211                        let boundary = left_recursive_boundary(atn, state, *target);
6212                        outcomes.extend(
6213                            self.recognize_state_fast(
6214                                atn,
6215                                FastRecognizeRequest {
6216                                    state_number: *target,
6217                                    stop_state,
6218                                    index,
6219                                    rule_start_index,
6220                                    decision_start_index: next_decision_start_index,
6221                                    precedence,
6222                                    depth: depth + 1,
6223                                    recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6224                                    recovery_state: epsilon_recovery_state,
6225                                },
6226                                visiting,
6227                                memo,
6228                                expected,
6229                            )
6230                            .into_iter()
6231                            .map(|mut outcome| {
6232                                if let Some(rule_index) = boundary {
6233                                    outcome.nodes.prepend(Rc::new(
6234                                        FastRecognizedNode::LeftRecursiveBoundary { rule_index },
6235                                    ));
6236                                }
6237                                outcome
6238                            }),
6239                        );
6240                    }
6241                }
6242                Transition::Rule {
6243                    target,
6244                    rule_index,
6245                    follow_state,
6246                    precedence: rule_precedence,
6247                    ..
6248                } => {
6249                    #[cfg(feature = "perf-counters")]
6250                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
6251                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
6252                    else {
6253                        continue;
6254                    };
6255                    // Lookahead-based pruning. The recognizer would otherwise
6256                    // explore every speculative rule call, producing exponential
6257                    // work on grammars with many epsilon-reachable rules. When
6258                    // the rule is non-nullable and its FIRST set excludes the
6259                    // current lookahead, recursion can't find a clean path
6260                    // *through this rule*. Skipping is only safe if some sibling
6261                    // transition can still consume the lookahead — otherwise the
6262                    // rule call is the sole continuation and must run so the
6263                    // single-token insertion / deletion recovery inside the
6264                    // called rule can fire (mirroring ANTLR's reference behavior
6265                    // of conjuring a missing token at child-rule entry).
6266                    let symbol = self.token_type_at(index);
6267                    if self.fast_first_set_prefilter {
6268                        // Probe the shared cross-parse cache first; build
6269                        // the entry on miss and intern it there. The
6270                        // computation is purely a function of the ATN, so
6271                        // the cached entry is reused across parses (and
6272                        // freshly-instantiated parser values that share
6273                        // the same `&'static Atn`).
6274                        //
6275                        // `rule_first_set` returns the computed entry
6276                        // directly — it intentionally skips inserting into
6277                        // the cache when the FIRST-set walk hit a cycle, so
6278                        // we cannot assume the entry is in the cache after
6279                        // computing it.
6280                        let first = self.cached_rule_first_set(atn, *target, child_stop);
6281                        if should_skip_rule_via_first_set(
6282                            &first,
6283                            symbol,
6284                            self.fast_recovery_enabled,
6285                            index,
6286                            expected,
6287                        ) {
6288                            continue;
6289                        }
6290                    }
6291                    let expected_before_child =
6292                        self.fast_recovery_enabled.then(|| expected.clone());
6293                    let mut children = self.recognize_state_fast(
6294                        atn,
6295                        FastRecognizeRequest {
6296                            state_number: *target,
6297                            stop_state: child_stop,
6298                            index,
6299                            rule_start_index: index,
6300                            decision_start_index: None,
6301                            precedence: *rule_precedence,
6302                            depth: depth + 1,
6303                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
6304                            recovery_state: epsilon_recovery_state,
6305                        },
6306                        visiting,
6307                        memo,
6308                        expected,
6309                    );
6310                    if children.is_empty() && self.fast_recovery_enabled {
6311                        children = self.fast_child_rule_failure_recovery_outcomes(
6312                            FastChildRuleFailureRecoveryRequest {
6313                                atn,
6314                                rule_index: *rule_index,
6315                                start_index: index,
6316                                follow_state: *follow_state,
6317                                stop_state,
6318                                expected,
6319                            },
6320                        );
6321                    }
6322                    if let Some(expected_before_child) = expected_before_child {
6323                        if children
6324                            .iter()
6325                            .any(|child| child.diagnostics.is_empty() && child.index > index)
6326                        {
6327                            *expected = expected_before_child;
6328                        }
6329                    }
6330                    for child in children {
6331                        let child_index = child.index;
6332                        let child_consumed_eof = child.consumed_eof;
6333                        let child_diagnostics = child.diagnostics;
6334                        let empty_recovery = self.empty_recovery_symbols();
6335                        let follow_outcomes = self.recognize_state_fast(
6336                            atn,
6337                            FastRecognizeRequest {
6338                                state_number: *follow_state,
6339                                stop_state,
6340                                index: child_index,
6341                                rule_start_index,
6342                                decision_start_index: next_decision_start_index,
6343                                precedence,
6344                                depth: depth + 1,
6345                                recovery_symbols: empty_recovery,
6346                                recovery_state: None,
6347                            },
6348                            visiting,
6349                            memo,
6350                            expected,
6351                        );
6352                        if follow_outcomes.is_empty() {
6353                            continue;
6354                        }
6355                        let child_node = Rc::new(FastRecognizedNode::Rule {
6356                            rule_index: *rule_index,
6357                            invoking_state: invoking_state_number(state_number),
6358                            start_index: index,
6359                            stop_index: self.rule_stop_token_index(child_index, child_consumed_eof),
6360                            children: child.nodes,
6361                        });
6362                        let child_diags_empty = child_diagnostics.is_empty();
6363                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
6364                            outcome.consumed_eof |= child_consumed_eof;
6365                            // Skip the prepend dance when there's nothing to
6366                            // merge from the child — common case in pass 1.
6367                            if !child_diags_empty {
6368                                let mut diagnostics = child_diagnostics.clone();
6369                                diagnostics.append(&mut outcome.diagnostics);
6370                                outcome.diagnostics = diagnostics;
6371                            }
6372                            outcome.nodes.prepend(Rc::clone(&child_node));
6373                            outcome
6374                        }));
6375                    }
6376                }
6377                Transition::Atom { target, .. }
6378                | Transition::Range { target, .. }
6379                | Transition::Set { target, .. }
6380                | Transition::NotSet { target, .. }
6381                | Transition::Wildcard { target, .. } => {
6382                    #[cfg(feature = "perf-counters")]
6383                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
6384                    let symbol = self.token_type_at(index);
6385                    if transition.matches(symbol, 1, atn.max_token_type()) {
6386                        let next_index = self.consume_index(index, symbol);
6387                        let empty_recovery = self.empty_recovery_symbols();
6388                        outcomes.extend(
6389                            self.recognize_state_fast(
6390                                atn,
6391                                FastRecognizeRequest {
6392                                    state_number: *target,
6393                                    stop_state,
6394                                    index: next_index,
6395                                    rule_start_index,
6396                                    decision_start_index: next_decision_start_index,
6397                                    precedence,
6398                                    depth: depth + 1,
6399                                    recovery_symbols: empty_recovery,
6400                                    recovery_state: None,
6401                                },
6402                                visiting,
6403                                memo,
6404                                expected,
6405                            )
6406                            .into_iter()
6407                            .map(|mut outcome| {
6408                                outcome.consumed_eof |= symbol == TOKEN_EOF;
6409                                if self.fast_token_nodes_enabled {
6410                                    outcome
6411                                        .nodes
6412                                        .prepend(Rc::new(FastRecognizedNode::Token { index }));
6413                                }
6414                                outcome
6415                            }),
6416                        );
6417                    } else {
6418                        if !self.fast_recovery_enabled {
6419                            // In pass 1 there is no recovery to attempt; the
6420                            // recovery branch below would never run, and the
6421                            // `expected_symbols` computation is just there
6422                            // to gate that branch. Skipping it eliminates
6423                            // ~1× `state_expected_symbols` lookup per failed
6424                            // atom transition (≈82K on mono-statement.cs)
6425                            // for zero observable behavior change.
6426                            continue;
6427                        }
6428                        let expected_symbols = fast_recovery_expected_symbols(
6429                            self,
6430                            atn,
6431                            state.state_number,
6432                            &recovery_symbols,
6433                        );
6434                        if expected_symbols.contains(&symbol) {
6435                            continue;
6436                        }
6437                        {
6438                            expected.record_transition(index, transition, atn.max_token_type());
6439                            record_no_viable_if_ambiguous(
6440                                expected,
6441                                next_decision_start_index,
6442                                index,
6443                            );
6444                            outcomes.extend(self.fast_single_token_deletion_recovery(
6445                                FastRecoveryRequest {
6446                                    atn,
6447                                    transition,
6448                                    expected_symbols: Rc::clone(&expected_symbols),
6449                                    target: *target,
6450                                    request: FastRecognizeRequest {
6451                                        state_number,
6452                                        stop_state,
6453                                        index,
6454                                        rule_start_index,
6455                                        decision_start_index,
6456                                        precedence,
6457                                        depth,
6458                                        recovery_symbols: Rc::clone(&recovery_symbols),
6459                                        recovery_state,
6460                                    },
6461                                    visiting,
6462                                    memo,
6463                                    expected,
6464                                },
6465                            ));
6466                            if !state_is_left_recursive_rule(atn, state) {
6467                                outcomes.extend(self.fast_single_token_insertion_recovery(
6468                                    FastRecoveryRequest {
6469                                        atn,
6470                                        transition,
6471                                        expected_symbols: Rc::clone(&expected_symbols),
6472                                        target: *target,
6473                                        request: FastRecognizeRequest {
6474                                            state_number,
6475                                            stop_state,
6476                                            index,
6477                                            rule_start_index,
6478                                            decision_start_index,
6479                                            precedence,
6480                                            depth,
6481                                            recovery_symbols: Rc::clone(&recovery_symbols),
6482                                            recovery_state,
6483                                        },
6484                                        visiting,
6485                                        memo,
6486                                        expected,
6487                                    },
6488                                ));
6489                            }
6490                            outcomes.extend(self.fast_current_token_deletion_recovery(
6491                                FastCurrentTokenDeletionRequest {
6492                                    atn,
6493                                    expected_symbols,
6494                                    request: FastRecognizeRequest {
6495                                        state_number,
6496                                        stop_state,
6497                                        index,
6498                                        rule_start_index,
6499                                        decision_start_index,
6500                                        precedence,
6501                                        depth,
6502                                        recovery_symbols: Rc::clone(&recovery_symbols),
6503                                        recovery_state,
6504                                    },
6505                                    visiting,
6506                                    memo,
6507                                    expected,
6508                                },
6509                            ));
6510                        }
6511                    }
6512                }
6513            }
6514        }
6515
6516        if has_inserted_cycle_guard {
6517            visiting.remove(&key);
6518        }
6519        if matches!(
6520            self.prediction_mode,
6521            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
6522        ) && self.fast_recovery_enabled
6523        {
6524            // Without recovery enabled every outcome already has empty
6525            // diagnostics, so the discard pass is a no-op — skipping it
6526            // saves an iter+retain on each of the ~1M visits.
6527            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
6528        }
6529        if self.fast_recovery_enabled {
6530            dedupe_fast_outcomes(&mut outcomes);
6531        } else {
6532            dedupe_clean_fast_outcomes(&mut outcomes);
6533        }
6534        // Skip memoization for single-transition states whose outcome is
6535        // unambiguous: they only get re-entered if the caller revisits the
6536        // exact same call site, which is rare since the loop above already
6537        // collapsed straight-line epsilon walks. Multi-alternative states
6538        // are where backtracking actually revisits the same coordinate, so
6539        // we still memoize there. With recovery on we keep the existing
6540        // memoization unconditionally because the recovery branch may
6541        // record diagnostics that the cache must surface to repeated
6542        // failed visits.
6543        let should_memoize = self.fast_recovery_enabled
6544            || (transition_count > 1
6545                && (outcomes.is_empty()
6546                    || outcomes.len() > 1
6547                    || (outcomes.len() == 1 && self.should_memoize_single_outcome(&key))));
6548        // Apply inline pending state to each outcome before returning.
6549        // Tokens consumed inline by the loop-collapse don't appear in the
6550        // recursive recognizer's output, so we need to prepend them here.
6551        let apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
6552            if inline_consumed_eof {
6553                outcome.consumed_eof = true;
6554            }
6555            if !inline_consumed_tokens.is_empty() {
6556                for token_index in inline_consumed_tokens.iter().rev() {
6557                    outcome.nodes.prepend(Rc::new(FastRecognizedNode::Token {
6558                        index: *token_index,
6559                    }));
6560                }
6561            }
6562            outcome
6563        };
6564        if should_memoize {
6565            #[cfg(feature = "perf-counters")]
6566            {
6567                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
6568                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
6569                match outcomes.len() {
6570                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
6571                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
6572                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
6573                }
6574            }
6575            // The memo is keyed by the loop-exit `(state_number, index)` so
6576            // the inline-consumed tokens belong to *this* call's output, not
6577            // the cached result. Memoize the bare outcomes (without the
6578            // inline-pending data), then prepend the inline data on return.
6579            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
6580            memo.insert(key, Rc::clone(&stored));
6581            if inline_pending {
6582                return stored.iter().cloned().map(apply_inline_pending).collect();
6583            }
6584            return stored.to_vec();
6585        }
6586        #[cfg(feature = "perf-counters")]
6587        match outcomes.len() {
6588            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
6589            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
6590            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
6591        }
6592        if inline_pending {
6593            return outcomes.into_iter().map(apply_inline_pending).collect();
6594        }
6595        outcomes
6596    }
6597
6598    /// Explores single-token deletion recovery while preserving the matched
6599    /// token and skipped error token in the selected parse tree path.
6600    fn single_token_deletion_recovery(
6601        &mut self,
6602        recovery: RecoveryRequest<'_, '_>,
6603    ) -> Vec<RecognizeOutcome> {
6604        let RecoveryRequest {
6605            atn,
6606            transition,
6607            expected_symbols,
6608            target,
6609            request,
6610            visiting,
6611            memo,
6612            expected,
6613        } = recovery;
6614        let RecognizeRequest {
6615            stop_state,
6616            index,
6617            rule_start_index,
6618            decision_start_index,
6619            init_action_rules,
6620            predicates,
6621            semantics,
6622            rule_args,
6623            member_actions,
6624            return_actions,
6625            local_int_arg,
6626            member_values,
6627            return_values,
6628            rule_alt_number,
6629            track_alt_numbers,
6630            consumed_eof,
6631            precedence,
6632            depth,
6633            ..
6634        } = request;
6635        let Some((diagnostic, next_index, next_symbol)) =
6636            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
6637        else {
6638            return Vec::new();
6639        };
6640        let after_next = self.consume_index(next_index, next_symbol);
6641        self.recognize_state(
6642            atn,
6643            RecognizeRequest {
6644                state_number: target,
6645                stop_state,
6646                index: after_next,
6647                rule_start_index,
6648                decision_start_index,
6649                init_action_rules,
6650                predicates,
6651                semantics,
6652                rule_args,
6653                member_actions,
6654                return_actions,
6655                local_int_arg,
6656                member_values,
6657                return_values,
6658                rule_alt_number,
6659                track_alt_numbers,
6660                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
6661                precedence,
6662                depth: depth + 1,
6663                recovery_symbols: BTreeSet::new(),
6664                recovery_state: None,
6665            },
6666            visiting,
6667            memo,
6668            expected,
6669        )
6670        .into_iter()
6671        .map(|mut outcome| {
6672            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
6673            outcome.diagnostics.insert(0, diagnostic.clone());
6674            outcome
6675                .nodes
6676                .insert(0, RecognizedNode::Token { index: next_index });
6677            outcome
6678                .nodes
6679                .insert(0, RecognizedNode::ErrorToken { index });
6680            outcome
6681        })
6682        .collect()
6683    }
6684
6685    /// Retries the current recognition state after deleting one unexpected
6686    /// token, preserving the deleted token as an error node in the parse tree.
6687    fn current_token_deletion_recovery(
6688        &mut self,
6689        recovery: CurrentTokenDeletionRequest<'_, '_>,
6690    ) -> Vec<RecognizeOutcome> {
6691        let CurrentTokenDeletionRequest {
6692            atn,
6693            expected_symbols,
6694            mut request,
6695            visiting,
6696            memo,
6697            expected,
6698        } = recovery;
6699        let error_index = request.index;
6700        if error_index == request.rule_start_index {
6701            return Vec::new();
6702        }
6703        let Some((diagnostic, next_index, skipped)) =
6704            self.current_token_deletion(error_index, &expected_symbols)
6705        else {
6706            return Vec::new();
6707        };
6708        request.state_number = request.recovery_state.unwrap_or(request.state_number);
6709        request.index = next_index;
6710        request.depth += 1;
6711        request.recovery_state = None;
6712        self.recognize_state(atn, request, visiting, memo, expected)
6713            .into_iter()
6714            .map(|mut outcome| {
6715                outcome.diagnostics.insert(0, diagnostic.clone());
6716                for index in skipped.iter().rev() {
6717                    outcome
6718                        .nodes
6719                        .insert(0, RecognizedNode::ErrorToken { index: *index });
6720                }
6721                outcome
6722            })
6723            .collect()
6724    }
6725
6726    /// Falls back after deletion/insertion repairs cannot continue from a
6727    /// failed consuming transition.
6728    fn consuming_failure_fallback(
6729        &mut self,
6730        fallback: ConsumingFailureFallback<'_>,
6731        visiting: &mut BTreeSet<RecognizeKey>,
6732        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
6733        expected: &mut ExpectedTokens,
6734    ) -> Vec<RecognizeOutcome> {
6735        if fallback.expected_symbols.is_empty() {
6736            return Vec::new();
6737        }
6738        if fallback.symbol == TOKEN_EOF {
6739            return self.eof_consuming_failure_fallback(fallback, expected);
6740        }
6741        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
6742    }
6743
6744    /// Keeps unexpected non-EOF input visible as an error node when no repair
6745    /// path can otherwise reach the transition target.
6746    fn non_eof_consuming_failure_fallback(
6747        &mut self,
6748        fallback: ConsumingFailureFallback<'_>,
6749        visiting: &mut BTreeSet<RecognizeKey>,
6750        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
6751        expected: &mut ExpectedTokens,
6752    ) -> Vec<RecognizeOutcome> {
6753        let ConsumingFailureFallback {
6754            atn,
6755            target,
6756            request,
6757            symbol,
6758            expected_symbols,
6759            decision_start_index,
6760            decision,
6761        } = fallback;
6762        let error_index = request.index;
6763        let diagnostic =
6764            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
6765        let next_index = self.consume_index(error_index, symbol);
6766        self.recognize_state(
6767            atn,
6768            RecognizeRequest {
6769                state_number: target,
6770                stop_state: request.stop_state,
6771                index: next_index,
6772                rule_start_index: request.rule_start_index,
6773                decision_start_index,
6774                init_action_rules: request.init_action_rules,
6775                predicates: request.predicates,
6776                semantics: request.semantics,
6777                rule_args: request.rule_args,
6778                member_actions: request.member_actions,
6779                return_actions: request.return_actions,
6780                local_int_arg: request.local_int_arg,
6781                member_values: request.member_values,
6782                return_values: request.return_values,
6783                rule_alt_number: request.rule_alt_number,
6784                track_alt_numbers: request.track_alt_numbers,
6785                consumed_eof: request.consumed_eof,
6786                precedence: request.precedence,
6787                depth: request.depth + 1,
6788                recovery_symbols: BTreeSet::new(),
6789                recovery_state: None,
6790            },
6791            visiting,
6792            memo,
6793            expected,
6794        )
6795        .into_iter()
6796        .map(|mut outcome| {
6797            prepend_decision(&mut outcome, decision);
6798            outcome.diagnostics.insert(0, diagnostic.clone());
6799            outcome
6800                .nodes
6801                .insert(0, RecognizedNode::ErrorToken { index: error_index });
6802            outcome
6803        })
6804        .collect()
6805    }
6806
6807    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
6808    /// behavior of unwinding instead of inserting caller tokens at EOF.
6809    fn eof_consuming_failure_fallback(
6810        &mut self,
6811        fallback: ConsumingFailureFallback<'_>,
6812        expected: &ExpectedTokens,
6813    ) -> Vec<RecognizeOutcome> {
6814        let request = fallback.request;
6815        if request.index == request.rule_start_index {
6816            return Vec::new();
6817        }
6818        let diagnostic =
6819            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
6820        vec![RecognizeOutcome {
6821            index: request.index,
6822            consumed_eof: request.consumed_eof,
6823            alt_number: request.rule_alt_number,
6824            member_values: request.member_values,
6825            return_values: request.return_values,
6826            diagnostics: vec![diagnostic],
6827            decisions: Vec::new(),
6828            actions: Vec::new(),
6829            nodes: Vec::new(),
6830        }]
6831    }
6832
6833    /// Explores single-token insertion recovery while adding a conjured
6834    /// missing-token error node to the selected parse tree path.
6835    fn single_token_insertion_recovery(
6836        &mut self,
6837        recovery: RecoveryRequest<'_, '_>,
6838    ) -> Vec<RecognizeOutcome> {
6839        let RecoveryRequest {
6840            atn,
6841            transition,
6842            expected_symbols,
6843            target,
6844            request,
6845            visiting,
6846            memo,
6847            expected,
6848        } = recovery;
6849        let RecognizeRequest {
6850            stop_state,
6851            index,
6852            rule_start_index,
6853            decision_start_index,
6854            init_action_rules,
6855            predicates,
6856            semantics,
6857            rule_args,
6858            member_actions,
6859            return_actions,
6860            local_int_arg,
6861            member_values,
6862            return_values,
6863            rule_alt_number,
6864            track_alt_numbers,
6865            consumed_eof,
6866            precedence,
6867            depth,
6868            ..
6869        } = request;
6870        let follow_symbols = state_expected_symbols(atn, transition.target());
6871        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
6872            transition,
6873            index,
6874            atn.max_token_type(),
6875            &expected_symbols,
6876            &follow_symbols,
6877        ) else {
6878            return Vec::new();
6879        };
6880        self.recognize_state(
6881            atn,
6882            RecognizeRequest {
6883                state_number: target,
6884                stop_state,
6885                index,
6886                rule_start_index,
6887                decision_start_index,
6888                init_action_rules,
6889                predicates,
6890                semantics,
6891                rule_args,
6892                member_actions,
6893                return_actions,
6894                local_int_arg,
6895                member_values,
6896                return_values,
6897                rule_alt_number,
6898                track_alt_numbers,
6899                consumed_eof,
6900                precedence,
6901                depth: depth + 1,
6902                recovery_symbols: BTreeSet::new(),
6903                recovery_state: None,
6904            },
6905            visiting,
6906            memo,
6907            expected,
6908        )
6909        .into_iter()
6910        .map(|mut outcome| {
6911            outcome.diagnostics.insert(0, diagnostic.clone());
6912            outcome.nodes.insert(
6913                0,
6914                RecognizedNode::MissingToken {
6915                    token_type,
6916                    at_index: index,
6917                    text: text.clone(),
6918                },
6919            );
6920            outcome
6921        })
6922        .collect()
6923    }
6924
6925    /// Attempts to reach `stop_state` and carries semantic actions for the
6926    /// selected parser path.
6927    #[allow(clippy::too_many_lines)]
6928    fn recognize_state(
6929        &mut self,
6930        atn: &Atn,
6931        request: RecognizeRequest<'_>,
6932        visiting: &mut BTreeSet<RecognizeKey>,
6933        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
6934        expected: &mut ExpectedTokens,
6935    ) -> Vec<RecognizeOutcome> {
6936        let request_template = request.clone();
6937        let RecognizeRequest {
6938            state_number,
6939            stop_state,
6940            index,
6941            rule_start_index,
6942            decision_start_index,
6943            init_action_rules,
6944            predicates,
6945            semantics,
6946            rule_args,
6947            member_actions,
6948            return_actions,
6949            local_int_arg,
6950            member_values,
6951            return_values,
6952            rule_alt_number,
6953            track_alt_numbers,
6954            consumed_eof,
6955            precedence,
6956            depth,
6957            recovery_symbols,
6958            recovery_state,
6959        } = request;
6960        if depth > RECOGNITION_DEPTH_LIMIT {
6961            return Vec::new();
6962        }
6963        if state_number == stop_state {
6964            return stop_outcome(
6965                index,
6966                consumed_eof,
6967                rule_alt_number,
6968                member_values,
6969                return_values,
6970            );
6971        }
6972        let key = RecognizeKey {
6973            state_number,
6974            stop_state,
6975            index,
6976            rule_start_index,
6977            decision_start_index,
6978            local_int_arg,
6979            member_values: member_values.clone(),
6980            return_values: return_values.clone(),
6981            rule_alt_number,
6982            track_alt_numbers,
6983            consumed_eof,
6984            precedence,
6985            recovery_symbols: recovery_symbols.clone(),
6986            recovery_state,
6987        };
6988        if let Some(outcomes) = memo.get(&key) {
6989            return outcomes.clone();
6990        }
6991
6992        let visit_key = key.clone();
6993        if !visiting.insert(visit_key.clone()) {
6994            return Vec::new();
6995        }
6996
6997        let Some(state) = atn.state(state_number) else {
6998            visiting.remove(&visit_key);
6999            return Vec::new();
7000        };
7001        let next_decision_start_index = if starts_prediction_decision(state) {
7002            Some(index)
7003        } else {
7004            decision_start_index
7005        };
7006        let (epsilon_recovery_symbols, epsilon_recovery_state) =
7007            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
7008        let mut outcomes = Vec::new();
7009        for (transition_index, transition) in state.transitions.iter().enumerate() {
7010            let decision = transition_decision(atn, state, transition_index, predicates);
7011            let next_alt_number =
7012                next_alt_number(state, transition_index, rule_alt_number, track_alt_numbers);
7013            match transition {
7014                Transition::Epsilon { target } | Transition::Action { target, .. } => {
7015                    let action_rule_index = match transition {
7016                        Transition::Action { rule_index, .. } => Some(*rule_index),
7017                        _ => None,
7018                    };
7019                    outcomes.extend(self.recognize_epsilon_or_action_step(
7020                        atn,
7021                        &request_template,
7022                        EpsilonActionStep {
7023                            source_state: state_number,
7024                            target: *target,
7025                            action_rule_index,
7026                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
7027                            decision,
7028                            decision_start_index: next_decision_start_index,
7029                            alt_number: next_alt_number,
7030                            recovery_symbols: epsilon_recovery_symbols.clone(),
7031                            recovery_state: epsilon_recovery_state,
7032                        },
7033                        RecognizeScratch {
7034                            visiting,
7035                            memo,
7036                            expected,
7037                        },
7038                    ));
7039                }
7040                Transition::Predicate {
7041                    target,
7042                    rule_index,
7043                    pred_index,
7044                    ..
7045                } => {
7046                    let predicate = PredicateEval {
7047                        index,
7048                        rule_index: *rule_index,
7049                        pred_index: *pred_index,
7050                        predicates,
7051                        semantics,
7052                        context: None,
7053                        local_int_arg,
7054                        member_values: &member_values,
7055                    };
7056                    if self.parser_predicate_matches(predicate) {
7057                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
7058                        outcomes.extend(
7059                            self.recognize_state(
7060                                atn,
7061                                RecognizeRequest {
7062                                    state_number: *target,
7063                                    stop_state,
7064                                    index,
7065                                    rule_start_index,
7066                                    decision_start_index: next_decision_start_index,
7067                                    init_action_rules,
7068                                    predicates,
7069                                    semantics,
7070                                    rule_args,
7071                                    member_actions,
7072                                    return_actions,
7073                                    local_int_arg,
7074                                    member_values: member_values.clone(),
7075                                    return_values: return_values.clone(),
7076                                    rule_alt_number: next_alt_number,
7077                                    track_alt_numbers,
7078                                    consumed_eof,
7079                                    precedence,
7080                                    depth: depth + 1,
7081                                    recovery_symbols: epsilon_recovery_symbols.clone(),
7082                                    recovery_state: epsilon_recovery_state,
7083                                },
7084                                visiting,
7085                                memo,
7086                                expected,
7087                            )
7088                            .into_iter()
7089                            .map(|mut outcome| {
7090                                prepend_decision(&mut outcome, decision);
7091                                if let Some(rule_index) = left_recursive_boundary {
7092                                    outcome.nodes.insert(
7093                                        0,
7094                                        RecognizedNode::LeftRecursiveBoundary { rule_index },
7095                                    );
7096                                }
7097                                outcome
7098                            }),
7099                        );
7100                    } else if let Some(message) = semantics
7101                        .and_then(|semantics| {
7102                            self.parser_semantic_ir_predicate_failure_message(
7103                                *rule_index,
7104                                *pred_index,
7105                                semantics,
7106                            )
7107                        })
7108                        .or_else(|| {
7109                            self.parser_predicate_failure_message(
7110                                *rule_index,
7111                                *pred_index,
7112                                predicates,
7113                            )
7114                        })
7115                    {
7116                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
7117                            rule_index: *rule_index,
7118                            index,
7119                            message,
7120                            member_values: member_values.clone(),
7121                            return_values: return_values.clone(),
7122                            rule_alt_number,
7123                        }));
7124                    } else {
7125                        record_predicate_no_viable(expected, next_decision_start_index, index);
7126                    }
7127                }
7128                Transition::Precedence {
7129                    target,
7130                    precedence: transition_precedence,
7131                } => {
7132                    if *transition_precedence >= precedence {
7133                        outcomes.extend(
7134                            self.recognize_state(
7135                                atn,
7136                                RecognizeRequest {
7137                                    state_number: *target,
7138                                    stop_state,
7139                                    index,
7140                                    rule_start_index,
7141                                    decision_start_index: next_decision_start_index,
7142                                    init_action_rules,
7143                                    predicates,
7144                                    semantics,
7145                                    rule_args,
7146                                    member_actions,
7147                                    return_actions,
7148                                    local_int_arg,
7149                                    member_values: member_values.clone(),
7150                                    return_values: return_values.clone(),
7151                                    rule_alt_number: next_alt_number,
7152                                    track_alt_numbers,
7153                                    consumed_eof,
7154                                    precedence,
7155                                    depth: depth + 1,
7156                                    recovery_symbols: epsilon_recovery_symbols.clone(),
7157                                    recovery_state: epsilon_recovery_state,
7158                                },
7159                                visiting,
7160                                memo,
7161                                expected,
7162                            )
7163                            .into_iter()
7164                            .map(|mut outcome| {
7165                                prepend_decision(&mut outcome, decision);
7166                                outcome
7167                            }),
7168                        );
7169                    }
7170                }
7171                Transition::Rule {
7172                    target,
7173                    rule_index,
7174                    follow_state,
7175                    precedence: rule_precedence,
7176                    ..
7177                } => {
7178                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
7179                    else {
7180                        continue;
7181                    };
7182                    let child_local_int_arg =
7183                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
7184                    let expected_before_child = expected.clone();
7185                    let children = self.recognize_state(
7186                        atn,
7187                        RecognizeRequest {
7188                            state_number: *target,
7189                            stop_state: child_stop,
7190                            index,
7191                            rule_start_index: index,
7192                            decision_start_index: None,
7193                            init_action_rules,
7194                            predicates,
7195                            semantics,
7196                            rule_args,
7197                            member_actions,
7198                            return_actions,
7199                            local_int_arg: child_local_int_arg,
7200                            member_values: member_values.clone(),
7201                            return_values: BTreeMap::new(),
7202                            rule_alt_number: 0,
7203                            track_alt_numbers,
7204                            consumed_eof: false,
7205                            precedence: *rule_precedence,
7206                            depth: depth + 1,
7207                            recovery_symbols: epsilon_recovery_symbols.clone(),
7208                            recovery_state: epsilon_recovery_state,
7209                        },
7210                        visiting,
7211                        memo,
7212                        expected,
7213                    );
7214                    let children = if children.is_empty() {
7215                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
7216                            atn,
7217                            rule_index: *rule_index,
7218                            start_index: index,
7219                            follow_state: *follow_state,
7220                            stop_state,
7221                            member_values: member_values.clone(),
7222                            expected,
7223                        })
7224                    } else {
7225                        children
7226                    };
7227                    let preserve_child_expected =
7228                        self.child_expected_reaches_clean_eof(&children, expected);
7229                    restore_expected(
7230                        &children,
7231                        index,
7232                        expected,
7233                        expected_before_child,
7234                        preserve_child_expected,
7235                    );
7236                    for child in children {
7237                        let child_node = RecognizedNode::Rule {
7238                            rule_index: *rule_index,
7239                            invoking_state: invoking_state_number(state_number),
7240                            alt_number: child.alt_number,
7241                            start_index: index,
7242                            stop_index: self.rule_stop_token_index(child.index, child.consumed_eof),
7243                            return_values: child.return_values.clone(),
7244                            children: fold_left_recursive_boundaries(child.nodes.clone()),
7245                        };
7246                        outcomes.extend(
7247                            self.recognize_state(
7248                                atn,
7249                                RecognizeRequest {
7250                                    state_number: *follow_state,
7251                                    stop_state,
7252                                    index: child.index,
7253                                    rule_start_index,
7254                                    decision_start_index: next_decision_start_index,
7255                                    init_action_rules,
7256                                    predicates,
7257                                    semantics,
7258                                    rule_args,
7259                                    member_actions,
7260                                    return_actions,
7261                                    local_int_arg,
7262                                    member_values: child.member_values.clone(),
7263                                    return_values: return_values.clone(),
7264                                    rule_alt_number,
7265                                    track_alt_numbers,
7266                                    consumed_eof: consumed_eof || child.consumed_eof,
7267                                    precedence,
7268                                    depth: depth + 1,
7269                                    recovery_symbols: BTreeSet::new(),
7270                                    recovery_state: None,
7271                                },
7272                                visiting,
7273                                memo,
7274                                expected,
7275                            )
7276                            .into_iter()
7277                            .map(|mut outcome| {
7278                                outcome.consumed_eof |= child.consumed_eof;
7279                                let mut diagnostics = child.diagnostics.clone();
7280                                diagnostics.append(&mut outcome.diagnostics);
7281                                outcome.diagnostics = diagnostics;
7282                                let mut decisions = child.decisions.clone();
7283                                decisions.append(&mut outcome.decisions);
7284                                outcome.decisions = decisions;
7285                                prepend_decision(&mut outcome, decision);
7286                                let mut actions = child.actions.clone();
7287                                if init_action_rules.contains(rule_index) {
7288                                    actions.insert(
7289                                        0,
7290                                        ParserAction::new_rule_init(
7291                                            *rule_index,
7292                                            index,
7293                                            Some(*follow_state),
7294                                        ),
7295                                    );
7296                                }
7297                                actions.append(&mut outcome.actions);
7298                                outcome.actions = actions;
7299                                outcome.nodes.insert(0, child_node.clone());
7300                                outcome
7301                            }),
7302                        );
7303                    }
7304                }
7305                Transition::Atom { target, .. }
7306                | Transition::Range { target, .. }
7307                | Transition::Set { target, .. }
7308                | Transition::NotSet { target, .. }
7309                | Transition::Wildcard { target, .. } => {
7310                    let symbol = self.token_type_at(index);
7311                    if transition.matches(symbol, 1, atn.max_token_type()) {
7312                        let next_index = self.consume_index(index, symbol);
7313                        outcomes.extend(
7314                            self.recognize_state(
7315                                atn,
7316                                RecognizeRequest {
7317                                    state_number: *target,
7318                                    stop_state,
7319                                    index: next_index,
7320                                    rule_start_index,
7321                                    decision_start_index: next_decision_start_index,
7322                                    init_action_rules,
7323                                    predicates,
7324                                    semantics,
7325                                    rule_args,
7326                                    member_actions,
7327                                    return_actions,
7328                                    local_int_arg,
7329                                    member_values: member_values.clone(),
7330                                    return_values: return_values.clone(),
7331                                    rule_alt_number: next_alt_number,
7332                                    track_alt_numbers,
7333                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
7334                                    precedence,
7335                                    depth: depth + 1,
7336                                    recovery_symbols: BTreeSet::new(),
7337                                    recovery_state: None,
7338                                },
7339                                visiting,
7340                                memo,
7341                                expected,
7342                            )
7343                            .into_iter()
7344                            .map(|mut outcome| {
7345                                prepend_decision(&mut outcome, decision);
7346                                outcome.consumed_eof |= symbol == TOKEN_EOF;
7347                                outcome.nodes.insert(0, RecognizedNode::Token { index });
7348                                outcome
7349                            }),
7350                        );
7351                    } else {
7352                        let expected_symbols =
7353                            recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
7354                        if expected_symbols.contains(&symbol) {
7355                            continue;
7356                        }
7357                        expected.record_transition(index, transition, atn.max_token_type());
7358                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
7359                        let before_recovery = outcomes.len();
7360                        let recovery_request = request_template.clone();
7361                        outcomes.extend(
7362                            self.single_token_deletion_recovery(RecoveryRequest {
7363                                atn,
7364                                transition,
7365                                expected_symbols: expected_symbols.clone(),
7366                                target: *target,
7367                                request: recovery_request.clone(),
7368                                visiting,
7369                                memo,
7370                                expected,
7371                            })
7372                            .into_iter()
7373                            .map(|mut outcome| {
7374                                prepend_decision(&mut outcome, decision);
7375                                outcome
7376                            }),
7377                        );
7378                        if !state_is_left_recursive_rule(atn, state) {
7379                            outcomes.extend(
7380                                self.single_token_insertion_recovery(RecoveryRequest {
7381                                    atn,
7382                                    transition,
7383                                    expected_symbols: expected_symbols.clone(),
7384                                    target: *target,
7385                                    request: recovery_request.clone(),
7386                                    visiting,
7387                                    memo,
7388                                    expected,
7389                                })
7390                                .into_iter()
7391                                .map(|mut outcome| {
7392                                    prepend_decision(&mut outcome, decision);
7393                                    outcome
7394                                }),
7395                            );
7396                        }
7397                        outcomes.extend(self.current_token_deletion_recovery(
7398                            CurrentTokenDeletionRequest {
7399                                atn,
7400                                expected_symbols: expected_symbols.clone(),
7401                                request: recovery_request.clone(),
7402                                visiting,
7403                                memo,
7404                                expected,
7405                            },
7406                        ));
7407                        if outcomes.len() == before_recovery {
7408                            outcomes.extend(self.consuming_failure_fallback(
7409                                ConsumingFailureFallback {
7410                                    atn,
7411                                    target: *target,
7412                                    request: recovery_request,
7413                                    symbol,
7414                                    expected_symbols,
7415                                    decision_start_index: next_decision_start_index,
7416                                    decision,
7417                                },
7418                                visiting,
7419                                memo,
7420                                expected,
7421                            ));
7422                        }
7423                    }
7424                }
7425            }
7426        }
7427
7428        visiting.remove(&visit_key);
7429        self.record_prediction_diagnostics(atn, state, index, &outcomes);
7430        if matches!(
7431            self.prediction_mode,
7432            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
7433        ) {
7434            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes);
7435        }
7436        dedupe_outcomes(&mut outcomes);
7437        memo.insert(key, outcomes.clone());
7438        outcomes
7439    }
7440
7441    /// Follows an epsilon or semantic-action transition while preserving the
7442    /// path-local side effects that may later become generated action output.
7443    fn recognize_epsilon_or_action_step(
7444        &mut self,
7445        atn: &Atn,
7446        request: &RecognizeRequest<'_>,
7447        step: EpsilonActionStep,
7448        scratch: RecognizeScratch<'_>,
7449    ) -> Vec<RecognizeOutcome> {
7450        let RecognizeScratch {
7451            visiting,
7452            memo,
7453            expected,
7454        } = scratch;
7455        let action = step.action_rule_index.map(|rule_index| {
7456            ParserAction::new(
7457                step.source_state,
7458                rule_index,
7459                request.rule_start_index,
7460                self.rule_stop_token_index(request.index, request.consumed_eof),
7461            )
7462        });
7463        let next_member_values = if action.is_some() {
7464            member_values_after_action(
7465                step.source_state,
7466                request.member_actions,
7467                request.semantics,
7468                &request.member_values,
7469            )
7470        } else {
7471            request.member_values.clone()
7472        };
7473        let next_return_values = action.map_or_else(
7474            || request.return_values.clone(),
7475            |action| {
7476                return_values_after_action(
7477                    step.source_state,
7478                    action.rule_index(),
7479                    request.return_actions,
7480                    request.semantics,
7481                    &request.return_values,
7482                )
7483            },
7484        );
7485
7486        self.recognize_state(
7487            atn,
7488            RecognizeRequest {
7489                state_number: step.target,
7490                stop_state: request.stop_state,
7491                index: request.index,
7492                rule_start_index: request.rule_start_index,
7493                decision_start_index: step.decision_start_index,
7494                init_action_rules: request.init_action_rules,
7495                predicates: request.predicates,
7496                semantics: request.semantics,
7497                rule_args: request.rule_args,
7498                member_actions: request.member_actions,
7499                return_actions: request.return_actions,
7500                local_int_arg: request.local_int_arg,
7501                member_values: next_member_values,
7502                return_values: next_return_values,
7503                rule_alt_number: step.alt_number,
7504                track_alt_numbers: request.track_alt_numbers,
7505                consumed_eof: request.consumed_eof,
7506                precedence: request.precedence,
7507                depth: request.depth + 1,
7508                recovery_symbols: step.recovery_symbols,
7509                recovery_state: step.recovery_state,
7510            },
7511            visiting,
7512            memo,
7513            expected,
7514        )
7515        .into_iter()
7516        .map(|mut outcome| {
7517            prepend_decision(&mut outcome, step.decision);
7518            if let Some(rule_index) = step.left_recursive_boundary {
7519                outcome
7520                    .nodes
7521                    .insert(0, RecognizedNode::LeftRecursiveBoundary { rule_index });
7522            }
7523            if let Some(action) = action {
7524                outcome.actions.insert(0, action);
7525            }
7526            outcome
7527        })
7528        .collect()
7529    }
7530
7531    /// Reads the token type at an absolute token-stream index without moving
7532    /// the parser's stream cursor. The fast recognizer probes lookahead at
7533    /// every state visit, so avoiding the seek round-trip is a measurable
7534    /// hot-path win on long inputs.
7535    fn token_type_at(&mut self, index: usize) -> i32 {
7536        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
7537            self.input.fill();
7538        }
7539        self.input.token_type_at_index(index)
7540    }
7541
7542    /// Returns the cached `state_expected_symbols` set for an ATN state.
7543    ///
7544    /// The fast recognizer consults this set on every state visit through
7545    /// `next_recovery_context`; the underlying DFS is a pure function of the
7546    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
7547    ///
7548    /// Caching is layered through `intern_recovery_symbols` so two ATN states
7549    /// with the same expected-symbol set share one `Rc`. That invariant is
7550    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
7551    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
7552    /// always interned before it ends up in a key.
7553    fn cached_state_expected_symbols(
7554        &mut self,
7555        atn: &Atn,
7556        state_number: usize,
7557    ) -> Rc<BTreeSet<i32>> {
7558        if let Some(cached) = self.state_expected_cache.get(&state_number) {
7559            return Rc::clone(cached);
7560        }
7561        let symbols = state_expected_symbols(atn, state_number);
7562        let entry = self.intern_recovery_symbols(symbols);
7563        self.state_expected_cache
7564            .insert(state_number, Rc::clone(&entry));
7565        entry
7566    }
7567
7568    fn cached_state_expected_token_set(
7569        &mut self,
7570        atn: &Atn,
7571        state_number: usize,
7572    ) -> Rc<TokenBitSet> {
7573        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
7574            return Rc::clone(cached);
7575        }
7576        // Purely a function of the ATN, so back the per-parser cache with the
7577        // thread-shared one — fresh parser instances (one per parse in
7578        // generated usage) start warm instead of rewalking the ATN.
7579        let symbols = with_shared_atn_caches(atn, |cache| {
7580            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
7581                return Rc::clone(cached);
7582            }
7583            let symbols = Rc::new(state_expected_token_set(atn, state_number));
7584            cache
7585                .state_expected_tokens
7586                .insert(state_number, Rc::clone(&symbols));
7587            symbols
7588        });
7589        self.state_expected_token_cache
7590            .insert(state_number, Rc::clone(&symbols));
7591        symbols
7592    }
7593
7594    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
7595        if self.rule_stop_reach_cache.len() <= state_number {
7596            self.rule_stop_reach_cache
7597                .resize_with(atn.states().len().max(state_number + 1), || None);
7598        }
7599        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
7600            return reaches;
7601        }
7602        let reaches = with_shared_atn_caches(atn, |cache| {
7603            *cache
7604                .rule_stop_reach
7605                .entry(state_number)
7606                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
7607        });
7608        self.rule_stop_reach_cache[state_number] = Some(reaches);
7609        reaches
7610    }
7611
7612    /// Returns the parser's empty `recovery_symbols` singleton so callers can
7613    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
7614    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
7615        Rc::clone(&self.empty_recovery_symbols)
7616    }
7617
7618    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
7619    /// recognizer can hash and compare keys by pointer.
7620    ///
7621    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
7622    /// come from this method or the empty singleton; otherwise two
7623    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
7624    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
7625    /// recognition coordinates.
7626    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
7627        if set.is_empty() {
7628            return Rc::clone(&self.empty_recovery_symbols);
7629        }
7630        let candidate = Rc::new(set);
7631        match self.recovery_symbols_intern.get(&candidate) {
7632            Some(existing) => Rc::clone(existing),
7633            None => {
7634                self.recovery_symbols_intern
7635                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
7636                candidate
7637            }
7638        }
7639    }
7640
7641    /// Returns the cached look-1 entry for a decision state, computing it on
7642    /// first use. Multi-alternative states are visited many times during
7643    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
7644    /// hash lookup per visit.
7645    fn cached_decision_lookahead(
7646        &mut self,
7647        atn: &Atn,
7648        state: &AtnState,
7649        rule_stop_state: usize,
7650    ) -> Rc<DecisionLookahead> {
7651        // Hit the parser-instance cache first. Decision lookahead is purely
7652        // a function of the ATN/state, so on a warm cache we skip the
7653        // thread-local + RefCell + HashMap-entry dance through
7654        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
7655        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
7656        // hashmap-entry overhead in profiles.
7657        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number) {
7658            return Rc::clone(cached);
7659        }
7660        let entry = with_shared_atn_caches(atn, |cache| {
7661            if let Some(cached) = cache.decision_lookahead.get(&state.state_number) {
7662                return Rc::clone(cached);
7663            }
7664            let mut entry = DecisionLookahead {
7665                transitions: Vec::with_capacity(state.transitions.len()),
7666            };
7667            for transition in &state.transitions {
7668                entry.transitions.push(transition_first_set(
7669                    atn,
7670                    transition,
7671                    rule_stop_state,
7672                    &mut cache.first_set,
7673                ));
7674            }
7675            let entry = Rc::new(entry);
7676            cache
7677                .decision_lookahead
7678                .insert(state.state_number, Rc::clone(&entry));
7679            entry
7680        });
7681        self.decision_lookahead_cache
7682            .insert(state.state_number, Rc::clone(&entry));
7683        entry
7684    }
7685
7686    fn cached_rule_first_set(
7687        &mut self,
7688        atn: &Atn,
7689        target: usize,
7690        child_stop: usize,
7691    ) -> Rc<FirstSet> {
7692        if self.rule_first_set_cache.len() <= target {
7693            self.rule_first_set_cache
7694                .resize_with(atn.states().len().max(target + 1), || None);
7695        }
7696        if let Some(cached) = self
7697            .rule_first_set_cache
7698            .get(target)
7699            .and_then(Option::as_ref)
7700        {
7701            return Rc::clone(cached);
7702        }
7703        let first = with_shared_first_set_cache(atn, |cache| {
7704            rule_first_set(atn, target, child_stop, cache)
7705        });
7706        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
7707        first
7708    }
7709
7710    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
7711        if self.empty_cycle_cache.len() <= state_number {
7712            self.empty_cycle_cache
7713                .resize_with(atn.states().len().max(state_number + 1), || None);
7714        }
7715        if let Some(cached) = self.empty_cycle_cache[state_number] {
7716            return cached;
7717        }
7718        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
7719        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
7720        self.empty_cycle_cache[state_number] = Some(result);
7721        result
7722    }
7723
7724    fn empty_path_reaches_state(
7725        &mut self,
7726        atn: &Atn,
7727        state_number: usize,
7728        target_state: usize,
7729        visited: &mut FxHashSet<usize>,
7730    ) -> bool {
7731        if !visited.insert(state_number) {
7732            return false;
7733        }
7734        let Some(state) = atn.state(state_number) else {
7735            return false;
7736        };
7737        for transition in &state.transitions {
7738            match transition {
7739                Transition::Atom { .. }
7740                | Transition::Range { .. }
7741                | Transition::Set { .. }
7742                | Transition::NotSet { .. }
7743                | Transition::Wildcard { .. } => {}
7744                Transition::Rule {
7745                    target,
7746                    rule_index,
7747                    follow_state,
7748                    ..
7749                } => {
7750                    if *target == target_state
7751                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
7752                    {
7753                        return true;
7754                    }
7755                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
7756                    else {
7757                        continue;
7758                    };
7759                    if self
7760                        .cached_rule_first_set(atn, *target, child_stop)
7761                        .nullable
7762                        && (*follow_state == target_state
7763                            || self.empty_path_reaches_state(
7764                                atn,
7765                                *follow_state,
7766                                target_state,
7767                                visited,
7768                            ))
7769                    {
7770                        return true;
7771                    }
7772                }
7773                Transition::Epsilon { target }
7774                | Transition::Predicate { target, .. }
7775                | Transition::Action { target, .. }
7776                | Transition::Precedence { target, .. } => {
7777                    if *target == target_state
7778                        || self.empty_path_reaches_state(atn, *target, target_state, visited)
7779                    {
7780                        return true;
7781                    }
7782                }
7783            }
7784        }
7785        false
7786    }
7787
7788    /// Decides whether a clean one-outcome entry is worth storing in the full
7789    /// outcome memo table for this parse.
7790    fn should_memoize_single_outcome(&mut self, key: &FastRecognizeKey) -> bool {
7791        match self.single_outcome_memo_mode {
7792            SingleOutcomeMemoMode::Promote => true,
7793            SingleOutcomeMemoMode::Sparse => false,
7794            SingleOutcomeMemoMode::Probe => {
7795                self.single_outcome_probe_samples += 1;
7796                if !self.single_outcome_probe_seen.insert(key.clone()) {
7797                    self.single_outcome_probe_repeats += 1;
7798                }
7799                if self.single_outcome_probe_repeats >= CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
7800                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Promote;
7801                    self.single_outcome_probe_seen.clear();
7802                    return true;
7803                }
7804                if self.single_outcome_probe_samples >= CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT {
7805                    self.single_outcome_memo_mode = SingleOutcomeMemoMode::Sparse;
7806                    self.single_outcome_probe_seen.clear();
7807                    return false;
7808                }
7809                true
7810            }
7811        }
7812    }
7813
7814    /// Clones the visible token at an absolute token-stream index.
7815    fn token_at(&mut self, index: usize) -> Option<CommonToken> {
7816        self.input.get(index).cloned()
7817    }
7818
7819    /// Clones the shared token handle at an absolute token-stream index.
7820    fn token_ref_at(&mut self, index: usize) -> Option<TokenRef> {
7821        self.input.get_ref(index)
7822    }
7823
7824    /// Normalizes the current token-stream cursor to the next parser-visible
7825    /// token before capturing a rule start boundary.
7826    fn current_visible_index(&mut self) -> usize {
7827        let index = self.input.index();
7828        self.input.seek(index);
7829        self.input.index()
7830    }
7831
7832    /// Reports whether a child rule reached EOF cleanly while also recording
7833    /// an EOF expectation from a longer path inside that child.
7834    fn child_expected_reaches_clean_eof(
7835        &mut self,
7836        children: &[RecognizeOutcome],
7837        expected: &ExpectedTokens,
7838    ) -> bool {
7839        let Some(index) = expected.index else {
7840            return false;
7841        };
7842        self.token_type_at(index) == TOKEN_EOF
7843            && children
7844                .iter()
7845                .any(|child| child.diagnostics.is_empty() && child.index == index)
7846    }
7847
7848    /// Finds the previous token visible to the parser before `index`.
7849    ///
7850    /// The token stream cursor skips hidden-channel tokens, so subtracting one
7851    /// from a visible-token index can point at whitespace. Parser intervals use
7852    /// this helper to stop at the previous visible token while preserving hidden
7853    /// text inside the rendered interval.
7854    fn previous_token_index(&mut self, index: usize) -> Option<usize> {
7855        self.input.previous_visible_token_index(index)
7856    }
7857
7858    /// Returns the token-stream index used as a rule stop boundary.
7859    ///
7860    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
7861    /// stop at `index` rather than at the previous visible token.
7862    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
7863        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
7864            Some(index)
7865        } else {
7866            self.previous_token_index(index)
7867        }
7868    }
7869
7870    /// Stop-token index for a rule's `@after` action, matching the boundary that
7871    /// `finish_rule` records on the rule context.
7872    ///
7873    /// A rule that matched EOF leaves the cursor parked on the EOF token
7874    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
7875    /// the current index rather than the previous visible token. Without this,
7876    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
7877    /// report the token before EOF (or `None` for empty input), diverging from
7878    /// the rule context that `finish_rule` builds.
7879    ///
7880    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
7881    /// rule ends right before EOF without matching it (the cursor is parked on
7882    /// EOF, but the rule did not consume it). Prefer
7883    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
7884    /// rule context already recorded with the real flag. Kept for callers without
7885    /// the rule tree in hand.
7886    #[must_use]
7887    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
7888        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
7889        self.rule_stop_token_index(current_index, consumed_eof)
7890    }
7891
7892    /// Stop-token index for a rule's `@after` action, taken from the stop token
7893    /// the rule context already recorded.
7894    ///
7895    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
7896    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
7897    /// the rule context — even when the rule ends immediately before EOF without
7898    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
7899    /// to the cursor-based inference only when the tree carries no rule stop.
7900    #[must_use]
7901    pub fn after_action_stop_index_for_tree(
7902        &mut self,
7903        tree: &ParseTree,
7904        current_index: usize,
7905    ) -> Option<usize> {
7906        if let ParseTree::Rule(rule) = tree {
7907            if let Some(stop) = rule.context().stop() {
7908                let token_index = stop.token_index();
7909                if token_index >= 0 {
7910                    return Some(token_index.unsigned_abs());
7911                }
7912            }
7913        }
7914        self.after_action_stop_index(current_index)
7915    }
7916
7917    /// Start-token index for a rule's `@after` action, taken from the start token
7918    /// the rule context already recorded.
7919    ///
7920    /// `enter_rule` sets the rule context start to the first visible token (it
7921    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
7922    /// `$text` in an `@after` action aligned with the rule context — even when the
7923    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
7924    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
7925    /// the tree carries no rule start.
7926    #[must_use]
7927    pub fn after_action_start_index_for_tree(
7928        &self,
7929        tree: &ParseTree,
7930        fallback_index: usize,
7931    ) -> usize {
7932        if let ParseTree::Rule(rule) = tree {
7933            if let Some(start) = rule.context().start() {
7934                let token_index = start.token_index();
7935                if token_index >= 0 {
7936                    return token_index.unsigned_abs();
7937                }
7938            }
7939        }
7940        fallback_index
7941    }
7942
7943    /// Returns the rule stop token for a selected parse path.
7944    ///
7945    /// EOF transitions do not advance the token-stream cursor, so an EOF match
7946    /// must use the current token rather than the previous visible token.
7947    fn rule_stop_token_ref(&mut self, index: usize, consumed_eof: bool) -> Option<TokenRef> {
7948        self.rule_stop_token_index(index, consumed_eof)
7949            .and_then(|token_index| self.token_ref_at(token_index))
7950    }
7951
7952    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
7953    ///
7954    /// Generated Java reports the failed-predicate message at the current
7955    /// lookahead, then consumes until rule recovery can resume. The metadata
7956    /// runtime models the same visible tree shape by keeping skipped tokens as
7957    /// error nodes and returning from the active rule at EOF.
7958    fn predicate_failure_recovery(
7959        &mut self,
7960        request: PredicateFailureRecovery<'_>,
7961    ) -> RecognizeOutcome {
7962        let PredicateFailureRecovery {
7963            rule_index,
7964            index,
7965            message,
7966            member_values,
7967            return_values,
7968            rule_alt_number,
7969        } = request;
7970        let rule_name = self
7971            .rule_names()
7972            .get(rule_index)
7973            .map_or_else(|| rule_index.to_string(), Clone::clone);
7974        let diagnostic = diagnostic_for_token(
7975            self.token_at(index).as_ref(),
7976            format!("rule {rule_name} {message}"),
7977        );
7978        let mut nodes = Vec::new();
7979        let mut next_index = index;
7980        loop {
7981            let symbol = self.token_type_at(next_index);
7982            if symbol == TOKEN_EOF {
7983                break;
7984            }
7985            nodes.push(RecognizedNode::ErrorToken { index: next_index });
7986            let after = self.consume_index(next_index, symbol);
7987            if after == next_index {
7988                break;
7989            }
7990            next_index = after;
7991        }
7992        RecognizeOutcome {
7993            index: next_index,
7994            consumed_eof: false,
7995            alt_number: rule_alt_number,
7996            member_values,
7997            return_values,
7998            diagnostics: vec![diagnostic],
7999            decisions: Vec::new(),
8000            actions: Vec::new(),
8001            nodes,
8002        }
8003    }
8004
8005    /// Evaluates a user hook for a predicate coordinate that has no generated
8006    /// runtime table entry.
8007    fn parser_semantic_hook_result(
8008        &mut self,
8009        request: ParserSemanticHookRequest<'_>,
8010    ) -> Option<bool> {
8011        let ParserSemanticHookRequest {
8012            index,
8013            rule_index,
8014            pred_index,
8015            context,
8016            local_int_arg,
8017            member_values,
8018        } = request;
8019        let rule_name = self.rule_names().get(rule_index).cloned();
8020        self.input.seek(index);
8021        let input = &mut self.input;
8022        let semantic_hooks = &mut self.semantic_hooks;
8023        let mut ctx = ParserSemCtx {
8024            input,
8025            rule_index,
8026            coordinate_index: pred_index,
8027            rule_name,
8028            context,
8029            tree: None,
8030            local_int_arg,
8031            member_values,
8032            action: None,
8033        };
8034        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
8035    }
8036
8037    /// Re-inserts unknown-predicate coordinates recorded before a nested
8038    /// interpreted recognition, preserving order and skipping any the nested
8039    /// call already recorded, so a generated parent's fail-loud coordinates
8040    /// survive descending into an interpreted child.
8041    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
8042        if prior.is_empty() {
8043            return;
8044        }
8045        let mut merged = prior;
8046        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
8047            if !merged.contains(&coordinate) {
8048                merged.push(coordinate);
8049            }
8050        }
8051        self.unknown_predicate_hits = merged;
8052    }
8053
8054    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
8055    /// that has no entry in the generated predicate table.
8056    ///
8057    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
8058    /// the guarded path is abandoned; the parse entry surfaces the recorded
8059    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
8060    /// because a parse that consulted an unknown predicate is unreliable no
8061    /// matter which paths were ultimately selected.
8062    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
8063        apply_unknown_predicate_policy(
8064            self.unknown_predicate_policy,
8065            rule_index,
8066            pred_index,
8067            &mut self.unknown_predicate_hits,
8068        )
8069    }
8070
8071    /// Builds the fail-loud error for unknown predicate coordinates recorded
8072    /// by the current parse, if any.
8073    fn unknown_semantic_error(&self) -> Option<AntlrError> {
8074        use std::fmt::Write as _;
8075        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
8076            return None;
8077        }
8078        let mut message = String::new();
8079        for (rule_index, pred_index) in &self.unknown_predicate_hits {
8080            if !message.is_empty() {
8081                message.push_str("; ");
8082            }
8083            let _ = match self.rule_names().get(*rule_index) {
8084                Some(rule_name) => write!(
8085                    message,
8086                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
8087                ),
8088                None => write!(
8089                    message,
8090                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
8091                ),
8092            };
8093        }
8094        for (rule_index, source_state) in &self.unhandled_action_hits {
8095            if !message.is_empty() {
8096                message.push_str("; ");
8097            }
8098            let _ = match self.rule_names().get(*rule_index) {
8099                Some(rule_name) => write!(
8100                    message,
8101                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
8102                ),
8103                None => write!(
8104                    message,
8105                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
8106                ),
8107            };
8108        }
8109        Some(AntlrError::Unsupported(message))
8110    }
8111
8112    /// Evaluates one lowered predicate expression at the requested input
8113    /// position.
8114    ///
8115    /// This sits in the prediction hot loop, so the context borrows the
8116    /// speculative member state read-only and the rule name by reference —
8117    /// no per-evaluation allocation. Only the hook escape path materializes
8118    /// owned copies, and only when a hook is actually consulted.
8119    fn parser_semir_predicate_matches(
8120        &mut self,
8121        semantics: &ParserSemantics,
8122        predicate: &ParserSemanticPredicate,
8123        request: ParserSemanticHookRequest<'_>,
8124    ) -> bool {
8125        self.input.seek(request.index);
8126        let rule_name = self
8127            .data
8128            .rule_names()
8129            .get(request.rule_index)
8130            .map(String::as_str);
8131        let unknown_predicate_policy = self.unknown_predicate_policy;
8132        let mut ctx = ParserSemIrCtx {
8133            input: &mut self.input,
8134            semantic_hooks: &mut self.semantic_hooks,
8135            rule_index: request.rule_index,
8136            coordinate_index: request.pred_index,
8137            rule_name,
8138            context: request.context,
8139            local_int_arg: request.local_int_arg,
8140            member_values: request.member_values,
8141            invoked_predicates: &mut self.invoked_predicates,
8142            unknown_predicate_policy,
8143            unknown_predicate_hits: &mut self.unknown_predicate_hits,
8144        };
8145        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
8146    }
8147
8148    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
8149        let PredicateEval {
8150            index,
8151            rule_index,
8152            pred_index,
8153            predicates,
8154            semantics,
8155            context,
8156            local_int_arg,
8157            member_values,
8158        } = eval;
8159        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
8160            semantics
8161                .predicates
8162                .iter()
8163                .find(|predicate| {
8164                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
8165                })
8166                .map(|predicate| (semantics, predicate))
8167        }) {
8168            return self.parser_semir_predicate_matches(
8169                semantics,
8170                predicate,
8171                ParserSemanticHookRequest {
8172                    index,
8173                    rule_index,
8174                    pred_index,
8175                    context,
8176                    local_int_arg,
8177                    member_values,
8178                },
8179            );
8180        }
8181        let Some((_, _, predicate)) = predicates
8182            .iter()
8183            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
8184        else {
8185            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
8186                index,
8187                rule_index,
8188                pred_index,
8189                context,
8190                local_int_arg,
8191                member_values,
8192            }) {
8193                return result;
8194            }
8195            return self.unknown_predicate_result(rule_index, pred_index);
8196        };
8197        self.input.seek(index);
8198        match predicate {
8199            ParserPredicate::True => true,
8200            ParserPredicate::False => false,
8201            ParserPredicate::FalseWithMessage { .. } => false,
8202            ParserPredicate::Invoke { value } => {
8203                let key = (rule_index, pred_index);
8204                if !self.invoked_predicates.contains(&key) {
8205                    self.invoked_predicates.push(key);
8206                    use std::io::Write as _;
8207                    let mut stdout = std::io::stdout().lock();
8208                    let _ = writeln!(stdout, "eval={value}");
8209                }
8210                *value
8211            }
8212            ParserPredicate::LookaheadTextEquals { offset, text } => {
8213                self.input.lt(*offset).and_then(Token::text) == Some(*text)
8214            }
8215            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
8216                self.la(*offset) != *token_type
8217            }
8218            ParserPredicate::TokenPairAdjacent => {
8219                let Some(first) = self.input.lt(-2).map(Token::token_index) else {
8220                    return false;
8221                };
8222                let Some(second) = self.input.lt(-1).map(Token::token_index) else {
8223                    return false;
8224                };
8225                first + 1 == second
8226            }
8227            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
8228                .and_then(|context| {
8229                    context.children().iter().find_map(|child| match child {
8230                        ParseTree::Rule(rule) if rule.context().rule_index() == *rule_index => {
8231                            Some(child.text())
8232                        }
8233                        ParseTree::Rule(_) | ParseTree::Terminal(_) | ParseTree::Error(_) => None,
8234                    })
8235                })
8236                .is_none_or(|actual| actual != *text),
8237            ParserPredicate::LocalIntEquals { value } => {
8238                local_int_arg.is_none_or(|(_, actual)| actual == *value)
8239            }
8240            ParserPredicate::LocalIntLessOrEqual { value } => {
8241                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
8242            }
8243            ParserPredicate::MemberModuloEquals {
8244                member,
8245                modulus,
8246                value,
8247                equals,
8248            } => {
8249                if *modulus == 0 {
8250                    return false;
8251                }
8252                let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
8253                (actual == *value) == *equals
8254            }
8255            ParserPredicate::MemberEquals {
8256                member,
8257                value,
8258                equals,
8259            } => {
8260                let actual = member_values.get(member).copied().unwrap_or_default();
8261                (actual == *value) == *equals
8262            }
8263        }
8264    }
8265
8266    /// Returns a generated fail-option message for a predicate coordinate.
8267    fn parser_predicate_failure_message(
8268        &self,
8269        rule_index: usize,
8270        pred_index: usize,
8271        predicates: &[(usize, usize, ParserPredicate)],
8272    ) -> Option<&'static str> {
8273        predicates
8274            .iter()
8275            .find_map(|(rule, pred, predicate)| match predicate {
8276                ParserPredicate::FalseWithMessage { message }
8277                    if *rule == rule_index && *pred == pred_index =>
8278                {
8279                    Some(*message)
8280                }
8281                _ => None,
8282            })
8283    }
8284
8285    /// Returns a generated fail-option message for a `SemIR` predicate
8286    /// coordinate.
8287    pub fn parser_semantic_ir_predicate_failure_message(
8288        &self,
8289        rule_index: usize,
8290        pred_index: usize,
8291        semantics: &ParserSemantics,
8292    ) -> Option<&'static str> {
8293        semantics
8294            .predicates
8295            .iter()
8296            .find(|predicate| {
8297                predicate.rule_index == rule_index && predicate.pred_index == pred_index
8298            })
8299            .and_then(|predicate| predicate.failure_message)
8300    }
8301
8302    /// Returns the token-stream index after consuming `symbol` at `index`.
8303    ///
8304    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
8305    /// index stable and rely on `consumed_eof` to record that EOF was matched.
8306    /// The parser's stream cursor is left untouched: speculative recognition
8307    /// reads ahead by absolute index, so paying for `seek` on every visited
8308    /// state would dominate the hot path. Real consumption is committed by
8309    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
8310    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
8311        if symbol == TOKEN_EOF {
8312            return index;
8313        }
8314        self.input.next_visible_after(index)
8315    }
8316
8317    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
8318    /// decision that failed after consuming a shared prefix.
8319    fn no_viable_alternative(
8320        &mut self,
8321        start_index: usize,
8322        error_index: usize,
8323    ) -> ParserDiagnostic {
8324        let text = display_input_text(&self.input.text(start_index, error_index));
8325        diagnostic_for_token(
8326            self.token_at(error_index).as_ref(),
8327            format!("no viable alternative at input '{text}'"),
8328        )
8329    }
8330
8331    /// Selects the diagnostic for a failed consuming transition after all
8332    /// recovery repairs have been ruled out.
8333    fn recovery_failure_diagnostic(
8334        &mut self,
8335        index: usize,
8336        decision_start_index: Option<usize>,
8337        expected_symbols: &BTreeSet<i32>,
8338    ) -> ParserDiagnostic {
8339        if expected_symbols.len() > 1 {
8340            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
8341                return self.no_viable_alternative(decision_start, index);
8342            }
8343        }
8344        diagnostic_for_token(
8345            self.token_at(index).as_ref(),
8346            format!(
8347                "mismatched input {} expecting {}",
8348                self.token_at(index)
8349                    .as_ref()
8350                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8351                self.expected_symbols_display(expected_symbols)
8352            ),
8353        )
8354    }
8355
8356    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
8357    /// instead of inserting missing tokens in the caller.
8358    fn eof_rule_recovery_diagnostic(
8359        &mut self,
8360        index: usize,
8361        expected_symbols: &BTreeSet<i32>,
8362        expected: &ExpectedTokens,
8363    ) -> ParserDiagnostic {
8364        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
8365            &expected.symbols
8366        } else {
8367            expected_symbols
8368        };
8369        diagnostic_for_token(
8370            self.token_at(index).as_ref(),
8371            format!(
8372                "mismatched input {} expecting {}",
8373                self.token_at(index)
8374                    .as_ref()
8375                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8376                self.expected_symbols_display(symbols)
8377            ),
8378        )
8379    }
8380
8381    /// Returns token text for a buffered token interval used by generated
8382    /// `$text` actions.
8383    ///
8384    /// ANTLR treats EOF as a range boundary rather than printable input text,
8385    /// even when an action interval explicitly stops at the EOF token.
8386    pub fn text_interval(&mut self, start: usize, stop: Option<usize>) -> String {
8387        let Some(stop) = stop else {
8388            return String::new();
8389        };
8390        let stop = if self
8391            .token_at(stop)
8392            .is_some_and(|token| token.token_type() == TOKEN_EOF)
8393        {
8394            let Some(previous) = self.previous_token_index(stop) else {
8395                return String::new();
8396            };
8397            previous
8398        } else {
8399            stop
8400        };
8401        self.input.text(start, stop)
8402    }
8403
8404    /// Resets per-parse prediction diagnostics while keeping the parser-level
8405    /// reporting flag configured by generated harness code.
8406    fn clear_prediction_diagnostics(&mut self) {
8407        self.prediction_diagnostics.clear();
8408        self.reported_prediction_diagnostics.clear();
8409    }
8410
8411    /// Drops every per-parse cache that depends on ATN identity or pins
8412    /// recovery-symbol allocations.
8413    ///
8414    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
8415    /// same parser instance can legally be driven against different grammars
8416    /// in sequence. The four caches reset here are keyed by raw ATN
8417    /// coordinates (state numbers, rule indexes) and would silently hand back
8418    /// entries from a previous ATN if reused — pruning lookahead against the
8419    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
8420    /// for the rest of the process. Clearing them on every parse entry keeps
8421    /// the perf wins (caches still amortize within one parse) without making
8422    /// long-lived parsers leak memory or surface stale ATN data:
8423    ///
8424    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
8425    ///   functions of the ATN's state graph.
8426    /// * `state_expected_cache`, `state_expected_token_cache`,
8427    ///   `rule_stop_reach_cache`, and
8428    ///   `recovery_symbols_intern` together form
8429    ///   the identity invariant that lets `FastRecognizeKey` hash
8430    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
8431    ///   so a stale interned `Rc` cannot outlive its map entry.
8432    fn reset_per_parse_caches(&mut self) {
8433        self.rule_first_set_cache.clear();
8434        self.decision_lookahead_cache.clear();
8435        self.ll1_decision_cache.clear();
8436        self.empty_cycle_cache.clear();
8437        self.rule_stop_reach_cache.clear();
8438        self.single_outcome_memo_mode = SingleOutcomeMemoMode::Probe;
8439        self.single_outcome_probe_seen.clear();
8440        self.single_outcome_probe_samples = 0;
8441        self.single_outcome_probe_repeats = 0;
8442        self.recovery_symbols_intern.clear();
8443        self.state_expected_cache.clear();
8444        self.state_expected_token_cache.clear();
8445    }
8446
8447    /// Buffers ANTLR-style diagnostic-listener messages for decision states
8448    /// where multiple clean alternatives survive full-context recognition.
8449    fn record_prediction_diagnostics(
8450        &mut self,
8451        atn: &Atn,
8452        state: &AtnState,
8453        start_index: usize,
8454        outcomes: &[RecognizeOutcome],
8455    ) {
8456        if !self.report_diagnostic_errors || state.transitions.len() < 2 {
8457            return;
8458        }
8459        let Some(decision) = atn
8460            .decision_to_state()
8461            .iter()
8462            .position(|state_number| *state_number == state.state_number)
8463        else {
8464            return;
8465        };
8466        let Some(rule_index) = state.rule_index else {
8467            return;
8468        };
8469        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
8470        for outcome in outcomes
8471            .iter()
8472            .filter(|outcome| outcome.diagnostics.is_empty())
8473        {
8474            let Some(alt) = outcome.decisions.first() else {
8475                continue;
8476            };
8477            alts_by_end
8478                .entry(outcome.index)
8479                .or_default()
8480                .insert(alt + 1);
8481        }
8482        let Some((&end_index, ambig_alts)) = alts_by_end
8483            .iter()
8484            .filter(|(_, alts)| alts.len() > 1)
8485            .max_by_key(|(end, _)| *end)
8486        else {
8487            return;
8488        };
8489        let rule_name = self
8490            .rule_names()
8491            .get(rule_index)
8492            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
8493        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
8494        let input = display_input_text(&self.input.text(start_index, stop_index));
8495        let alts = ambig_alts
8496            .iter()
8497            .map(usize::to_string)
8498            .collect::<Vec<_>>()
8499            .join(", ");
8500        let key = (decision, start_index, format!("{alts}:{input}"));
8501        if !self.reported_prediction_diagnostics.insert(key) {
8502            return;
8503        }
8504        let start_token = self.token_at(start_index);
8505        let stop_token = self.token_at(stop_index);
8506        self.prediction_diagnostics.push(diagnostic_for_token(
8507            start_token.as_ref(),
8508            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
8509        ));
8510        self.prediction_diagnostics.push(diagnostic_for_token(
8511            stop_token.as_ref(),
8512            format!(
8513                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
8514            ),
8515        ));
8516    }
8517
8518    /// Formats the tokens expected from an ATN state using ANTLR display names.
8519    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
8520        expected_symbols_display(
8521            &state_expected_symbols(atn, state_number),
8522            self.vocabulary(),
8523        )
8524    }
8525
8526    /// Expected-token set at the parser's current ATN state — ANTLR's
8527    /// `getExpectedTokens()`. Generated recognizers expose this as
8528    /// `self.expected_tokens()` for embedded test actions
8529    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
8530    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
8531        let state = usize::try_from(self.data().state()).unwrap_or(0);
8532        ExpectedTokenSet {
8533            symbols: state_expected_symbols(atn, state),
8534        }
8535    }
8536
8537    /// Enables the bail error strategy: the first syntax error aborts the
8538    /// parse instead of recovering.
8539    pub const fn set_bail_on_error(&mut self, bail: bool) {
8540        self.bail_on_error = bail;
8541    }
8542
8543    /// Whether the bail error strategy is active.
8544    #[must_use]
8545    pub const fn bail_on_error(&self) -> bool {
8546        self.bail_on_error
8547    }
8548
8549    /// Names of the rules on the live invocation stack, current rule first —
8550    /// ANTLR's `getRuleInvocationStack()`.
8551    pub fn rule_invocation_stack(&self) -> Vec<String> {
8552        self.rule_context_stack
8553            .iter()
8554            .rev()
8555            .map(|frame| {
8556                self.data()
8557                    .rule_names()
8558                    .get(frame.rule_index)
8559                    .cloned()
8560                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
8561            })
8562            .collect()
8563    }
8564
8565    /// Formats a buffered token in ANTLR's diagnostic token display form.
8566    pub fn token_display_at(&mut self, index: usize) -> Option<String> {
8567        self.token_at(index).map(|token| format!("{token}"))
8568    }
8569
8570    /// Converts a recognized internal node into a public parse-tree node.
8571    fn recognized_node_tree(
8572        &mut self,
8573        node: &RecognizedNode,
8574        track_alt_numbers: bool,
8575    ) -> Result<ParseTree, AntlrError> {
8576        match node {
8577            RecognizedNode::Token { index } => {
8578                let token = self
8579                    .input
8580                    .get_ref(*index)
8581                    .ok_or_else(|| AntlrError::ParserError {
8582                        line: 0,
8583                        column: 0,
8584                        message: format!("missing token at index {index}"),
8585                    })?;
8586                Ok(ParseTree::Terminal(TerminalNode::from_ref(token)))
8587            }
8588            RecognizedNode::ErrorToken { index } => {
8589                let token = self
8590                    .input
8591                    .get_ref(*index)
8592                    .ok_or_else(|| AntlrError::ParserError {
8593                        line: 0,
8594                        column: 0,
8595                        message: format!("missing error token at index {index}"),
8596                    })?;
8597                Ok(ParseTree::Error(ErrorNode::from_ref(token)))
8598            }
8599            RecognizedNode::MissingToken {
8600                token_type,
8601                at_index,
8602                text,
8603            } => {
8604                let current = self.token_at(*at_index);
8605                let token = CommonToken::new(*token_type)
8606                    .with_text(text.as_str())
8607                    .with_span(usize::MAX, usize::MAX)
8608                    .with_position(
8609                        current.as_ref().map(Token::line).unwrap_or_default(),
8610                        current.as_ref().map(Token::column).unwrap_or_default(),
8611                    );
8612                Ok(ParseTree::Error(ErrorNode::new(token)))
8613            }
8614            RecognizedNode::Rule {
8615                rule_index,
8616                invoking_state,
8617                alt_number,
8618                start_index,
8619                stop_index,
8620                return_values,
8621                children,
8622            } => {
8623                let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
8624                if track_alt_numbers {
8625                    context.set_alt_number(*alt_number);
8626                }
8627                for (name, value) in return_values {
8628                    context.set_int_return(name.clone(), *value);
8629                }
8630                if let Some(token) = self.token_ref_at(*start_index) {
8631                    context.set_start_ref(token);
8632                }
8633                if let Some(token) = stop_index.and_then(|index| self.token_ref_at(index)) {
8634                    context.set_stop_ref(token);
8635                }
8636                for child in children {
8637                    context.add_child(self.recognized_node_tree(child, track_alt_numbers)?);
8638                }
8639                Ok(self.rule_node(context))
8640            }
8641            RecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
8642                format!("unfolded left-recursive boundary for rule {rule_index}"),
8643            )),
8644        }
8645    }
8646}
8647
8648impl<S, H> DirectAdaptiveParser<'_, '_, S, H>
8649where
8650    S: TokenSource,
8651    H: SemanticHooks,
8652{
8653    fn parse_rule(
8654        &mut self,
8655        rule_index: usize,
8656        invoking_state: isize,
8657        precedence: i32,
8658    ) -> DirectAdaptiveParseResult<ParseTree> {
8659        let start_state = *self.atn.rule_to_start_state().get(rule_index).ok_or(
8660            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
8661        )?;
8662        let stop_state = *self
8663            .atn
8664            .rule_to_stop_state()
8665            .get(rule_index)
8666            .filter(|state| **state != usize::MAX)
8667            .ok_or(DirectAdaptiveParseControl::Fallback(
8668                DirectAdaptiveFallback::MissingAtn,
8669            ))?;
8670        let start_index = self.parser.current_visible_index();
8671        let mut children = Vec::new();
8672        let mut state_number = start_state;
8673        let mut consumed_eof = false;
8674        while state_number != stop_state {
8675            self.step()?;
8676            let (transition, boundary) = self.next_transition(state_number, precedence)?;
8677            if boundary.is_some() {
8678                return Err(DirectAdaptiveParseControl::Fallback(
8679                    DirectAdaptiveFallback::LeftRecursiveBoundary,
8680                ));
8681            }
8682            match transition {
8683                Transition::Epsilon { target } => {
8684                    state_number = target;
8685                }
8686                Transition::Precedence {
8687                    target,
8688                    precedence: transition_precedence,
8689                } => {
8690                    if transition_precedence < precedence {
8691                        return Err(DirectAdaptiveParseControl::Fallback(
8692                            DirectAdaptiveFallback::Precedence,
8693                        ));
8694                    }
8695                    state_number = target;
8696                }
8697                Transition::Rule {
8698                    rule_index,
8699                    follow_state,
8700                    precedence: rule_precedence,
8701                    ..
8702                } => {
8703                    let child = self.parse_rule(
8704                        rule_index,
8705                        invoking_state_number(state_number),
8706                        rule_precedence,
8707                    )?;
8708                    if self.parser.build_parse_trees {
8709                        children.push(child);
8710                    }
8711                    state_number = follow_state;
8712                }
8713                Transition::Atom { .. }
8714                | Transition::Range { .. }
8715                | Transition::Set { .. }
8716                | Transition::NotSet { .. }
8717                | Transition::Wildcard { .. } => {
8718                    let (matched_eof, child) = self.consume_transition(&transition)?;
8719                    consumed_eof |= matched_eof;
8720                    if let Some(child) = child {
8721                        children.push(child);
8722                    }
8723                    state_number = transition.target();
8724                }
8725                Transition::Predicate { .. } => {
8726                    return Err(DirectAdaptiveParseControl::Fallback(
8727                        DirectAdaptiveFallback::Predicate,
8728                    ));
8729                }
8730                Transition::Action { .. } => {
8731                    return Err(DirectAdaptiveParseControl::Fallback(
8732                        DirectAdaptiveFallback::Action,
8733                    ));
8734                }
8735            }
8736        }
8737
8738        let mut context = ParserRuleContext::with_child_capacity(
8739            rule_index,
8740            invoking_state,
8741            if self.parser.build_parse_trees {
8742                children.len()
8743            } else {
8744                0
8745            },
8746        );
8747        if let Some(token) = self.parser.token_ref_at(start_index) {
8748            context.set_start_ref(token);
8749        }
8750        let stop_index = self
8751            .parser
8752            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
8753        if let Some(token) = stop_index.and_then(|index| self.parser.token_ref_at(index)) {
8754            context.set_stop_ref(token);
8755        }
8756        if self.parser.build_parse_trees {
8757            for child in children {
8758                context.add_child(child);
8759            }
8760        }
8761        Ok(self.parser.rule_node(context))
8762    }
8763
8764    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
8765        self.steps += 1;
8766        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
8767            return Err(DirectAdaptiveParseControl::Fallback(
8768                DirectAdaptiveFallback::StepLimit,
8769            ));
8770        }
8771        Ok(())
8772    }
8773
8774    fn next_transition(
8775        &mut self,
8776        state_number: usize,
8777        precedence: i32,
8778    ) -> DirectAdaptiveParseResult<(Transition, Option<usize>)> {
8779        let state = self
8780            .atn
8781            .state(state_number)
8782            .ok_or(DirectAdaptiveParseControl::Fallback(
8783                DirectAdaptiveFallback::MissingAtn,
8784            ))?;
8785        if state.is_rule_stop() {
8786            return Err(DirectAdaptiveParseControl::Fallback(
8787                DirectAdaptiveFallback::RuleStop,
8788            ));
8789        }
8790        let transition_index =
8791            self.transition_index(state_number, state.transitions.len(), precedence)?;
8792        let transition = state.transitions.get(transition_index).cloned().ok_or(
8793            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
8794        )?;
8795        let boundary = match &transition {
8796            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
8797                left_recursive_boundary(self.atn, state, *target)
8798            }
8799            _ => None,
8800        };
8801        Ok((transition, boundary))
8802    }
8803
8804    fn transition_index(
8805        &mut self,
8806        state_number: usize,
8807        transition_count: usize,
8808        precedence: i32,
8809    ) -> DirectAdaptiveParseResult<usize> {
8810        match transition_count {
8811            0 => Err(DirectAdaptiveParseControl::Fallback(
8812                DirectAdaptiveFallback::NoTransition,
8813            )),
8814            1 => Ok(0),
8815            _ => {
8816                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
8817                    return Ok(alt);
8818                }
8819                let decision = self
8820                    .decision_by_state
8821                    .get(state_number)
8822                    .and_then(|decision| *decision)
8823                    .ok_or(DirectAdaptiveParseControl::Fallback(
8824                        DirectAdaptiveFallback::UnknownDecision,
8825                    ))?;
8826                let prediction = self
8827                    .simulator
8828                    .adaptive_predict_stream_info_with_precedence(
8829                        decision,
8830                        direct_precedence(precedence),
8831                        &mut self.parser.input,
8832                    )
8833                    .map_err(|_| {
8834                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
8835                    })?;
8836                if prediction.has_semantic_context {
8837                    return Err(DirectAdaptiveParseControl::Fallback(
8838                        DirectAdaptiveFallback::SemanticContext,
8839                    ));
8840                }
8841                prediction
8842                    .alt
8843                    .checked_sub(1)
8844                    .filter(|index| *index < transition_count)
8845                    .ok_or(DirectAdaptiveParseControl::Fallback(
8846                        DirectAdaptiveFallback::InvalidAlt,
8847                    ))
8848            }
8849        }
8850    }
8851
8852    fn ll1_transition_index(
8853        &mut self,
8854        state_number: usize,
8855        transition_count: usize,
8856    ) -> DirectAdaptiveParseResult<Option<usize>> {
8857        let state = self
8858            .atn
8859            .state(state_number)
8860            .ok_or(DirectAdaptiveParseControl::Fallback(
8861                DirectAdaptiveFallback::MissingAtn,
8862            ))?;
8863        if state.precedence_rule_decision {
8864            return Ok(None);
8865        }
8866        let Some(rule_stop) = state
8867            .rule_index
8868            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index).copied())
8869        else {
8870            return Ok(None);
8871        };
8872        let symbol = self.parser.input.la_token(1);
8873        let entry = self
8874            .parser
8875            .cached_decision_lookahead(self.atn, state, rule_stop);
8876        Ok(ll1_greedy_alt(&entry, symbol, state.non_greedy).filter(|alt| *alt < transition_count))
8877    }
8878
8879    fn consume_transition(
8880        &mut self,
8881        transition: &Transition,
8882    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
8883        let symbol = self.parser.input.la_token(1);
8884        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
8885            return Err(DirectAdaptiveParseControl::Fallback(
8886                DirectAdaptiveFallback::TokenMismatch,
8887            ));
8888        }
8889        let token = self
8890            .parser
8891            .input
8892            .lt_ref(1)
8893            .ok_or(DirectAdaptiveParseControl::Fallback(
8894                DirectAdaptiveFallback::TokenMismatch,
8895            ))?;
8896        let matched_eof = symbol == TOKEN_EOF;
8897        if !matched_eof {
8898            self.parser.consume();
8899        }
8900        let child = self
8901            .parser
8902            .build_parse_trees
8903            .then(|| ParseTree::Terminal(TerminalNode::from_ref(token)));
8904        Ok((matched_eof, child))
8905    }
8906}
8907
8908/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
8909/// transformed left-recursive rule.
8910fn left_recursive_boundary(atn: &Atn, state: &AtnState, target: usize) -> Option<usize> {
8911    if !state.precedence_rule_decision {
8912        return None;
8913    }
8914    let target_state = atn.state(target)?;
8915    if target_state.kind == AtnStateKind::LoopEnd {
8916        return None;
8917    }
8918    state.rule_index
8919}
8920
8921/// Selects the first outer alternative observed for a rule path.
8922///
8923/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
8924/// outer decision. The metadata recognizer only needs this when a generated
8925/// grammar opts into that target template; otherwise the value remains `0` and
8926/// parse-tree rendering is unchanged.
8927const fn next_alt_number(
8928    state: &AtnState,
8929    transition_index: usize,
8930    current_alt_number: usize,
8931    track_alt_numbers: bool,
8932) -> usize {
8933    if !track_alt_numbers || current_alt_number != 0 || state.transitions.len() <= 1 {
8934        return current_alt_number;
8935    }
8936    if matches!(
8937        state.kind,
8938        AtnStateKind::Basic
8939            | AtnStateKind::BlockStart
8940            | AtnStateKind::PlusBlockStart
8941            | AtnStateKind::StarBlockStart
8942            | AtnStateKind::StarLoopEntry
8943    ) && !state.precedence_rule_decision
8944    {
8945        return transition_index + 1;
8946    }
8947    current_alt_number
8948}
8949
8950/// Folds boundary markers emitted at precedence-loop entries into nested rule
8951/// nodes, matching ANTLR's recursive-context parse-tree shape.
8952fn fold_left_recursive_boundaries(nodes: Vec<RecognizedNode>) -> Vec<RecognizedNode> {
8953    let mut folded = Vec::new();
8954    for node in nodes {
8955        match node {
8956            RecognizedNode::LeftRecursiveBoundary { rule_index } => {
8957                if !folded.is_empty() {
8958                    let children = std::mem::take(&mut folded);
8959                    let start_index = recognized_nodes_start_index(&children).unwrap_or_default();
8960                    let stop_index = recognized_nodes_stop_index(&children);
8961                    folded.push(RecognizedNode::Rule {
8962                        rule_index,
8963                        invoking_state: -1,
8964                        alt_number: 0,
8965                        start_index,
8966                        stop_index,
8967                        return_values: BTreeMap::new(),
8968                        children,
8969                    });
8970                }
8971            }
8972            node => folded.push(node),
8973        }
8974    }
8975    folded
8976}
8977
8978/// Mirrors [`fold_left_recursive_boundaries`] for [`FastRecognizedNode`].
8979fn fold_fast_left_recursive_boundaries(
8980    nodes: Vec<Rc<FastRecognizedNode>>,
8981) -> Vec<Rc<FastRecognizedNode>> {
8982    // Most rule invocations have no left-recursive boundaries, so skip the
8983    // fold work entirely when none are present. The boundary marker is only
8984    // emitted at precedence-rule loop entries, which are rare relative to
8985    // every rule call the recognizer fans out.
8986    if !nodes.iter().any(|node| {
8987        matches!(
8988            node.as_ref(),
8989            FastRecognizedNode::LeftRecursiveBoundary { .. }
8990        )
8991    }) {
8992        return nodes;
8993    }
8994    let mut folded: Vec<Rc<FastRecognizedNode>> = Vec::with_capacity(nodes.len());
8995    for node in nodes {
8996        match node.as_ref() {
8997            FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
8998                if !folded.is_empty() {
8999                    let children = std::mem::take(&mut folded);
9000                    let start_index =
9001                        fast_recognized_nodes_start_index(&children).unwrap_or_default();
9002                    let stop_index = fast_recognized_nodes_stop_index(&children);
9003                    folded.push(Rc::new(FastRecognizedNode::Rule {
9004                        rule_index: *rule_index,
9005                        invoking_state: -1,
9006                        start_index,
9007                        stop_index,
9008                        children: NodeList::from_vec(children),
9009                    }));
9010                }
9011            }
9012            _ => folded.push(node),
9013        }
9014    }
9015    folded
9016}
9017
9018fn fast_node_has_left_recursive_boundary(node: &FastRecognizedNode) -> bool {
9019    match node {
9020        FastRecognizedNode::LeftRecursiveBoundary { .. } => true,
9021        FastRecognizedNode::Rule { children, .. } => children.has_left_recursive_boundary(),
9022        FastRecognizedNode::Token { .. }
9023        | FastRecognizedNode::ErrorToken { .. }
9024        | FastRecognizedNode::MissingToken { .. } => false,
9025    }
9026}
9027
9028fn fast_recognized_nodes_start_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
9029    nodes
9030        .iter()
9031        .find_map(|node| fast_recognized_node_start_index(node.as_ref()))
9032}
9033
9034const fn fast_recognized_node_start_index(node: &FastRecognizedNode) -> Option<usize> {
9035    match node {
9036        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9037            Some(*index)
9038        }
9039        FastRecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
9040        FastRecognizedNode::Rule { start_index, .. } => Some(*start_index),
9041        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9042    }
9043}
9044
9045const fn fast_recognized_node_span(node: &FastRecognizedNode) -> Option<(usize, Option<usize>)> {
9046    match node {
9047        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9048            Some((*index, Some(*index)))
9049        }
9050        FastRecognizedNode::MissingToken { at_index, .. } => Some((*at_index, None)),
9051        FastRecognizedNode::Rule {
9052            start_index,
9053            stop_index,
9054            ..
9055        } => Some((*start_index, *stop_index)),
9056        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9057    }
9058}
9059
9060fn fast_recognized_nodes_stop_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
9061    nodes
9062        .iter()
9063        .rev()
9064        .find_map(|node| fast_recognized_node_stop_index(node.as_ref()))
9065}
9066
9067const fn fast_recognized_node_stop_index(node: &FastRecognizedNode) -> Option<usize> {
9068    match node {
9069        FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
9070            Some(*index)
9071        }
9072        FastRecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
9073        FastRecognizedNode::Rule { stop_index, .. } => *stop_index,
9074        FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
9075    }
9076}
9077
9078fn recognized_nodes_start_index(nodes: &[RecognizedNode]) -> Option<usize> {
9079    nodes.iter().find_map(recognized_node_start_index)
9080}
9081
9082const fn recognized_node_start_index(node: &RecognizedNode) -> Option<usize> {
9083    match node {
9084        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
9085        RecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
9086        RecognizedNode::Rule { start_index, .. } => Some(*start_index),
9087        RecognizedNode::LeftRecursiveBoundary { .. } => None,
9088    }
9089}
9090
9091fn recognized_nodes_stop_index(nodes: &[RecognizedNode]) -> Option<usize> {
9092    nodes.iter().rev().find_map(recognized_node_stop_index)
9093}
9094
9095/// Converts an ATN state number into the signed invoking-state slot used by
9096/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
9097fn invoking_state_number(state_number: usize) -> isize {
9098    isize::try_from(state_number).unwrap_or(isize::MAX)
9099}
9100
9101fn direct_precedence(precedence: i32) -> usize {
9102    usize::try_from(precedence.max(0)).unwrap_or_default()
9103}
9104
9105const fn recognized_node_stop_index(node: &RecognizedNode) -> Option<usize> {
9106    match node {
9107        RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
9108        RecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
9109        RecognizedNode::Rule { stop_index, .. } => *stop_index,
9110        RecognizedNode::LeftRecursiveBoundary { .. } => None,
9111    }
9112}
9113
9114fn token_input_display(token: &impl Token) -> String {
9115    format!("'{}'", token.text().unwrap_or("<EOF>"))
9116}
9117
9118fn display_input_text(text: &str) -> String {
9119    let mut out = String::new();
9120    for ch in text.chars() {
9121        match ch {
9122            '\n' => out.push_str("\\n"),
9123            '\r' => out.push_str("\\r"),
9124            '\t' => out.push_str("\\t"),
9125            other => out.push(other),
9126        }
9127    }
9128    out
9129}
9130
9131fn diagnostic_for_token(token: Option<&impl Token>, message: String) -> ParserDiagnostic {
9132    ParserDiagnostic {
9133        line: token.map(Token::line).unwrap_or_default(),
9134        column: token.map(Token::column).unwrap_or_default(),
9135        message,
9136    }
9137}
9138
9139/// Emits parser diagnostics for the selected recovered parse path.
9140#[allow(clippy::print_stderr)]
9141fn report_parser_diagnostics(diagnostics: &[ParserDiagnostic]) {
9142    for diagnostic in diagnostics {
9143        eprintln!(
9144            "line {}:{} {}",
9145            diagnostic.line, diagnostic.column, diagnostic.message
9146        );
9147    }
9148}
9149
9150/// Emits generated parser diagnostics and lexer diagnostics in the same
9151/// source-position order as ANTLR's lazy token stream reports them.
9152#[allow(clippy::print_stderr)]
9153fn report_generated_diagnostics(
9154    parser_diagnostics: &[ParserDiagnostic],
9155    token_errors: &[TokenSourceError],
9156) {
9157    // Parser diagnostics keep their event order: Java's console and
9158    // DiagnosticErrorListener print reports as prediction produces them, so
9159    // `reportAttemptingFullContext` precedes `reportContextSensitivity` even
9160    // though the latter's position is earlier. Buffered token-source errors
9161    // interleave by source position — ANTLR's lazy token stream surfaces a
9162    // lexer error when the parser first fetches that token — and win ties.
9163    let mut token_iter = token_errors.iter().peekable();
9164    for diagnostic in parser_diagnostics {
9165        while let Some(error) = token_iter.peek() {
9166            if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
9167                eprintln!("line {}:{} {}", error.line, error.column, error.message);
9168                token_iter.next();
9169            } else {
9170                break;
9171            }
9172        }
9173        eprintln!(
9174            "line {}:{} {}",
9175            diagnostic.line, diagnostic.column, diagnostic.message
9176        );
9177    }
9178    for error in token_iter {
9179        eprintln!("line {}:{} {}", error.line, error.column, error.message);
9180    }
9181}
9182
9183/// Emits buffered token-source diagnostics after parser diagnostics that were
9184/// discovered while speculatively reading the same token stream.
9185#[allow(clippy::print_stderr)]
9186fn report_token_source_errors(errors: &[TokenSourceError]) {
9187    for error in errors {
9188        eprintln!("line {}:{} {}", error.line, error.column, error.message);
9189    }
9190}
9191
9192fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
9193    let items = symbols
9194        .iter()
9195        .map(|symbol| expected_symbol_display(*symbol, vocabulary))
9196        .collect::<Vec<_>>();
9197    if let [single] = items.as_slice() {
9198        return single.clone();
9199    }
9200    format!("{{{}}}", items.join(", "))
9201}
9202
9203fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
9204    if symbol == TOKEN_EOF {
9205        return "<EOF>".to_owned();
9206    }
9207    vocabulary.display_name(symbol)
9208}
9209
9210fn is_caller_follow_boundary_text(text: &str) -> bool {
9211    text.chars().any(|ch| ch == ';' || ch == '\n')
9212        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
9213}
9214
9215fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
9216    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
9217}
9218
9219/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
9220/// Inline insertion in those precedence loops can synthesize a missing operand
9221/// before an operator and then block the legitimate loop-exit path.
9222fn state_is_left_recursive_rule(atn: &Atn, state: &AtnState) -> bool {
9223    let Some(rule_index) = state.rule_index else {
9224        return false;
9225    };
9226    atn.rule_to_start_state()
9227        .get(rule_index)
9228        .and_then(|state_number| atn.state(*state_number))
9229        .is_some_and(|rule_start| rule_start.left_recursive_rule)
9230}
9231
9232/// Picks the better of two `parse_atn_rule` passes (with and without the
9233/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
9234/// recovered one; among recovered outcomes the second pass is preferred
9235/// because the no-prefilter walk reaches ANTLR-style recovery inside child
9236/// rules. If both passes failed, the second pass's expected-token snapshot
9237/// is returned so the caller renders the same diagnostic ANTLR would.
9238fn select_better_top_outcome(
9239    first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
9240    second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
9241) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
9242    match (first, second) {
9243        (Ok(first), Ok(second)) => {
9244            if first.0.diagnostics.is_empty() {
9245                Ok(first)
9246            } else {
9247                Ok(second)
9248            }
9249        }
9250        (Ok(first), Err(_)) => Ok(first),
9251        (Err(_), Ok(second)) => Ok(second),
9252        (Err(_), Err(second_expected)) => Err(second_expected),
9253    }
9254}
9255
9256/// Chooses the outermost parse result that consumed the most input.
9257///
9258/// The recognizer intentionally keeps shorter endpoints available while walking
9259/// nested rule transitions so callers can satisfy following tokens such as
9260/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
9261fn select_best_fast_outcome(
9262    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
9263    prediction_mode: PredictionMode,
9264    caller_follow: Option<&TokenBitSet>,
9265    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
9266) -> Option<FastRecognizeOutcome> {
9267    let mut best = None;
9268    let mut best_caller_follow = None;
9269    for outcome in outcomes {
9270        if matches!(
9271            prediction_mode,
9272            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9273        ) && outcome.diagnostics.is_empty()
9274            && let Some(follow) = caller_follow
9275        {
9276            let (token_type, is_boundary, _) = token_info_at(outcome.index);
9277            if is_boundary && follow.contains(token_type) {
9278                let replace =
9279                    best_caller_follow
9280                        .as_ref()
9281                        .is_none_or(|existing: &FastRecognizeOutcome| {
9282                            (outcome.index, outcome.consumed_eof)
9283                                < (existing.index, existing.consumed_eof)
9284                        });
9285                if replace {
9286                    best_caller_follow = Some(outcome.clone());
9287                }
9288            }
9289        }
9290        let Some(existing) = best else {
9291            best = Some(outcome);
9292            continue;
9293        };
9294        let outcome_position = (outcome.index, outcome.consumed_eof);
9295        let best_position = (existing.index, existing.consumed_eof);
9296        let better = match prediction_mode {
9297            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
9298                outcome_position,
9299                &outcome.diagnostics,
9300                best_position,
9301                &existing.diagnostics,
9302            ),
9303            PredictionMode::Sll => outcome.index > existing.index,
9304        };
9305        best = Some(if better { outcome } else { existing });
9306    }
9307    let should_use_caller_follow =
9308        best_caller_follow
9309            .as_ref()
9310            .zip(best.as_ref())
9311            .is_some_and(|(candidate, selected)| {
9312                if !selected.diagnostics.is_empty() {
9313                    return true;
9314                }
9315                candidate.index < selected.index
9316                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
9317            });
9318    if should_use_caller_follow {
9319        best_caller_follow
9320    } else {
9321        best
9322    }
9323}
9324
9325fn select_best_outcome(
9326    outcomes: impl Iterator<Item = RecognizeOutcome>,
9327    prediction_mode: PredictionMode,
9328) -> Option<RecognizeOutcome> {
9329    let outcomes = outcomes.collect::<Vec<_>>();
9330    let prefer_first_tie = outcomes
9331        .iter()
9332        .any(|outcome| nodes_need_stable_tie(&outcome.nodes));
9333    outcomes.into_iter().reduce(|best, outcome| {
9334        let outcome_position = (outcome.index, outcome.consumed_eof);
9335        let best_position = (best.index, best.consumed_eof);
9336        let better = match prediction_mode {
9337            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
9338                outcome_is_better(
9339                    outcome_position,
9340                    &outcome.diagnostics,
9341                    best_position,
9342                    &best.diagnostics,
9343                ) || (!prefer_first_tie
9344                    && outcome_position == best_position
9345                    && outcome.diagnostics.len() == best.diagnostics.len()
9346                    && diagnostic_recovery_rank(&outcome.diagnostics)
9347                        == diagnostic_recovery_rank(&best.diagnostics)
9348                    && (outcome.decisions < best.decisions
9349                        || (outcome.decisions == best.decisions && outcome.actions > best.actions)))
9350            }
9351            PredictionMode::Sll => {
9352                outcome_position > best_position
9353                    || (outcome_position == best_position
9354                        && !prefer_first_tie
9355                        && (outcome.decisions < best.decisions
9356                            || (outcome.decisions == best.decisions
9357                                && outcome_is_better(
9358                                    outcome_position,
9359                                    &outcome.diagnostics,
9360                                    best_position,
9361                                    &best.diagnostics,
9362                                ))))
9363            }
9364        };
9365        if better {
9366            return outcome;
9367        }
9368        best
9369    })
9370}
9371
9372/// Records the serialized transition order at parser decision states.
9373///
9374/// When two clean paths consume the same input, ANTLR's adaptive prediction
9375/// chooses by alternative order. Keeping this compact trace lets the metadata
9376/// recognizer distinguish greedy and non-greedy optional blocks without a full
9377/// prediction simulator.
9378fn transition_decision(
9379    atn: &Atn,
9380    state: &AtnState,
9381    transition_index: usize,
9382    predicates: &[(usize, usize, ParserPredicate)],
9383) -> Option<usize> {
9384    if state.transitions.len() <= 1
9385        || state.precedence_rule_decision
9386        || decision_reaches_unsupported_predicate(atn, state, predicates)
9387    {
9388        return None;
9389    }
9390    Some(transition_index)
9391}
9392
9393/// Reports whether a state should reset the active no-viable decision start.
9394///
9395/// Loop entry/back states are continuations of the surrounding adaptive
9396/// prediction; resetting at those states would turn LL-star failures back into
9397/// ordinary mismatches.
9398const fn starts_prediction_decision(state: &AtnState) -> bool {
9399    state.transitions.len() > 1
9400        && !matches!(
9401            state.kind,
9402            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
9403        )
9404}
9405
9406/// Marks a farthest expected-token set as no-viable when multiple alternatives
9407/// failed after the active decision had already consumed input.
9408fn record_no_viable_if_ambiguous(
9409    expected: &mut ExpectedTokens,
9410    decision_start_index: Option<usize>,
9411    index: usize,
9412) {
9413    if expected.index == Some(index) && expected.symbols.len() > 1 {
9414        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
9415            expected.record_no_viable(decision_start, index);
9416        }
9417    }
9418}
9419
9420/// Records a no-viable decision caused by a failed semantic predicate before
9421/// any consuming transition can contribute an expected-token set.
9422const fn record_predicate_no_viable(
9423    expected: &mut ExpectedTokens,
9424    decision_start_index: Option<usize>,
9425    index: usize,
9426) {
9427    if let Some(decision_start) = decision_start_index {
9428        expected.record_no_viable(decision_start, index);
9429    }
9430}
9431
9432/// Returns the active decision start only when the error is past that start.
9433const fn no_viable_decision_start(
9434    decision_start_index: Option<usize>,
9435    index: usize,
9436) -> Option<usize> {
9437    match decision_start_index {
9438        Some(start) if index > start => Some(start),
9439        _ => None,
9440    }
9441}
9442
9443/// Restores expected-token bookkeeping when a child rule found a clean
9444/// consuming path; failures in longer child alternatives should not pollute the
9445/// caller's final expectation set.
9446fn restore_expected(
9447    children: &[RecognizeOutcome],
9448    child_start_index: usize,
9449    expected: &mut ExpectedTokens,
9450    snapshot: ExpectedTokens,
9451    preserve_child_expected: bool,
9452) {
9453    if preserve_child_expected {
9454        return;
9455    }
9456    if children
9457        .iter()
9458        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
9459    {
9460        *expected = snapshot;
9461    }
9462}
9463
9464/// Reports whether a decision can reach a predicate the generator did not
9465/// translate. Static alternative order is unsafe for those context predicates.
9466fn decision_reaches_unsupported_predicate(
9467    atn: &Atn,
9468    state: &AtnState,
9469    predicates: &[(usize, usize, ParserPredicate)],
9470) -> bool {
9471    state.transitions.iter().any(|transition| {
9472        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
9473    })
9474}
9475
9476/// Walks epsilon-like edges from one transition to find unsupported predicates.
9477fn transition_reaches_unsupported_predicate(
9478    atn: &Atn,
9479    transition: &Transition,
9480    predicates: &[(usize, usize, ParserPredicate)],
9481    visited: &mut BTreeSet<usize>,
9482) -> bool {
9483    match transition {
9484        Transition::Predicate {
9485            rule_index,
9486            pred_index,
9487            ..
9488        } => !predicates
9489            .iter()
9490            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
9491        Transition::Epsilon { target }
9492        | Transition::Action { target, .. }
9493        | Transition::Rule { target, .. } => {
9494            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
9495        }
9496        Transition::Precedence { .. }
9497        | Transition::Atom { .. }
9498        | Transition::Range { .. }
9499        | Transition::Set { .. }
9500        | Transition::NotSet { .. }
9501        | Transition::Wildcard { .. } => false,
9502    }
9503}
9504
9505/// Finds an unsupported predicate reachable before a consuming transition.
9506fn state_reaches_unsupported_predicate(
9507    atn: &Atn,
9508    state_number: usize,
9509    predicates: &[(usize, usize, ParserPredicate)],
9510    visited: &mut BTreeSet<usize>,
9511) -> bool {
9512    if !visited.insert(state_number) {
9513        return false;
9514    }
9515    let Some(state) = atn.state(state_number) else {
9516        return false;
9517    };
9518    state.transitions.iter().any(|transition| {
9519        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
9520    })
9521}
9522
9523/// Adds a decision step to the front of an already-recognized suffix path.
9524fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
9525    if let Some(decision) = decision {
9526        outcome.decisions.insert(0, decision);
9527    }
9528}
9529
9530fn outcome_is_better(
9531    outcome_position: (usize, bool),
9532    outcome_diagnostics: &[ParserDiagnostic],
9533    best_position: (usize, bool),
9534    best_diagnostics: &[ParserDiagnostic],
9535) -> bool {
9536    outcome_position > best_position
9537        || (outcome_position == best_position
9538            && (outcome_diagnostics.len() < best_diagnostics.len()
9539                || (outcome_diagnostics.len() == best_diagnostics.len()
9540                    && diagnostic_recovery_rank(outcome_diagnostics)
9541                        < diagnostic_recovery_rank(best_diagnostics))))
9542}
9543
9544/// Ranks concrete recovery repairs ahead of generic non-EOF mismatch fallbacks
9545/// when speculative paths otherwise consume the same input.
9546fn diagnostic_recovery_rank(diagnostics: &[ParserDiagnostic]) -> usize {
9547    diagnostics
9548        .iter()
9549        .filter(|diagnostic| {
9550            diagnostic.message.starts_with("mismatched input ")
9551                && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
9552        })
9553        .count()
9554}
9555
9556fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
9557    if outcomes
9558        .iter()
9559        .any(|outcome| outcome.diagnostics.is_empty())
9560    {
9561        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
9562    }
9563}
9564
9565fn discard_recovered_outcomes_if_clean_path_exists(outcomes: &mut Vec<RecognizeOutcome>) {
9566    if outcomes.iter().any(outcome_has_rule_failure_diagnostic) {
9567        return;
9568    }
9569    if outcomes
9570        .iter()
9571        .any(|outcome| outcome.diagnostics.is_empty())
9572    {
9573        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
9574    }
9575}
9576
9577/// Reports whether a recovered outcome came from an explicit predicate
9578/// fail-option and therefore should compete with shorter clean loop exits.
9579fn outcome_has_rule_failure_diagnostic(outcome: &RecognizeOutcome) -> bool {
9580    outcome
9581        .diagnostics
9582        .iter()
9583        .any(|diagnostic| diagnostic.message.starts_with("rule "))
9584}
9585
9586/// Reports whether a candidate contains recursive tree structure where ANTLR's
9587/// first viable candidate preserves the correct left-recursive context shape.
9588fn nodes_need_stable_tie(nodes: &[RecognizedNode]) -> bool {
9589    nodes.iter().any(node_needs_stable_tie)
9590}
9591
9592fn node_needs_stable_tie(node: &RecognizedNode) -> bool {
9593    match node {
9594        RecognizedNode::Token { .. }
9595        | RecognizedNode::ErrorToken { .. }
9596        | RecognizedNode::MissingToken { .. } => false,
9597        RecognizedNode::LeftRecursiveBoundary { .. } => true,
9598        RecognizedNode::Rule {
9599            rule_index,
9600            children,
9601            ..
9602        } => children.iter().any(|child| {
9603            matches!(
9604                child,
9605                RecognizedNode::Rule {
9606                    rule_index: child_rule,
9607                    ..
9608                } if child_rule == rule_index
9609            ) || node_needs_stable_tie(child)
9610        }),
9611    }
9612}
9613
9614/// Removes equivalent endpoints before memoizing a state result while
9615/// preserving ATN transition-discovery order.
9616///
9617/// Outcomes are compared on observable recognition state — the input index,
9618/// EOF consumption, and diagnostics — without descending into the parse-tree
9619/// fragment carried by `nodes`. Two paths reaching the same point with
9620/// different node trees would otherwise prevent memoization from collapsing
9621/// equivalent suffixes and explode the speculative-path cache.
9622///
9623/// The first occurrence per recognition key wins, which matches ANTLR's
9624/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
9625/// transitions before loop-exit, so the first-discovered outcome carries the
9626/// greedy parse-tree fragment.
9627fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
9628    if outcomes.len() < 2 {
9629        return;
9630    }
9631    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
9632    outcomes.retain(|outcome| {
9633        seen.insert((
9634            outcome.index,
9635            outcome.consumed_eof,
9636            outcome.diagnostics.len(),
9637            diagnostic_recovery_rank(&outcome.diagnostics),
9638        ))
9639    });
9640}
9641
9642fn dedupe_clean_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
9643    if outcomes.len() < 2 {
9644        return;
9645    }
9646    // Most outcomes lists are 2-4 entries; an inline scan beats BTreeSet
9647    // here because BTreeSet's allocation + per-insert balancing dominates
9648    // O(log n) wins on tiny n. Retains the original order so callers that
9649    // depend on alt ordering (e.g. fast outcome selection) stay correct.
9650    //
9651    // Beyond the inline buffer we promote to a heap Vec so all kept entries
9652    // continue to participate in dedup — leaking duplicates here on
9653    // pathological grammars (e.g. ktor's deeply ambiguous Kotlin parse)
9654    // explodes the speculative cache one step up the recursion.
9655    let mut inline_keys: [(usize, bool); 8] = [(0, false); 8];
9656    let mut inline_len = 0_usize;
9657    let mut overflow: Vec<(usize, bool)> = Vec::new();
9658    outcomes.retain(|outcome| {
9659        let key = (outcome.index, outcome.consumed_eof);
9660        for &existing in &inline_keys[..inline_len] {
9661            if existing == key {
9662                return false;
9663            }
9664        }
9665        if !overflow.is_empty() {
9666            for &existing in &overflow {
9667                if existing == key {
9668                    return false;
9669                }
9670            }
9671        }
9672        if inline_len < inline_keys.len() {
9673            inline_keys[inline_len] = key;
9674            inline_len += 1;
9675        } else {
9676            overflow.push(key);
9677        }
9678        true
9679    });
9680}
9681
9682/// Sorts and removes equivalent endpoints, including their action traces.
9683fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>) {
9684    outcomes.sort_unstable();
9685    outcomes.dedup();
9686}
9687
9688impl<S, H> Recognizer for BaseParser<S, H>
9689where
9690    S: TokenSource,
9691    H: SemanticHooks,
9692{
9693    fn data(&self) -> &RecognizerData {
9694        &self.data
9695    }
9696
9697    fn data_mut(&mut self) -> &mut RecognizerData {
9698        &mut self.data
9699    }
9700}
9701
9702impl<S, H> Parser for BaseParser<S, H>
9703where
9704    S: TokenSource,
9705    H: SemanticHooks,
9706{
9707    fn build_parse_trees(&self) -> bool {
9708        self.build_parse_trees
9709    }
9710
9711    fn set_build_parse_trees(&mut self, build: bool) {
9712        self.build_parse_trees = build;
9713    }
9714
9715    fn number_of_syntax_errors(&self) -> usize {
9716        Self::number_of_syntax_errors(self)
9717    }
9718
9719    fn report_diagnostic_errors(&self) -> bool {
9720        self.report_diagnostic_errors
9721    }
9722
9723    fn set_report_diagnostic_errors(&mut self, report: bool) {
9724        self.report_diagnostic_errors = report;
9725    }
9726
9727    fn prediction_mode(&self) -> PredictionMode {
9728        self.prediction_mode
9729    }
9730
9731    fn set_prediction_mode(&mut self, mode: PredictionMode) {
9732        self.prediction_mode = mode;
9733    }
9734}
9735
9736#[cfg(test)]
9737mod tests {
9738    use super::*;
9739    use crate::atn::AtnType;
9740    use crate::atn::IntervalSet;
9741    use crate::atn::parser::{
9742        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
9743    };
9744    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
9745    use crate::token::{CommonToken, HIDDEN_CHANNEL, Token};
9746    use crate::token_stream::CommonTokenStream;
9747    use crate::vocabulary::Vocabulary;
9748
9749    #[test]
9750    fn fx_hasher_write_matches_typed_methods_for_full_words() {
9751        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
9752        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
9753        // fields) must hash the same as the typed methods, otherwise an
9754        // `FxHashMap` keyed on such a type silently disagrees with itself
9755        // depending on which entry point the caller used. Verify the
9756        // little-endian word equivalence this PR established.
9757        let value: u64 = 0x0102_0304_0506_0708;
9758        let mut typed = FxHasher::default();
9759        typed.write_u64(value);
9760        let mut bytewise = FxHasher::default();
9761        bytewise.write(&value.to_le_bytes());
9762        assert_eq!(typed.finish(), bytewise.finish());
9763    }
9764
9765    #[derive(Debug)]
9766    struct Source {
9767        tokens: Vec<CommonToken>,
9768        index: usize,
9769    }
9770
9771    impl TokenSource for Source {
9772        fn next_token(&mut self) -> CommonToken {
9773            let token = self
9774                .tokens
9775                .get(self.index)
9776                .cloned()
9777                .unwrap_or_else(|| CommonToken::eof("parser-test", self.index, 1, self.index));
9778            self.index += 1;
9779            token
9780        }
9781
9782        fn line(&self) -> usize {
9783            1
9784        }
9785
9786        fn column(&self) -> usize {
9787            self.index
9788        }
9789
9790        fn source_name(&self) -> &'static str {
9791            "parser-test"
9792        }
9793    }
9794
9795    fn mini_parser_data() -> RecognizerData {
9796        RecognizerData::new(
9797            "Mini.g4",
9798            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
9799        )
9800        .with_rule_names(["s"])
9801    }
9802
9803    fn mini_parser(tokens: Vec<CommonToken>) -> BaseParser<Source> {
9804        let data = mini_parser_data();
9805        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
9806    }
9807
9808    fn mini_parser_with_hooks<H>(tokens: Vec<CommonToken>, hooks: H) -> BaseParser<Source, H>
9809    where
9810        H: SemanticHooks,
9811    {
9812        BaseParser::with_semantic_hooks(
9813            CommonTokenStream::new(Source { tokens, index: 0 }),
9814            mini_parser_data(),
9815            hooks,
9816        )
9817    }
9818
9819    fn token_then_eof_atn() -> Atn {
9820        AtnDeserializer::new(&SerializedAtn::from_i32(&[
9821            4, 1, 2, // version, parser, max token type
9822            3, // states
9823            2, 0, // rule start
9824            1, 0, // basic
9825            7, 0, // rule stop
9826            0, // non-greedy states
9827            0, // precedence states
9828            1, // rules
9829            0, // rule 0 start
9830            0, // modes
9831            0, // sets
9832            2, // transitions
9833            0, 1, 5, 1, 0, 0, // match token 1
9834            1, 2, 5, -1, 0, 0, // match EOF
9835            0, // decisions
9836        ]))
9837        .deserialize()
9838        .expect("artificial parser ATN should deserialize")
9839    }
9840
9841    fn eof_then_action_atn() -> Atn {
9842        AtnDeserializer::new(&SerializedAtn::from_i32(&[
9843            4, 1, 1, // version, parser, max token type
9844            3, // states
9845            2, 0, // rule start
9846            1, 0, // basic
9847            7, 0, // rule stop
9848            0, // non-greedy states
9849            0, // precedence states
9850            1, // rules
9851            0, // rule 0 start
9852            0, // modes
9853            0, // sets
9854            2, // transitions
9855            0, 1, 5, -1, 0, 0, // match EOF
9856            1, 2, 6, 0, 0, 0, // parser action
9857            0, // decisions
9858        ]))
9859        .deserialize()
9860        .expect("artificial parser ATN should deserialize")
9861    }
9862
9863    fn noop_action_then_token_then_eof_atn() -> Atn {
9864        AtnDeserializer::new(&SerializedAtn::from_i32(&[
9865            4, 1, 2, // version, parser, max token type
9866            4, // states
9867            2, 0, // rule start
9868            1, 0, // basic
9869            1, 0, // basic
9870            7, 0, // rule stop
9871            0, // non-greedy states
9872            0, // precedence states
9873            1, // rules
9874            0, // rule 0 start
9875            0, // modes
9876            0, // sets
9877            3, // transitions
9878            0, 1, 6, 0, -1, 0, // no-op parser action
9879            1, 2, 5, 1, 0, 0, // match token 1
9880            2, 3, 5, -1, 0, 0, // match EOF
9881            0, // decisions
9882        ]))
9883        .deserialize()
9884        .expect("artificial no-op action ATN should deserialize")
9885    }
9886
9887    fn two_alt_decision_atn() -> Atn {
9888        let mut atn = Atn::new(AtnType::Parser, 2);
9889        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
9890        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
9891        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
9892        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
9893        atn.add_state(AtnState::new(4, AtnStateKind::BlockEnd).with_rule_index(0));
9894        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
9895        atn.set_rule_to_start_state(vec![0]);
9896        atn.set_rule_to_stop_state(vec![5]);
9897        atn.add_decision_state(1);
9898        atn.state_mut(0)
9899            .expect("state 0")
9900            .add_transition(Transition::Epsilon { target: 1 });
9901        atn.state_mut(1)
9902            .expect("state 1")
9903            .add_transition(Transition::Atom {
9904                target: 2,
9905                label: 1,
9906            });
9907        atn.state_mut(1)
9908            .expect("state 1")
9909            .add_transition(Transition::Atom {
9910                target: 3,
9911                label: 2,
9912            });
9913        atn.state_mut(2)
9914            .expect("state 2")
9915            .add_transition(Transition::Epsilon { target: 4 });
9916        atn.state_mut(3)
9917            .expect("state 3")
9918            .add_transition(Transition::Epsilon { target: 4 });
9919        atn.state_mut(4)
9920            .expect("state 4")
9921            .add_transition(Transition::Epsilon { target: 5 });
9922        atn
9923    }
9924
9925    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
9926    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
9927    fn optional_then_b_eof_atn() -> Atn {
9928        let mut atn = Atn::new(AtnType::Parser, 3);
9929        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
9930        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
9931        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
9932        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
9933        atn.add_state(AtnState::new(4, AtnStateKind::Basic).with_rule_index(0));
9934        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(0));
9935        atn.set_rule_to_start_state(vec![0]);
9936        atn.set_rule_to_stop_state(vec![5]);
9937        atn.add_decision_state(1);
9938        atn.state_mut(0)
9939            .expect("state 0")
9940            .add_transition(Transition::Epsilon { target: 1 });
9941        // Optional block: match A then fall through, or skip straight to state 3.
9942        atn.state_mut(1)
9943            .expect("state 1")
9944            .add_transition(Transition::Atom {
9945                target: 3,
9946                label: 1,
9947            });
9948        atn.state_mut(1)
9949            .expect("state 1")
9950            .add_transition(Transition::Epsilon { target: 3 });
9951        // Match B, then EOF.
9952        atn.state_mut(3)
9953            .expect("state 3")
9954            .add_transition(Transition::Atom {
9955                target: 4,
9956                label: 2,
9957            });
9958        atn.state_mut(4)
9959            .expect("state 4")
9960            .add_transition(Transition::Atom {
9961                target: 5,
9962                label: TOKEN_EOF,
9963            });
9964        atn
9965    }
9966
9967    #[test]
9968    fn sync_decision_deletes_only_a_single_token() {
9969        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
9970        // expected. `(A)? B EOF` at the optional-block decision:
9971        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
9972        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
9973        //               without consuming and records the expected set for the
9974        //               subsequent mismatch (the parser must not over-consume both
9975        //               `C`s and accept the input).
9976        let atn = optional_then_b_eof_atn();
9977
9978        let mut single = mini_parser(vec![
9979            CommonToken::new(3).with_text("c"),
9980            CommonToken::new(2).with_text("b"),
9981            CommonToken::eof("parser-test", 1, 2, 2),
9982        ]);
9983        single.rule_context_stack = vec![RuleContextFrame {
9984            rule_index: 0,
9985            invoking_state: 0,
9986        }];
9987        let children = single
9988            .sync_decision(&atn, 1, true, false)
9989            .expect("single extraneous token recovers");
9990        assert_eq!(children.len(), 1);
9991        assert!(matches!(children[0], ParseTree::Error(_)));
9992        assert_eq!(single.number_of_syntax_errors(), 1);
9993        // Exactly one token consumed (the cursor now sits on `b`).
9994        assert_eq!(single.la(1), 2);
9995
9996        let mut double = mini_parser(vec![
9997            CommonToken::new(3).with_text("c"),
9998            CommonToken::new(3).with_text("c"),
9999            CommonToken::new(2).with_text("b"),
10000            CommonToken::eof("parser-test", 1, 3, 3),
10001        ]);
10002        double.rule_context_stack = vec![RuleContextFrame {
10003            rule_index: 0,
10004            invoking_state: 0,
10005        }];
10006        let result = double.sync_decision(&atn, 1, true, false);
10007        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
10008        // consume either `c`. It reports the mismatch at the first `c` (so the parser
10009        // does not over-consume both and accept the input). Nothing is consumed, so
10010        // the cursor still sits on the first `c` for rule-level recovery.
10011        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
10012        match error {
10013            AntlrError::ParserError { message, .. } => {
10014                assert!(message.starts_with("mismatched input"), "got: {message}");
10015            }
10016            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
10017        }
10018        assert_eq!(double.la(1), 3);
10019    }
10020
10021    /// The real serialized ATN that `antlr4-rust-gen` emits for
10022    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
10023    /// the loop is `EOF`. The loop decision is state 5.
10024    fn star_loop_then_eof_atn() -> Atn {
10025        AtnDeserializer::new(&SerializedAtn::from_i32(&[
10026            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,
10027            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,
10028            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,
10029            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
10030        ]))
10031        .deserialize()
10032        .expect("star-loop-then-EOF ATN should deserialize")
10033    }
10034
10035    /// ATN for `s : a+ Y ; a : X ;`.
10036    ///
10037    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
10038    /// `+` loop must not treat that zero-width child as a successful iteration
10039    /// and then re-enter the loop at the same token index.
10040    fn plus_loop_with_recovering_body_atn() -> Atn {
10041        let mut atn = Atn::new(AtnType::Parser, 2);
10042        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10043        let mut loop_start = AtnState::new(1, AtnStateKind::PlusBlockStart).with_rule_index(0);
10044        loop_start.end_state = Some(3);
10045        atn.add_state(loop_start);
10046        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10047        atn.add_state(AtnState::new(3, AtnStateKind::BlockEnd).with_rule_index(0));
10048        atn.add_state(AtnState::new(4, AtnStateKind::PlusLoopBack).with_rule_index(0));
10049        let mut loop_end = AtnState::new(5, AtnStateKind::LoopEnd).with_rule_index(0);
10050        loop_end.loop_back_state = Some(4);
10051        atn.add_state(loop_end);
10052        atn.add_state(AtnState::new(6, AtnStateKind::RuleStop).with_rule_index(0));
10053        atn.add_state(AtnState::new(7, AtnStateKind::RuleStart).with_rule_index(1));
10054        atn.add_state(AtnState::new(8, AtnStateKind::Basic).with_rule_index(1));
10055        atn.add_state(AtnState::new(9, AtnStateKind::RuleStop).with_rule_index(1));
10056        atn.set_rule_to_start_state(vec![0, 7]);
10057        atn.set_rule_to_stop_state(vec![6, 9]);
10058        atn.state_mut(0)
10059            .expect("state 0")
10060            .add_transition(Transition::Epsilon { target: 1 });
10061        atn.state_mut(1)
10062            .expect("state 1")
10063            .add_transition(Transition::Epsilon { target: 2 });
10064        atn.state_mut(2)
10065            .expect("state 2")
10066            .add_transition(Transition::Rule {
10067                target: 7,
10068                rule_index: 1,
10069                follow_state: 3,
10070                precedence: 0,
10071            });
10072        atn.state_mut(3)
10073            .expect("state 3")
10074            .add_transition(Transition::Epsilon { target: 4 });
10075        atn.state_mut(4)
10076            .expect("state 4")
10077            .add_transition(Transition::Epsilon { target: 1 });
10078        atn.state_mut(4)
10079            .expect("state 4")
10080            .add_transition(Transition::Epsilon { target: 5 });
10081        atn.state_mut(5)
10082            .expect("state 5")
10083            .add_transition(Transition::Atom {
10084                target: 6,
10085                label: 2,
10086            });
10087        atn.state_mut(7)
10088            .expect("state 7")
10089            .add_transition(Transition::Atom {
10090                target: 8,
10091                label: 1,
10092            });
10093        atn.state_mut(8)
10094            .expect("state 8")
10095            .add_transition(Transition::Epsilon { target: 9 });
10096        atn
10097    }
10098
10099    #[test]
10100    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
10101        let atn = plus_loop_with_recovering_body_atn();
10102        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10103
10104        let error = parser
10105            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
10106            .expect_err("EOF recovery should report a bounded mismatch");
10107
10108        let AntlrError::ParserError { message, .. } = error else {
10109            panic!("expected ParserError, got {error:?}");
10110        };
10111        assert_eq!(message, "mismatched input '<EOF>' expecting {'x', 2}");
10112        assert_eq!(parser.number_of_syntax_errors(), 1);
10113        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
10114    }
10115
10116    #[test]
10117    fn sync_decision_deletes_token_before_eof_at_loop_back() {
10118        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
10119        // At the loop ENTRY (loop_back = false) a single unexpected token before
10120        // EOF is deleted as an error node (then the generated EOF match consumes
10121        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
10122        // EOF must be a valid scan-stop for this to fire.
10123        let atn = star_loop_then_eof_atn();
10124        let mut parser = mini_parser(vec![
10125            CommonToken::new(2).with_text("c"),
10126            CommonToken::eof("parser-test", 1, 1, 1),
10127        ]);
10128        parser.rule_context_stack = vec![RuleContextFrame {
10129            rule_index: 0,
10130            invoking_state: 0,
10131        }];
10132        let children = parser
10133            .sync_decision(&atn, 5, true, false)
10134            .expect("single token before EOF recovers");
10135        assert_eq!(children.len(), 1);
10136        assert!(matches!(children[0], ParseTree::Error(_)));
10137        assert_eq!(parser.number_of_syntax_errors(), 1);
10138        assert_eq!(
10139            parser.la(1),
10140            TOKEN_EOF,
10141            "EOF is left for the rule's EOF match"
10142        );
10143    }
10144
10145    #[test]
10146    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
10147        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
10148        // single-token deletion, which fails because LA(2) = `c` is not expected —
10149        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
10150        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
10151        let atn = star_loop_then_eof_atn();
10152        let mut parser = mini_parser(vec![
10153            CommonToken::new(2).with_text("c"),
10154            CommonToken::new(2).with_text("c"),
10155            CommonToken::eof("parser-test", 1, 2, 2),
10156        ]);
10157        parser.rule_context_stack = vec![RuleContextFrame {
10158            rule_index: 0,
10159            invoking_state: 0,
10160        }];
10161        let error = parser
10162            .sync_decision(&atn, 5, true, false)
10163            .expect_err("two tokens at the loop entry must not be deleted");
10164        match error {
10165            AntlrError::ParserError { message, .. } => {
10166                assert!(message.starts_with("mismatched input"), "got: {message}");
10167            }
10168            other => panic!("expected mismatched-input ParserError, got {other:?}"),
10169        }
10170        assert_eq!(
10171            parser.la(1),
10172            2,
10173            "nothing consumed; cursor still on first `c`"
10174        );
10175    }
10176
10177    #[test]
10178    fn sync_decision_consumes_until_eof_at_loop_back() {
10179        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
10180        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
10181        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
10182        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
10183        // post-`a` state directly: `c c <EOF>` with loop_back = true.
10184        let atn = star_loop_then_eof_atn();
10185        let mut parser = mini_parser(vec![
10186            CommonToken::new(2).with_text("c"),
10187            CommonToken::new(2).with_text("c"),
10188            CommonToken::eof("parser-test", 1, 2, 2),
10189        ]);
10190        parser.rule_context_stack = vec![RuleContextFrame {
10191            rule_index: 0,
10192            invoking_state: 0,
10193        }];
10194        let children = parser
10195            .sync_decision(&atn, 5, false, true)
10196            .expect("loop-back multi-token deletion recovers onto EOF");
10197        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
10198        assert!(children.iter().all(|c| matches!(c, ParseTree::Error(_))));
10199        assert_eq!(parser.number_of_syntax_errors(), 1);
10200        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
10201    }
10202
10203    fn predicate_after_token_atn() -> Atn {
10204        let mut atn = Atn::new(AtnType::Parser, 2);
10205        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10206        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
10207        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10208        atn.add_state(AtnState::new(3, AtnStateKind::Basic).with_rule_index(0));
10209        atn.add_state(AtnState::new(4, AtnStateKind::RuleStop).with_rule_index(0));
10210        atn.set_rule_to_start_state(vec![0]);
10211        atn.set_rule_to_stop_state(vec![4]);
10212        atn.state_mut(0)
10213            .expect("state 0")
10214            .add_transition(Transition::Atom {
10215                target: 1,
10216                label: 1,
10217            });
10218        atn.state_mut(1)
10219            .expect("state 1")
10220            .add_transition(Transition::Predicate {
10221                target: 2,
10222                rule_index: 0,
10223                pred_index: 0,
10224                context_dependent: false,
10225            });
10226        atn.state_mut(2)
10227            .expect("state 2")
10228            .add_transition(Transition::Atom {
10229                target: 3,
10230                label: 2,
10231            });
10232        atn.state_mut(3)
10233            .expect("state 3")
10234            .add_transition(Transition::Epsilon { target: 4 });
10235        atn
10236    }
10237
10238    fn nested_nullable_context_atn() -> Atn {
10239        let mut atn = Atn::new(AtnType::Parser, 1);
10240        for state_number in 0..=20 {
10241            let kind = match state_number {
10242                0 | 10 | 16 => AtnStateKind::RuleStart,
10243                9 | 15 | 20 => AtnStateKind::RuleStop,
10244                _ => AtnStateKind::Basic,
10245            };
10246            let rule_index = match state_number {
10247                0..=9 => 0,
10248                10..=15 => 1,
10249                _ => 2,
10250            };
10251            atn.add_state(AtnState::new(state_number, kind).with_rule_index(rule_index));
10252        }
10253        atn.set_rule_to_start_state(vec![0, 10, 16]);
10254        atn.set_rule_to_stop_state(vec![9, 15, 20]);
10255        atn.state_mut(1)
10256            .expect("state 1")
10257            .add_transition(Transition::Rule {
10258                target: 10,
10259                rule_index: 1,
10260                follow_state: 8,
10261                precedence: 0,
10262            });
10263        atn.state_mut(8)
10264            .expect("state 8")
10265            .add_transition(Transition::Atom {
10266                target: 9,
10267                label: 1,
10268            });
10269        atn.state_mut(8)
10270            .expect("state 8")
10271            .add_transition(Transition::Epsilon { target: 9 });
10272        atn.state_mut(2)
10273            .expect("state 2")
10274            .add_transition(Transition::Rule {
10275                target: 16,
10276                rule_index: 2,
10277                follow_state: 14,
10278                precedence: 0,
10279            });
10280        atn.state_mut(14)
10281            .expect("state 14")
10282            .add_transition(Transition::Epsilon { target: 15 });
10283        atn
10284    }
10285
10286    fn generated_match_recovery_atn() -> Atn {
10287        let mut atn = Atn::new(AtnType::Parser, 2);
10288        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10289        atn.add_state(AtnState::new(1, AtnStateKind::Basic).with_rule_index(0));
10290        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10291        atn.add_state(AtnState::new(3, AtnStateKind::RuleStop).with_rule_index(0));
10292        atn.add_state(AtnState::new(4, AtnStateKind::RuleStart).with_rule_index(1));
10293        atn.add_state(AtnState::new(5, AtnStateKind::RuleStop).with_rule_index(1));
10294        atn.set_rule_to_start_state(vec![0, 4]);
10295        atn.set_rule_to_stop_state(vec![3, 5]);
10296        atn.state_mut(1)
10297            .expect("state 1")
10298            .add_transition(Transition::Rule {
10299                target: 4,
10300                rule_index: 1,
10301                follow_state: 2,
10302                precedence: 0,
10303            });
10304        atn.state_mut(2)
10305            .expect("state 2")
10306            .add_transition(Transition::Atom {
10307                target: 3,
10308                label: TOKEN_EOF,
10309            });
10310        atn
10311    }
10312
10313    fn complement_set_atn() -> Atn {
10314        let mut atn = Atn::new(AtnType::Parser, 1);
10315        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10316        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
10317        atn.set_rule_to_start_state(vec![0]);
10318        atn.set_rule_to_stop_state(vec![1]);
10319        let mut excluded = IntervalSet::new();
10320        excluded.add(1);
10321        atn.state_mut(0)
10322            .expect("state 0")
10323            .add_transition(Transition::NotSet {
10324                target: 1,
10325                set: excluded,
10326            });
10327        atn
10328    }
10329
10330    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
10331    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
10332    fn wildcard_then_eof_atn() -> Atn {
10333        let mut atn = Atn::new(AtnType::Parser, 1);
10334        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10335        atn.add_state(AtnState::new(1, AtnStateKind::RuleStop).with_rule_index(0));
10336        atn.add_state(AtnState::new(2, AtnStateKind::Basic).with_rule_index(0));
10337        atn.set_rule_to_start_state(vec![0]);
10338        atn.set_rule_to_stop_state(vec![1]);
10339        atn.state_mut(0)
10340            .expect("state 0")
10341            .add_transition(Transition::Wildcard { target: 2 });
10342        atn.state_mut(2)
10343            .expect("state 2")
10344            .add_transition(Transition::Atom {
10345                target: 1,
10346                label: TOKEN_EOF,
10347            });
10348        atn
10349    }
10350
10351    #[test]
10352    fn parser_matches_token_and_reports_mismatch() {
10353        let source = Source {
10354            tokens: vec![
10355                CommonToken::new(1).with_text("x"),
10356                CommonToken::eof("parser-test", 1, 1, 1),
10357            ],
10358            index: 0,
10359        };
10360        let data = RecognizerData::new(
10361            "Mini.g4",
10362            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10363        );
10364        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
10365        assert_eq!(
10366            parser.match_token(1).expect("token 1 should match").text(),
10367            "x"
10368        );
10369        assert!(parser.match_token(1).is_err());
10370    }
10371
10372    #[test]
10373    fn parser_matches_token_sets() {
10374        let mut parser = mini_parser(vec![
10375            CommonToken::new(1).with_text("x"),
10376            CommonToken::eof("parser-test", 1, 1, 1),
10377        ]);
10378
10379        assert_eq!(
10380            parser
10381                .match_set(&[(1, 1), (3, 4)])
10382                .expect("token set should match")
10383                .text(),
10384            "x"
10385        );
10386        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
10387    }
10388
10389    #[test]
10390    fn generated_rule_api_tracks_state_and_precedence() {
10391        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10392
10393        let context = parser.enter_rule(7, 2);
10394        assert_eq!(context.rule_index(), 2);
10395        assert_eq!(parser.state(), 7);
10396        assert_eq!(
10397            parser.rule_context_stack,
10398            vec![RuleContextFrame {
10399                rule_index: 2,
10400                invoking_state: 7
10401            }]
10402        );
10403
10404        let recursive = parser.enter_recursion_rule(11, 3, 4);
10405        assert_eq!(recursive.rule_index(), 3);
10406        assert!(parser.precpred(4));
10407        assert!(parser.precpred(5));
10408        assert!(!parser.precpred(3));
10409
10410        let next = parser.push_new_recursion_context(13, 3);
10411        assert_eq!(next.invoking_state(), 13);
10412        parser.unroll_recursion_context();
10413        assert_eq!(parser.precedence_stack, vec![0]);
10414        assert_eq!(
10415            parser.rule_context_stack,
10416            vec![RuleContextFrame {
10417                rule_index: 2,
10418                invoking_state: 7
10419            }]
10420        );
10421
10422        parser.exit_rule();
10423        assert!(parser.rule_context_stack.is_empty());
10424    }
10425
10426    #[test]
10427    fn parser_predicates_support_token_adjacency() {
10428        let mut parser = mini_parser(vec![
10429            CommonToken::new(1).with_text("=").with_span(0, 0),
10430            CommonToken::new(1).with_text(">").with_span(1, 1),
10431            CommonToken::eof("parser-test", 2, 1, 2),
10432        ]);
10433        parser.consume();
10434        parser.consume();
10435
10436        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
10437
10438        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
10439
10440        let mut parser = mini_parser(vec![
10441            CommonToken::new(1).with_text("=").with_span(0, 0),
10442            CommonToken::new(1)
10443                .with_text(" ")
10444                .with_channel(HIDDEN_CHANNEL)
10445                .with_span(1, 1),
10446            CommonToken::new(1).with_text(">").with_span(2, 2),
10447            CommonToken::eof("parser-test", 3, 1, 3),
10448        ]);
10449        parser.consume();
10450        parser.consume();
10451
10452        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
10453    }
10454
10455    #[test]
10456    fn parser_predicates_support_context_child_text_checks() {
10457        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10458        let mut context = ParserRuleContext::new(1, 0);
10459        let mut child_context = ParserRuleContext::new(2, 0);
10460        child_context.add_child(ParseTree::Terminal(TerminalNode::new(
10461            CommonToken::new(1).with_text("var"),
10462        )));
10463        context.add_child(ParseTree::Rule(RuleNode::new(child_context)));
10464        let predicates = [(
10465            1,
10466            0,
10467            ParserPredicate::ContextChildRuleTextNotEquals {
10468                rule_index: 2,
10469                text: "var",
10470            },
10471        )];
10472
10473        assert!(
10474            !parser.parser_semantic_predicate_matches_with_context_and_local(
10475                &predicates,
10476                1,
10477                0,
10478                &context,
10479                0,
10480            )
10481        );
10482    }
10483
10484    #[test]
10485    fn context_expected_symbols_walks_nullable_parent_contexts() {
10486        let atn = nested_nullable_context_atn();
10487        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10488        parser.rule_context_stack = vec![
10489            RuleContextFrame {
10490                rule_index: 0,
10491                invoking_state: 0,
10492            },
10493            RuleContextFrame {
10494                rule_index: 1,
10495                invoking_state: 1,
10496            },
10497            RuleContextFrame {
10498                rule_index: 2,
10499                invoking_state: 2,
10500            },
10501        ];
10502
10503        let expected = parser.context_expected_symbols(&atn);
10504
10505        assert!(expected.contains(&1));
10506        assert!(expected.contains(&TOKEN_EOF));
10507    }
10508
10509    #[test]
10510    fn prediction_context_reuses_cached_stack_until_rule_stack_changes() {
10511        let atn = nested_nullable_context_atn();
10512        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10513        parser.rule_context_stack = vec![
10514            RuleContextFrame {
10515                rule_index: 0,
10516                invoking_state: 0,
10517            },
10518            RuleContextFrame {
10519                rule_index: 1,
10520                invoking_state: 1,
10521            },
10522            RuleContextFrame {
10523                rule_index: 2,
10524                invoking_state: 2,
10525            },
10526        ];
10527
10528        let first = parser.prediction_context(&atn);
10529        let second = parser.prediction_context(&atn);
10530        assert!(Rc::ptr_eq(&first, &second));
10531
10532        parser.exit_rule();
10533        let after_pop = parser.prediction_context(&atn);
10534        assert!(!Rc::ptr_eq(&first, &after_pop));
10535    }
10536
10537    #[test]
10538    fn generated_match_token_recovers_missing_token_from_context_follow() {
10539        let atn = generated_match_recovery_atn();
10540        let data = RecognizerData::new(
10541            "Mini.g4",
10542            Vocabulary::new(
10543                [None, Some("'X'"), Some("'Y'")],
10544                [None, Some("X"), Some("Y")],
10545                [None::<&str>, None, None],
10546            ),
10547        );
10548        let mut parser = BaseParser::new(
10549            CommonTokenStream::new(Source {
10550                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
10551                index: 0,
10552            }),
10553            data,
10554        );
10555        parser.rule_context_stack = vec![
10556            RuleContextFrame {
10557                rule_index: 0,
10558                invoking_state: 0,
10559            },
10560            RuleContextFrame {
10561                rule_index: 1,
10562                invoking_state: 1,
10563            },
10564        ];
10565        assert_eq!(parser.number_of_syntax_errors(), 0);
10566
10567        let node = parser
10568            .match_token_recovering(2, 5, &atn)
10569            .expect("generated match should insert missing token");
10570
10571        assert_eq!(node.children().len(), 1);
10572        assert_eq!(node.children()[0].text(), "<missing 'Y'>");
10573        // Single-token insertion synthesizes a missing token and consumes nothing,
10574        // so no EOF terminal is consumed even though lookahead is EOF.
10575        assert!(!node.consumed_eof());
10576        assert_eq!(parser.la(1), TOKEN_EOF);
10577        assert_eq!(parser.number_of_syntax_errors(), 1);
10578        assert_eq!(
10579            parser.generated_parser_diagnostics,
10580            [ParserDiagnostic {
10581                line: 1,
10582                column: 3,
10583                message: "missing 'Y' at '<EOF>'".to_owned(),
10584            }]
10585        );
10586    }
10587
10588    #[test]
10589    fn generated_match_token_counts_single_token_deletion_recovery() {
10590        let atn = generated_match_recovery_atn();
10591        let data = RecognizerData::new(
10592            "Mini.g4",
10593            Vocabulary::new(
10594                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
10595                [None, Some("X"), Some("Y"), Some("Z")],
10596                [None::<&str>, None, None, None],
10597            ),
10598        );
10599        let mut parser = BaseParser::new(
10600            CommonTokenStream::new(Source {
10601                tokens: vec![
10602                    CommonToken::new(3).with_text("z"),
10603                    CommonToken::new(2).with_text("y"),
10604                    CommonToken::eof("parser-test", 3, 1, 3),
10605                ],
10606                index: 0,
10607            }),
10608            data,
10609        );
10610
10611        let node = parser
10612            .match_token_recovering(2, 5, &atn)
10613            .expect("generated match should delete the extraneous token");
10614
10615        assert_eq!(node.children().len(), 2);
10616        assert!(matches!(node.children()[0], ParseTree::Error(_)));
10617        assert_eq!(node.children()[0].text(), "z");
10618        assert_eq!(node.children()[1].text(), "y");
10619        assert_eq!(parser.number_of_syntax_errors(), 1);
10620    }
10621
10622    #[test]
10623    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
10624        let atn = generated_match_recovery_atn();
10625        let data = RecognizerData::new(
10626            "Mini.g4",
10627            Vocabulary::new(
10628                [None, Some("'X'"), Some("'Y'")],
10629                [None, Some("X"), Some("Y")],
10630                [None::<&str>, None, None],
10631            ),
10632        );
10633        let mut parser = BaseParser::new(
10634            CommonTokenStream::new(Source {
10635                tokens: vec![CommonToken::eof("parser-test", 3, 1, 3)],
10636                index: 0,
10637            }),
10638            data,
10639        );
10640        parser.rule_context_stack = vec![
10641            RuleContextFrame {
10642                rule_index: 0,
10643                invoking_state: 0,
10644            },
10645            RuleContextFrame {
10646                rule_index: 1,
10647                invoking_state: 1,
10648            },
10649        ];
10650        let marker = parser.generated_diagnostics_checkpoint();
10651
10652        let _ = parser
10653            .match_token_recovering(2, 5, &atn)
10654            .expect("generated match should insert missing token");
10655        assert_eq!(parser.number_of_syntax_errors(), 1);
10656
10657        parser.restore_generated_diagnostics(marker);
10658
10659        assert_eq!(parser.number_of_syntax_errors(), 0);
10660        assert!(parser.generated_parser_diagnostics.is_empty());
10661    }
10662
10663    #[test]
10664    fn generated_prediction_diagnostics_use_adaptive_context() {
10665        let atn = two_alt_decision_atn();
10666        let data = RecognizerData::new(
10667            "Mini.g4",
10668            Vocabulary::new(
10669                [None, Some("'x'"), Some("'y'")],
10670                [None, Some("X"), Some("Y")],
10671                [None::<&str>, None, None],
10672            ),
10673        )
10674        .with_rule_names(["s"]);
10675        let mut parser = BaseParser::new(
10676            CommonTokenStream::new(Source {
10677                tokens: vec![
10678                    CommonToken::new(1)
10679                        .with_text("x")
10680                        .with_position(1, 0)
10681                        .with_span(0, 0),
10682                    CommonToken::new(2)
10683                        .with_text("y")
10684                        .with_position(1, 2)
10685                        .with_span(1, 1),
10686                    CommonToken::eof("parser-test", 2, 1, 3),
10687                ],
10688                index: 0,
10689            }),
10690            data,
10691        );
10692        parser.set_report_diagnostic_errors(true);
10693
10694        parser.record_generated_prediction_diagnostic(
10695            &atn,
10696            1,
10697            &ParserAtnPrediction {
10698                alt: 1,
10699                requires_full_context: true,
10700                has_semantic_context: false,
10701                diagnostic: Some(ParserAtnPredictionDiagnostic {
10702                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
10703                    start_index: 0,
10704                    sll_stop_index: 1,
10705                    ll_stop_index: 0,
10706                    conflicting_alts: vec![1, 2],
10707                    exact: false,
10708                }),
10709            },
10710        );
10711        // Ambiguities from the default LL prediction mode are non-exact, so —
10712        // matching Java's exactOnly DiagnosticErrorListener — only the
10713        // attempting-full-context line is reported. Exact-ambiguity mode
10714        // reports the ambiguity itself.
10715        parser.record_generated_prediction_diagnostic(
10716            &atn,
10717            1,
10718            &ParserAtnPrediction {
10719                alt: 1,
10720                requires_full_context: true,
10721                has_semantic_context: false,
10722                diagnostic: Some(ParserAtnPredictionDiagnostic {
10723                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
10724                    start_index: 0,
10725                    sll_stop_index: 1,
10726                    ll_stop_index: 1,
10727                    conflicting_alts: vec![1, 2],
10728                    exact: false,
10729                }),
10730            },
10731        );
10732
10733        assert_eq!(
10734            parser.generated_parser_diagnostics,
10735            [
10736                ParserDiagnostic {
10737                    line: 1,
10738                    column: 2,
10739                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
10740                },
10741                ParserDiagnostic {
10742                    line: 1,
10743                    column: 0,
10744                    message: "reportContextSensitivity d=0 (s), input='x'".to_owned(),
10745                },
10746                ParserDiagnostic {
10747                    line: 1,
10748                    column: 2,
10749                    message: "reportAttemptingFullContext d=0 (s), input='xy'".to_owned(),
10750                },
10751            ]
10752        );
10753    }
10754
10755    #[test]
10756    fn generated_match_not_set_recovers_empty_complement_at_eof() {
10757        let atn = complement_set_atn();
10758        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10759        parser.rule_context_stack = vec![RuleContextFrame {
10760            rule_index: 0,
10761            invoking_state: 0,
10762        }];
10763
10764        let node = parser
10765            .match_not_set_recovering(&[(1, 1)], 1, 1, 1, &atn)
10766            .expect("empty complement should recover at EOF");
10767
10768        assert_eq!(node.children().len(), 1);
10769        // Recovery synthesizes a missing token without consuming EOF, so the
10770        // enclosing rule must not record EOF as its stop token.
10771        assert!(!node.consumed_eof());
10772        assert_eq!(parser.la(1), TOKEN_EOF);
10773        assert_eq!(
10774            parser.generated_parser_diagnostics,
10775            [ParserDiagnostic {
10776                line: 1,
10777                column: 1,
10778                message: "missing {} at '<EOF>'".to_owned(),
10779            }]
10780        );
10781    }
10782
10783    #[test]
10784    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
10785        // `start : . EOF ;` on empty input. The wildcard is modeled as an
10786        // empty-complement not-set; at EOF the follow state (the explicit EOF
10787        // match) expects EOF, so even in the start rule recovery must perform
10788        // single-token insertion (`<missing ...>`) rather than aborting — matching
10789        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
10790        let atn = wildcard_then_eof_atn();
10791        let data = RecognizerData::new(
10792            "Mini.g4",
10793            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
10794        );
10795        let mut parser = BaseParser::new(
10796            CommonTokenStream::new(Source {
10797                tokens: vec![CommonToken::eof("parser-test", 1, 1, 1)],
10798                index: 0,
10799            }),
10800            data,
10801        );
10802        parser.rule_context_stack = vec![RuleContextFrame {
10803            rule_index: 0,
10804            invoking_state: 0,
10805        }];
10806
10807        let node = parser
10808            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
10809            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
10810
10811        // A single `<missing ...>` error node is inserted; EOF is not consumed.
10812        assert_eq!(node.children().len(), 1);
10813        assert!(!node.consumed_eof());
10814        assert!(node.children()[0].text().starts_with("<missing"));
10815        assert_eq!(parser.la(1), TOKEN_EOF);
10816        assert_eq!(
10817            parser.generated_parser_diagnostics,
10818            [ParserDiagnostic {
10819                line: 1,
10820                column: 1,
10821                message: "missing 'x' at '<EOF>'".to_owned(),
10822            }]
10823        );
10824    }
10825
10826    #[test]
10827    fn generated_rule_recovery_consumes_to_parent_follow() {
10828        let atn = generated_match_recovery_atn();
10829        let data = RecognizerData::new(
10830            "Mini.g4",
10831            Vocabulary::new(
10832                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
10833                [None, Some("X"), Some("Y"), Some("Z")],
10834                [None::<&str>, None, None, None],
10835            ),
10836        );
10837        let mut parser = BaseParser::new(
10838            CommonTokenStream::new(Source {
10839                tokens: vec![
10840                    CommonToken::new(3).with_text("z"),
10841                    CommonToken::eof("parser-test", 1, 1, 1),
10842                ],
10843                index: 0,
10844            }),
10845            data,
10846        );
10847        let _parent = parser.enter_rule(0, 0);
10848        let marker = parser.push_invoking_state(1);
10849        let mut child = parser.enter_rule(4, 1);
10850        parser.discard_invoking_state(marker);
10851
10852        parser.recover_generated_rule(
10853            &mut child,
10854            &atn,
10855            AntlrError::ParserError {
10856                line: 1,
10857                column: 0,
10858                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
10859            },
10860        );
10861        let tree = parser.finish_rule(child, false);
10862
10863        assert_eq!(parser.la(1), TOKEN_EOF);
10864        assert_eq!(tree.to_string_tree_with_names(&["s", "a"]), "(a z)");
10865        assert_eq!(parser.number_of_syntax_errors(), 1);
10866        assert_eq!(
10867            parser.generated_parser_diagnostics,
10868            [ParserDiagnostic {
10869                line: 1,
10870                column: 0,
10871                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
10872            }]
10873        );
10874        parser.exit_rule();
10875    }
10876
10877    #[test]
10878    fn greedy_ll1_alt_handles_nullable_loop_exit() {
10879        let mut body_symbols = TokenBitSet::default();
10880        body_symbols.insert(1);
10881        let entry = DecisionLookahead {
10882            transitions: vec![
10883                TransitionLookSet {
10884                    symbols: body_symbols,
10885                    nullable: false,
10886                },
10887                TransitionLookSet {
10888                    symbols: TokenBitSet::default(),
10889                    nullable: true,
10890                },
10891            ],
10892        };
10893
10894        assert_eq!(ll1_unique_alt(&entry, 2), None);
10895        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
10896        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
10897        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
10898    }
10899
10900    #[test]
10901    fn single_outcome_memo_probe_selects_sparse_or_promote_mode() {
10902        let key = |state_number| FastRecognizeKey {
10903            state_number,
10904            stop_state: 10,
10905            index: state_number,
10906            rule_start_index: 0,
10907            decision_start_index: None,
10908            precedence: 0,
10909            recovery_symbols_id: 0,
10910            recovery_state: None,
10911        };
10912
10913        let mut sparse = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10914        for state_number in 0..(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT - 1) {
10915            assert!(sparse.should_memoize_single_outcome(&key(state_number)));
10916        }
10917        assert!(!sparse.should_memoize_single_outcome(&key(CLEAN_SINGLE_OUTCOME_MEMO_PROBE_LIMIT)));
10918        assert_eq!(
10919            sparse.single_outcome_memo_mode,
10920            SingleOutcomeMemoMode::Sparse
10921        );
10922
10923        let mut promote = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
10924        let repeated = key(1);
10925        for _ in 0..=CLEAN_SINGLE_OUTCOME_MEMO_REPEAT_LIMIT {
10926            assert!(promote.should_memoize_single_outcome(&repeated));
10927        }
10928        assert_eq!(
10929            promote.single_outcome_memo_mode,
10930            SingleOutcomeMemoMode::Promote
10931        );
10932    }
10933
10934    #[test]
10935    fn clean_empty_multi_alt_outcomes_are_memoized() {
10936        let mut atn = Atn::new(AtnType::Parser, 2);
10937        atn.add_state(AtnState::new(0, AtnStateKind::RuleStart).with_rule_index(0));
10938        atn.add_state(AtnState::new(1, AtnStateKind::BlockStart).with_rule_index(0));
10939        atn.add_state(AtnState::new(2, AtnStateKind::RuleStop).with_rule_index(0));
10940        atn.set_rule_to_start_state(vec![0]);
10941        atn.set_rule_to_stop_state(vec![2]);
10942        atn.state_mut(0)
10943            .expect("state 0")
10944            .add_transition(Transition::Epsilon { target: 1 });
10945        atn.state_mut(1)
10946            .expect("state 1")
10947            .add_transition(Transition::Atom {
10948                target: 2,
10949                label: 1,
10950            });
10951        atn.state_mut(1)
10952            .expect("state 1")
10953            .add_transition(Transition::Atom {
10954                target: 2,
10955                label: 2,
10956            });
10957
10958        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
10959        parser.fast_recovery_enabled = false;
10960        let mut visiting = FxHashSet::default();
10961        let mut memo = FxHashMap::default();
10962        let mut expected = ExpectedTokens::default();
10963        let outcomes = parser.recognize_state_fast(
10964            &atn,
10965            FastRecognizeRequest {
10966                state_number: 1,
10967                stop_state: 2,
10968                index: 0,
10969                rule_start_index: 0,
10970                decision_start_index: None,
10971                precedence: 0,
10972                depth: 0,
10973                recovery_symbols: parser.empty_recovery_symbols(),
10974                recovery_state: None,
10975            },
10976            &mut visiting,
10977            &mut memo,
10978            &mut expected,
10979        );
10980
10981        assert!(outcomes.is_empty());
10982        assert_eq!(memo.len(), 1);
10983        assert!(memo.values().next().expect("memo entry").is_empty());
10984    }
10985
10986    #[test]
10987    fn wildcard_matches_non_eof_only() {
10988        let mut parser = mini_parser(vec![
10989            CommonToken::new(1).with_text("x"),
10990            CommonToken::eof("parser-test", 1, 1, 1),
10991        ]);
10992        assert_eq!(parser.match_wildcard().expect("wildcard").text(), "x");
10993        assert!(parser.match_wildcard().is_err());
10994    }
10995
10996    #[test]
10997    fn add_parse_child_records_match_even_without_tree_building() {
10998        // `sync_decision`'s "is the current context empty" flag must reflect real
10999        // matches, not parse-tree children: when `build_parse_trees(false)`,
11000        // `children` stays empty but `has_matched_child` must still flip so nested
11001        // recovery does not wrongly suppress single-token deletion.
11002        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11003        let token = CommonToken::new(1).with_text("x");
11004
11005        parser.set_build_parse_trees(false);
11006        let mut ctx = ParserRuleContext::new(0, 0);
11007        assert!(!ctx.has_matched_child());
11008        parser.add_parse_child(
11009            &mut ctx,
11010            ParseTree::Terminal(TerminalNode::new(token.clone())),
11011        );
11012        // Tree building is off, so no child is stored...
11013        assert!(ctx.children().is_empty());
11014        // ...but the match is recorded, so the context is no longer "empty".
11015        assert!(ctx.has_matched_child());
11016
11017        // With tree building on, the child is stored and the match is recorded.
11018        parser.set_build_parse_trees(true);
11019        let mut ctx = ParserRuleContext::new(0, 0);
11020        parser.add_parse_child(&mut ctx, ParseTree::Terminal(TerminalNode::new(token)));
11021        assert_eq!(ctx.children().len(), 1);
11022        assert!(ctx.has_matched_child());
11023    }
11024
11025    #[test]
11026    fn parser_interprets_simple_atn_rule() {
11027        let atn = token_then_eof_atn();
11028        let mut parser = mini_parser(vec![
11029            CommonToken::new(1).with_text("x"),
11030            CommonToken::eof("parser-test", 1, 1, 1),
11031        ]);
11032
11033        let tree = parser
11034            .parse_atn_rule(&atn, 0)
11035            .expect("artificial parser rule should parse");
11036        assert_eq!(tree.text(), "x<EOF>");
11037        assert_eq!(parser.number_of_syntax_errors(), 0);
11038        assert_eq!(
11039            tree.first_rule_stop(0)
11040                .expect("rule should stop at EOF")
11041                .token_type(),
11042            TOKEN_EOF
11043        );
11044
11045        let mut parser = mini_parser(vec![
11046            CommonToken::new(1).with_text("x"),
11047            CommonToken::eof("parser-test", 1, 1, 1),
11048        ]);
11049        let (tree, actions) = parser
11050            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11051            .expect("runtime-option parser rule should parse");
11052        assert!(actions.is_empty());
11053        assert_eq!(
11054            tree.first_rule_stop(0)
11055                .expect("rule should stop at EOF")
11056                .token_type(),
11057            TOKEN_EOF
11058        );
11059    }
11060
11061    #[test]
11062    fn runtime_options_default_ignores_noop_action_transitions() {
11063        let atn = noop_action_then_token_then_eof_atn();
11064        let mut parser = mini_parser(vec![
11065            CommonToken::new(1).with_text("x"),
11066            CommonToken::eof("parser-test", 1, 1, 1),
11067        ]);
11068
11069        let (tree, actions) = parser
11070            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11071            .expect("no-op parser action should not force action replay");
11072
11073        assert_eq!(tree.text(), "x<EOF>");
11074        assert!(
11075            actions.is_empty(),
11076            "action_index=None transitions are ANTLR metadata, not replay actions"
11077        );
11078        assert_eq!(parser.number_of_syntax_errors(), 0);
11079    }
11080
11081    #[test]
11082    fn parser_exposes_buffered_token_stream_after_parse() {
11083        let atn = token_then_eof_atn();
11084        let mut parser = mini_parser(vec![
11085            CommonToken::new(1).with_text("x"),
11086            CommonToken::eof("parser-test", 1, 1, 1),
11087        ]);
11088
11089        let tree = parser
11090            .parse_atn_rule(&atn, 0)
11091            .expect("artificial parser rule should parse");
11092        assert_eq!(tree.text(), "x<EOF>");
11093
11094        let stream = parser.token_stream();
11095        let source_index_after_parse = stream.token_source().index;
11096        let buffered = stream.tokens();
11097        assert_eq!(buffered.len(), 2);
11098        assert_eq!(buffered[0].text(), "x");
11099        assert_eq!(buffered[0].token_index(), 0);
11100        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
11101        assert_eq!(stream.token_source().index, source_index_after_parse);
11102
11103        let stream = parser.into_token_stream();
11104        assert_eq!(stream.token_source().index, source_index_after_parse);
11105        assert_eq!(stream.tokens()[0].text(), "x");
11106        assert_eq!(stream.tokens()[1].token_type(), TOKEN_EOF);
11107    }
11108
11109    #[test]
11110    fn parser_syntax_error_count_tracks_interpreted_recovery() {
11111        let atn = token_then_eof_atn();
11112        let mut parser = mini_parser(vec![
11113            CommonToken::new(1).with_text("x"),
11114            CommonToken::new(2).with_text("y"),
11115            CommonToken::eof("parser-test", 2, 1, 2),
11116        ]);
11117
11118        let tree = parser
11119            .parse_atn_rule(&atn, 0)
11120            .expect("invalid token should recover into an error node");
11121
11122        assert_eq!(parser.number_of_syntax_errors(), 1);
11123        assert_eq!(
11124            tree.first_error_token()
11125                .expect("recovery should embed an error token")
11126                .text(),
11127            "y"
11128        );
11129    }
11130
11131    #[test]
11132    fn parser_syntax_error_count_tracks_failed_interpreted_parse() {
11133        let atn = token_then_eof_atn();
11134        let mut parser = mini_parser(vec![
11135            CommonToken::new(2).with_text("y"),
11136            CommonToken::eof("parser-test", 1, 1, 1),
11137        ]);
11138
11139        let error = parser
11140            .parse_atn_rule(&atn, 0)
11141            .expect_err("start-rule mismatch should remain a parser error");
11142
11143        assert_eq!(parser.number_of_syntax_errors(), 1);
11144        assert!(matches!(error, AntlrError::ParserError { .. }));
11145    }
11146
11147    #[test]
11148    fn adaptive_direct_rule_uses_simulator_decision() {
11149        let atn = two_alt_decision_atn();
11150        let mut simulator = ParserAtnSimulator::new(&atn);
11151        let mut parser = mini_parser(vec![
11152            CommonToken::new(2).with_text("y"),
11153            CommonToken::eof("parser-test", 1, 1, 1),
11154        ]);
11155
11156        let tree = parser
11157            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
11158            .expect("direct adaptive rule should parse");
11159
11160        assert_eq!(tree.text(), "y");
11161        assert_eq!(parser.input.index(), 1);
11162    }
11163
11164    #[test]
11165    fn adaptive_direct_rule_restores_input_on_fallback() {
11166        let atn = predicate_after_token_atn();
11167        let mut simulator = ParserAtnSimulator::new(&atn);
11168        let mut parser = mini_parser(vec![
11169            CommonToken::new(1).with_text("x"),
11170            CommonToken::new(2).with_text("y"),
11171            CommonToken::eof("parser-test", 2, 1, 2),
11172        ]);
11173
11174        let tree = parser
11175            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
11176            .expect("fallback recognizer should parse");
11177
11178        assert_eq!(tree.text(), "xy");
11179        assert_eq!(parser.input.index(), 2);
11180    }
11181
11182    #[test]
11183    fn unknown_predicate_policy_defaults_to_assume_true() {
11184        let atn = predicate_after_token_atn();
11185        let mut parser = mini_parser(vec![
11186            CommonToken::new(1).with_text("x"),
11187            CommonToken::new(2).with_text("y"),
11188            CommonToken::eof("parser-test", 2, 1, 2),
11189        ]);
11190
11191        let (tree, _) = parser
11192            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11193            .expect("unknown predicate should pass under the default policy");
11194
11195        assert_eq!(tree.text(), "xy");
11196        assert_eq!(parser.number_of_syntax_errors(), 0);
11197    }
11198
11199    #[test]
11200    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
11201        // A generated parent may record an unknown-predicate coordinate, then
11202        // descend into an interpreted child. The child's interpreter entry must
11203        // not wipe the parent's recorded hit before the top-level surfaces it.
11204        let atn = token_then_eof_atn();
11205        let mut parser = mini_parser(vec![
11206            CommonToken::new(1).with_text("x"),
11207            CommonToken::eof("parser-test", 1, 1, 1),
11208        ]);
11209
11210        // Simulate the parent having recorded a fail-loud coordinate.
11211        parser.unknown_predicate_hits.push((7, 3));
11212
11213        // Run an interpreted child parse that records no coordinate of its own.
11214        parser
11215            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11216            .expect("child rule parses");
11217
11218        // The parent's coordinate must still be present for the top-level entry.
11219        let error = parser
11220            .take_unknown_semantic_error()
11221            .expect("parent's recorded coordinate must survive the nested interpreted parse");
11222        let AntlrError::Unsupported(message) = error else {
11223            panic!("expected AntlrError::Unsupported, got {error:?}");
11224        };
11225        assert!(message.contains("pred_index=3"), "message: {message}");
11226    }
11227
11228    #[test]
11229    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
11230        let atn = predicate_after_token_atn();
11231        let mut parser = mini_parser(vec![
11232            CommonToken::new(1).with_text("x"),
11233            CommonToken::new(2).with_text("y"),
11234            CommonToken::eof("parser-test", 2, 1, 2),
11235        ]);
11236
11237        let result = parser.parse_atn_rule_with_runtime_options(
11238            &atn,
11239            0,
11240            ParserRuntimeOptions {
11241                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
11242                ..ParserRuntimeOptions::default()
11243            },
11244        );
11245
11246        assert!(
11247            result.is_err(),
11248            "the only path is predicate-guarded, so assume-false must fail the parse"
11249        );
11250    }
11251
11252    #[test]
11253    fn unknown_predicate_policy_error_names_the_coordinate() {
11254        let atn = predicate_after_token_atn();
11255        let mut parser = mini_parser(vec![
11256            CommonToken::new(1).with_text("x"),
11257            CommonToken::new(2).with_text("y"),
11258            CommonToken::eof("parser-test", 2, 1, 2),
11259        ]);
11260
11261        let error = parser
11262            .parse_atn_rule_with_runtime_options(
11263                &atn,
11264                0,
11265                ParserRuntimeOptions {
11266                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
11267                    ..ParserRuntimeOptions::default()
11268                },
11269            )
11270            .expect_err("evaluating an unknown predicate under Error policy must fail");
11271
11272        let AntlrError::Unsupported(message) = error else {
11273            panic!("expected AntlrError::Unsupported, got {error:?}");
11274        };
11275        assert!(
11276            message.contains("unsupported semantic predicate"),
11277            "message should name the failure class: {message}"
11278        );
11279        assert!(
11280            message.contains("pred_index=0"),
11281            "message should carry the coordinate: {message}"
11282        );
11283    }
11284
11285    #[test]
11286    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
11287        // A parser reused after a fail-loud parse must not carry the old
11288        // coordinates into a later parse. The fail-loud return keeps the hits
11289        // (so a generated parent can surface a recovered child's coordinate),
11290        // and the next parse's entry stashes/replaces them, so a subsequent
11291        // clean parse surfaces no stale error.
11292        let atn = predicate_after_token_atn();
11293        let mut parser = mini_parser(vec![
11294            CommonToken::new(1).with_text("x"),
11295            CommonToken::new(2).with_text("y"),
11296            CommonToken::eof("parser-test", 2, 1, 2),
11297        ]);
11298
11299        parser
11300            .parse_atn_rule_with_runtime_options(
11301                &atn,
11302                0,
11303                ParserRuntimeOptions {
11304                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
11305                    ..ParserRuntimeOptions::default()
11306                },
11307            )
11308            .expect_err("first parse fails loud under the Error policy");
11309
11310        // The failed parse kept its coordinate on the parser (so a generated
11311        // parent could surface a recovered child). A top-level reuse resets the
11312        // hits — generated parsers call `reset_unknown_semantic_hits` at their
11313        // public entry; direct interpreter-API callers do the same.
11314        parser.reset_unknown_semantic_hits();
11315        assert!(
11316            parser.take_unknown_semantic_error().is_none(),
11317            "reset must drop stale unknown-predicate coordinates before a reused parse"
11318        );
11319    }
11320
11321    #[derive(Debug, Default)]
11322    struct RecordingHooks {
11323        predicates: Vec<(usize, usize, usize, Option<String>)>,
11324        actions: Vec<(usize, String, Option<String>)>,
11325    }
11326
11327    impl SemanticHooks for RecordingHooks {
11328        fn sempred<S>(
11329            &mut self,
11330            ctx: &mut ParserSemCtx<'_, S>,
11331            rule_index: usize,
11332            pred_index: usize,
11333        ) -> Option<bool>
11334        where
11335            S: TokenSource,
11336        {
11337            self.predicates.push((
11338                ctx.input_index(),
11339                rule_index,
11340                pred_index,
11341                ctx.token_text(1).map(str::to_owned),
11342            ));
11343            Some(true)
11344        }
11345
11346        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
11347        where
11348            S: TokenSource,
11349        {
11350            self.actions.push((
11351                action.source_state(),
11352                ctx.action_text(),
11353                ctx.rule_name().map(str::to_owned),
11354            ));
11355            true
11356        }
11357    }
11358
11359    #[derive(Debug, Default)]
11360    struct RejectingPredicateHooks {
11361        predicates: Vec<(usize, usize, usize, Option<String>)>,
11362    }
11363
11364    impl SemanticHooks for RejectingPredicateHooks {
11365        fn sempred<S>(
11366            &mut self,
11367            ctx: &mut ParserSemCtx<'_, S>,
11368            rule_index: usize,
11369            pred_index: usize,
11370        ) -> Option<bool>
11371        where
11372            S: TokenSource,
11373        {
11374            self.predicates.push((
11375                ctx.input_index(),
11376                rule_index,
11377                pred_index,
11378                ctx.token_text(1).map(str::to_owned),
11379            ));
11380            Some(false)
11381        }
11382    }
11383
11384    #[test]
11385    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
11386        let atn = predicate_after_token_atn();
11387        let mut parser = mini_parser_with_hooks(
11388            vec![
11389                CommonToken::new(1).with_text("x"),
11390                CommonToken::new(2).with_text("y"),
11391                CommonToken::eof("parser-test", 2, 1, 2),
11392            ],
11393            RecordingHooks::default(),
11394        );
11395
11396        let (tree, _) = parser
11397            .parse_atn_rule_with_runtime_options(
11398                &atn,
11399                0,
11400                ParserRuntimeOptions {
11401                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
11402                    ..ParserRuntimeOptions::default()
11403                },
11404            )
11405            .expect("hook supplies the missing predicate result");
11406
11407        assert_eq!(tree.text(), "xy");
11408        assert_eq!(
11409            parser.semantic_hooks.predicates,
11410            vec![(1, 0, 0, Some("y".to_owned()))]
11411        );
11412    }
11413
11414    #[test]
11415    fn runtime_options_default_preserves_semantic_hook_predicates() {
11416        let atn = predicate_after_token_atn();
11417        let mut parser = mini_parser_with_hooks(
11418            vec![
11419                CommonToken::new(1).with_text("x"),
11420                CommonToken::new(2).with_text("y"),
11421                CommonToken::eof("parser-test", 2, 1, 2),
11422            ],
11423            RejectingPredicateHooks::default(),
11424        );
11425
11426        let result =
11427            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
11428
11429        assert!(
11430            result.is_err(),
11431            "default runtime options must not bypass semantic hooks for predicate ATNs"
11432        );
11433        assert_eq!(
11434            parser.semantic_hooks.predicates,
11435            vec![(1, 0, 0, Some("y".to_owned()))]
11436        );
11437    }
11438
11439    #[test]
11440    fn semantic_hook_handles_committed_parser_action() {
11441        let atn = token_then_eof_atn();
11442        let mut parser = mini_parser_with_hooks(
11443            vec![
11444                CommonToken::new(1).with_text("x"),
11445                CommonToken::eof("parser-test", 1, 1, 1),
11446            ],
11447            RecordingHooks::default(),
11448        );
11449        let (tree, _) = parser
11450            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11451            .expect("rule parses before action hook is tested");
11452
11453        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
11454        assert_eq!(
11455            parser.semantic_hooks.actions,
11456            vec![(42, "x".to_owned(), Some("s".to_owned()))]
11457        );
11458    }
11459
11460    #[test]
11461    fn unhandled_committed_action_fails_loud_under_error_policy() {
11462        // An action offered to the hook that no hook handles (returns false)
11463        // must be recorded and surfaced as `AntlrError::Unsupported` under the
11464        // Error policy, so a `hook`-disposed action is not silently dropped.
11465        let mut parser =
11466            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
11467        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
11468        let tree = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, -1)));
11469
11470        // DecliningHooks::action returns false (unhandled).
11471        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
11472
11473        let error = parser
11474            .take_unknown_semantic_error()
11475            .expect("an unhandled committed action under Error policy must fail loud");
11476        let AntlrError::Unsupported(message) = error else {
11477            panic!("expected AntlrError::Unsupported, got {error:?}");
11478        };
11479        assert!(
11480            message.contains("unhandled semantic action") && message.contains("state=42"),
11481            "message should name the dropped action coordinate: {message}"
11482        );
11483
11484        // Under the default (assume-true) policy the same miss is not recorded.
11485        let mut lenient =
11486            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
11487        let tree = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, -1)));
11488        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), &tree));
11489        assert!(lenient.take_unknown_semantic_error().is_none());
11490    }
11491
11492    #[test]
11493    fn translated_predicate_is_unaffected_by_error_policy() {
11494        let atn = predicate_after_token_atn();
11495        let mut parser = mini_parser(vec![
11496            CommonToken::new(1).with_text("x"),
11497            CommonToken::new(2).with_text("y"),
11498            CommonToken::eof("parser-test", 2, 1, 2),
11499        ]);
11500
11501        let (tree, _) = parser
11502            .parse_atn_rule_with_runtime_options(
11503                &atn,
11504                0,
11505                ParserRuntimeOptions {
11506                    predicates: &[(0, 0, ParserPredicate::True)],
11507                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
11508                    ..ParserRuntimeOptions::default()
11509                },
11510            )
11511            .expect("a predicate covered by the table is not an unknown coordinate");
11512
11513        assert_eq!(tree.text(), "xy");
11514    }
11515
11516    /// Hooks that decline (`None`) must fall through to the configured policy
11517    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
11518    /// legacy table path. Regression for the `unwrap_or(false)` that silently
11519    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
11520    fn hook_predicate_semantics() -> ParserSemantics {
11521        let mut ir = SemIr::new();
11522        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
11523        ParserSemantics {
11524            ir,
11525            predicates: vec![ParserSemanticPredicate {
11526                rule_index: 0,
11527                pred_index: 0,
11528                expr,
11529                failure_message: None,
11530            }],
11531            actions: Vec::new(),
11532        }
11533    }
11534
11535    #[derive(Debug, Default)]
11536    struct DecliningHooks;
11537
11538    impl SemanticHooks for DecliningHooks {}
11539
11540    #[test]
11541    fn semir_hook_none_falls_through_to_assume_true() {
11542        let atn = predicate_after_token_atn();
11543        let semantics = hook_predicate_semantics();
11544        let mut parser = mini_parser_with_hooks(
11545            vec![
11546                CommonToken::new(1).with_text("x"),
11547                CommonToken::new(2).with_text("y"),
11548                CommonToken::eof("parser-test", 2, 1, 2),
11549            ],
11550            DecliningHooks,
11551        );
11552
11553        let (tree, _) = parser
11554            .parse_atn_rule_with_runtime_options(
11555                &atn,
11556                0,
11557                ParserRuntimeOptions {
11558                    semantics: Some(&semantics),
11559                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
11560                    ..ParserRuntimeOptions::default()
11561                },
11562            )
11563            .expect("a declined SemIR hook must pass under assume-true");
11564
11565        assert_eq!(tree.text(), "xy");
11566    }
11567
11568    #[test]
11569    fn semir_hook_none_falls_through_to_assume_false() {
11570        let atn = predicate_after_token_atn();
11571        let semantics = hook_predicate_semantics();
11572        let mut parser = mini_parser_with_hooks(
11573            vec![
11574                CommonToken::new(1).with_text("x"),
11575                CommonToken::new(2).with_text("y"),
11576                CommonToken::eof("parser-test", 2, 1, 2),
11577            ],
11578            DecliningHooks,
11579        );
11580
11581        let result = parser.parse_atn_rule_with_runtime_options(
11582            &atn,
11583            0,
11584            ParserRuntimeOptions {
11585                semantics: Some(&semantics),
11586                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
11587                ..ParserRuntimeOptions::default()
11588            },
11589        );
11590
11591        assert!(
11592            result.is_err(),
11593            "a declined SemIR hook must fail the only guarded path under assume-false"
11594        );
11595    }
11596
11597    #[test]
11598    fn semir_hook_none_records_coordinate_under_error_policy() {
11599        let atn = predicate_after_token_atn();
11600        let semantics = hook_predicate_semantics();
11601        let mut parser = mini_parser_with_hooks(
11602            vec![
11603                CommonToken::new(1).with_text("x"),
11604                CommonToken::new(2).with_text("y"),
11605                CommonToken::eof("parser-test", 2, 1, 2),
11606            ],
11607            DecliningHooks,
11608        );
11609
11610        let error = parser
11611            .parse_atn_rule_with_runtime_options(
11612                &atn,
11613                0,
11614                ParserRuntimeOptions {
11615                    semantics: Some(&semantics),
11616                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
11617                    ..ParserRuntimeOptions::default()
11618                },
11619            )
11620            .expect_err("a declined SemIR hook under Error policy must fail the parse");
11621
11622        let AntlrError::Unsupported(message) = error else {
11623            panic!("expected AntlrError::Unsupported, got {error:?}");
11624        };
11625        assert!(
11626            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
11627            "message should name the unresolved coordinate: {message}"
11628        );
11629    }
11630
11631    #[test]
11632    fn generated_direct_predicate_honors_installed_policy() {
11633        // The generated recursive-descent path calls
11634        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
11635        // going through `ParserRuntimeOptions`, so the policy must be installed
11636        // via `set_unknown_predicate_policy` (as the generated constructor now
11637        // does). A declining hook must then honor it rather than the default.
11638        let semantics = hook_predicate_semantics();
11639        let context = ParserRuleContext::new(0, -1);
11640
11641        let mut assume_true =
11642            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
11643        assert!(
11644            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
11645                &semantics, 0, 0, &context, 0
11646            ),
11647            "default AssumeTrue accepts a declined hook"
11648        );
11649        assert!(assume_true.take_unknown_semantic_error().is_none());
11650
11651        let mut error_policy =
11652            mini_parser_with_hooks(vec![CommonToken::eof("t", 0, 1, 0)], DecliningHooks);
11653        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
11654        assert!(
11655            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
11656                &semantics, 0, 0, &context, 0
11657            ),
11658            "Error policy rejects a declined hook on the generated-direct path"
11659        );
11660        let error = error_policy
11661            .take_unknown_semantic_error()
11662            .expect("Error policy records the unresolved coordinate for the generated path");
11663        let AntlrError::Unsupported(message) = error else {
11664            panic!("expected AntlrError::Unsupported, got {error:?}");
11665        };
11666        assert!(message.contains("pred_index=0"), "message: {message}");
11667    }
11668
11669    #[test]
11670    fn parser_rule_start_skips_leading_hidden_tokens() {
11671        let atn = token_then_eof_atn();
11672        let mut parser = mini_parser(vec![
11673            CommonToken::new(99)
11674                .with_text(" ")
11675                .with_channel(HIDDEN_CHANNEL),
11676            CommonToken::new(1).with_text("x"),
11677            CommonToken::eof("parser-test", 2, 1, 2),
11678        ]);
11679
11680        let tree = parser
11681            .parse_atn_rule(&atn, 0)
11682            .expect("artificial parser rule should parse");
11683        let Some(ParseTree::Rule(rule)) = tree.first_rule(0) else {
11684            panic!("rule node should be present");
11685        };
11686        assert_eq!(
11687            rule.context()
11688                .start()
11689                .expect("rule should have a start token")
11690                .token_type(),
11691            1
11692        );
11693    }
11694
11695    #[test]
11696    fn parser_action_after_eof_stops_at_eof_token() {
11697        let atn = eof_then_action_atn();
11698        let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
11699
11700        let (_, actions) = parser
11701            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
11702            .expect("EOF action rule should parse");
11703
11704        assert_eq!(actions.len(), 1);
11705        assert_eq!(actions[0].stop_index(), Some(0));
11706        assert_eq!(
11707            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
11708            ""
11709        );
11710    }
11711
11712    #[test]
11713    fn after_action_stop_uses_rule_context_stop_not_cursor() {
11714        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
11715        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
11716        // but the rule did not consume it. The @after stop must follow the rule
11717        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
11718        let mut id = CommonToken::new(1).with_text("x");
11719        id.set_token_index(0);
11720        let mut eof = CommonToken::eof("parser-test", 1, 1, 1);
11721        eof.set_token_index(1);
11722        let mut parser = mini_parser(vec![id.clone(), eof]);
11723        // Advance the cursor onto EOF, as it would be after `a` matched ID.
11724        parser.consume();
11725        assert_eq!(parser.la(1), TOKEN_EOF);
11726
11727        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
11728        // exactly what finish_rule(consumed_eof = false) records.
11729        let mut ctx = ParserRuleContext::new(0, 0);
11730        ctx.set_stop(id);
11731        let tree = ParseTree::Rule(RuleNode::new(ctx));
11732
11733        let current_index = parser.input.index();
11734        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
11735        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
11736        // ...but the tree-aware helper follows the rule context stop (ID).
11737        assert_eq!(
11738            parser.after_action_stop_index_for_tree(&tree, current_index),
11739            Some(0)
11740        );
11741    }
11742
11743    #[test]
11744    fn after_action_start_uses_rule_context_start_not_cursor() {
11745        // A rule that begins after leading hidden-channel tokens: the rule context
11746        // start (set by `enter_rule`) is the first visible token, not the raw cursor
11747        // that may still point at the hidden prefix. The @after start must follow
11748        // the context start so `$start`/`$text` excludes the hidden prefix.
11749        let parser = mini_parser(vec![CommonToken::eof("parser-test", 1, 1, 1)]);
11750        let mut id = CommonToken::new(1).with_text("x");
11751        // The first visible token sits at stream index 2 (after two hidden tokens).
11752        id.set_token_index(2);
11753
11754        let mut ctx = ParserRuleContext::new(0, 0);
11755        ctx.set_start(id);
11756        let tree = ParseTree::Rule(RuleNode::new(ctx));
11757
11758        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
11759        // ...but the tree-aware helper follows the rule context start (index 2).
11760        assert_eq!(parser.after_action_start_index_for_tree(&tree, 0), 2);
11761
11762        // With no rule start recorded, it falls back to the provided index.
11763        let empty = ParseTree::Rule(RuleNode::new(ParserRuleContext::new(0, 0)));
11764        assert_eq!(parser.after_action_start_index_for_tree(&empty, 7), 7);
11765    }
11766
11767    #[test]
11768    fn fast_outcome_selection_respects_sll_tie_order() {
11769        let first = FastRecognizeOutcome {
11770            index: 1,
11771            consumed_eof: false,
11772            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
11773                line: 1,
11774                column: 0,
11775                message: "mismatched input 'x'".to_owned(),
11776            }]),
11777            nodes: NodeList::new(),
11778        };
11779        let second = FastRecognizeOutcome {
11780            index: first.index,
11781            consumed_eof: first.consumed_eof,
11782            diagnostics: FastDiagnostics::new(),
11783            nodes: NodeList::new(),
11784        };
11785
11786        let selected = select_best_fast_outcome(
11787            [first.clone(), second.clone()].into_iter(),
11788            PredictionMode::Sll,
11789            None,
11790            |_| panic!("caller-follow token probe should not run"),
11791        )
11792        .expect("one outcome should be selected");
11793        assert_eq!(selected.diagnostics.len(), 1);
11794        let eof_second = FastRecognizeOutcome {
11795            index: second.index,
11796            consumed_eof: true,
11797            diagnostics: FastDiagnostics::new(),
11798            nodes: NodeList::new(),
11799        };
11800        let selected = select_best_fast_outcome(
11801            [first.clone(), eof_second].into_iter(),
11802            PredictionMode::Sll,
11803            None,
11804            |_| panic!("caller-follow token probe should not run"),
11805        )
11806        .expect("one outcome should be selected");
11807        assert!(!selected.consumed_eof);
11808        let selected = select_best_fast_outcome(
11809            [first, second].into_iter(),
11810            PredictionMode::Ll,
11811            None,
11812            |_| panic!("caller-follow token probe should not run"),
11813        )
11814        .expect("one outcome should be selected");
11815        assert!(selected.diagnostics.is_empty());
11816    }
11817
11818    #[test]
11819    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
11820        let first = FastRecognizeOutcome {
11821            index: 3,
11822            consumed_eof: false,
11823            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
11824                line: 1,
11825                column: 0,
11826                message: "mismatched input 'x' expecting 'a'".to_owned(),
11827            }]),
11828            nodes: NodeList::new(),
11829        };
11830        let same_rank = FastRecognizeOutcome {
11831            index: first.index,
11832            consumed_eof: first.consumed_eof,
11833            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
11834                line: 1,
11835                column: 0,
11836                message: "mismatched input 'x' expecting 'b'".to_owned(),
11837            }]),
11838            nodes: NodeList::new(),
11839        };
11840        let better_rank = FastRecognizeOutcome {
11841            index: first.index,
11842            consumed_eof: first.consumed_eof,
11843            diagnostics: FastDiagnostics::from_vec(vec![ParserDiagnostic {
11844                line: 1,
11845                column: 0,
11846                message: "missing 'a' at 'x'".to_owned(),
11847            }]),
11848            nodes: NodeList::new(),
11849        };
11850        let mut outcomes = vec![first, same_rank, better_rank];
11851
11852        dedupe_fast_outcomes(&mut outcomes);
11853
11854        assert_eq!(outcomes.len(), 2);
11855        assert_eq!(
11856            outcomes[0].diagnostics[0].message,
11857            "mismatched input 'x' expecting 'a'"
11858        );
11859        assert_eq!(outcomes[1].diagnostics[0].message, "missing 'a' at 'x'");
11860    }
11861
11862    #[test]
11863    fn fast_outcome_selection_prefers_generated_caller_follow() {
11864        let earlier = FastRecognizeOutcome {
11865            index: 7,
11866            consumed_eof: false,
11867            diagnostics: FastDiagnostics::new(),
11868            nodes: NodeList::new(),
11869        };
11870        let later = FastRecognizeOutcome {
11871            index: 8,
11872            consumed_eof: false,
11873            diagnostics: FastDiagnostics::new(),
11874            nodes: NodeList::new(),
11875        };
11876        let mut follow = TokenBitSet::default();
11877        follow.insert(5);
11878
11879        let selected = select_best_fast_outcome(
11880            [later.clone(), earlier.clone()].into_iter(),
11881            PredictionMode::Ll,
11882            Some(&follow),
11883            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
11884        )
11885        .expect("one outcome should be selected");
11886        assert_eq!(selected.index, 7);
11887
11888        let selected = select_best_fast_outcome(
11889            [later.clone(), earlier.clone()].into_iter(),
11890            PredictionMode::Ll,
11891            Some(&follow),
11892            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
11893        )
11894        .expect("one outcome should be selected");
11895        assert_eq!(selected.index, 8);
11896
11897        let indented_next_statement = FastRecognizeOutcome {
11898            index: 9,
11899            consumed_eof: false,
11900            diagnostics: FastDiagnostics::new(),
11901            nodes: NodeList::new(),
11902        };
11903        let selected = select_best_fast_outcome(
11904            [indented_next_statement, earlier.clone()].into_iter(),
11905            PredictionMode::Ll,
11906            Some(&follow),
11907            |index| {
11908                let is_boundary = index == 7;
11909                let is_boundary_gap = matches!(index, 7 | 8);
11910                (
11911                    if index == 7 { 5 } else { TOKEN_EOF },
11912                    is_boundary,
11913                    is_boundary_gap,
11914                )
11915            },
11916        )
11917        .expect("one outcome should be selected");
11918        assert_eq!(selected.index, 7);
11919
11920        let continuation = FastRecognizeOutcome {
11921            index: 10,
11922            consumed_eof: false,
11923            diagnostics: FastDiagnostics::new(),
11924            nodes: NodeList::new(),
11925        };
11926        let selected = select_best_fast_outcome(
11927            [continuation, earlier.clone()].into_iter(),
11928            PredictionMode::Ll,
11929            Some(&follow),
11930            |index| {
11931                let is_boundary = matches!(index, 7 | 9);
11932                (
11933                    if index == 7 { 5 } else { TOKEN_EOF },
11934                    is_boundary,
11935                    is_boundary,
11936                )
11937            },
11938        )
11939        .expect("one outcome should be selected");
11940        assert_eq!(selected.index, 10);
11941
11942        let selected = select_best_fast_outcome(
11943            [earlier, later].into_iter(),
11944            PredictionMode::Sll,
11945            Some(&follow),
11946            |_| panic!("caller-follow token probe should not run in SLL mode"),
11947        )
11948        .expect("one outcome should be selected");
11949        assert_eq!(selected.index, 8);
11950    }
11951
11952    #[test]
11953    fn caller_follow_boundary_text_requires_separator_shape() {
11954        assert!(is_caller_follow_boundary_text(";"));
11955        assert!(is_caller_follow_boundary_text("\n"));
11956        assert!(is_caller_follow_boundary_text("\r\n  "));
11957        assert!(is_caller_follow_boundary_text(";\n"));
11958        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
11959        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
11960        assert!(!is_caller_follow_boundary_text("identifier"));
11961        assert!(is_caller_follow_boundary_gap_text(" \t "));
11962        assert!(is_caller_follow_boundary_gap_text("\n  "));
11963        assert!(is_caller_follow_boundary_gap_text(";\t"));
11964        assert!(!is_caller_follow_boundary_gap_text(
11965            "\"\"\"line1\nline2\"\"\""
11966        ));
11967        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
11968    }
11969
11970    #[test]
11971    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
11972        let mut parser = mini_parser(vec![
11973            CommonToken::new(5).with_text("\n"),
11974            CommonToken::new(6)
11975                .with_text("// comment\n")
11976                .with_channel(HIDDEN_CHANNEL),
11977            CommonToken::new(1).with_text("x"),
11978            CommonToken::eof("parser-test", 1, 2, 0),
11979        ]);
11980
11981        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
11982        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
11983        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
11984    }
11985
11986    #[test]
11987    fn caller_follow_token_info_uses_stream_visible_channel() {
11988        let source = Source {
11989            tokens: vec![
11990                CommonToken::new(5).with_text("\n").with_channel(2),
11991                CommonToken::new(1).with_text("x").with_channel(2),
11992                CommonToken::new(6)
11993                    .with_text("// comment\n")
11994                    .with_channel(HIDDEN_CHANNEL),
11995                CommonToken::eof("parser-test", 1, 2, 0),
11996            ],
11997            index: 0,
11998        };
11999        let data = RecognizerData::new(
12000            "Mini.g4",
12001            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12002        );
12003        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
12004
12005        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
12006        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
12007        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
12008    }
12009
12010    #[test]
12011    fn reset_per_parse_caches_clears_state_expected_token_cache() {
12012        let atn = token_then_eof_atn();
12013        let mut parser = mini_parser(Vec::new());
12014
12015        let _ = parser.cached_state_expected_token_set(&atn, 0);
12016        assert!(!parser.state_expected_token_cache.is_empty());
12017
12018        parser.reset_per_parse_caches();
12019        assert!(parser.state_expected_token_cache.is_empty());
12020    }
12021
12022    #[test]
12023    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
12024        let source = Source {
12025            tokens: vec![
12026                CommonToken::new(1).with_text("x"),
12027                CommonToken::eof("parser-test", 1, 1, 1),
12028            ],
12029            index: 0,
12030        };
12031        let data = RecognizerData::new(
12032            "Mini.g4",
12033            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12034        );
12035        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
12036        let expected = ExpectedTokens {
12037            index: Some(0),
12038            symbols: BTreeSet::new(),
12039            no_viable: None,
12040        };
12041
12042        let (_, message) = parser.expected_error_message(0, 0, &expected);
12043
12044        assert_eq!(message, "mismatched input 'x'");
12045    }
12046
12047    #[test]
12048    fn eof_rule_stop_index_points_at_eof_token() {
12049        let source = Source {
12050            tokens: vec![
12051                CommonToken::new(1).with_text("x"),
12052                CommonToken::eof("parser-test", 1, 1, 1),
12053            ],
12054            index: 0,
12055        };
12056        let data = RecognizerData::new(
12057            "Mini.g4",
12058            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
12059        );
12060        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
12061
12062        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
12063        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
12064    }
12065
12066    #[test]
12067    fn generated_parser_action_uses_current_rule_stop_boundary() {
12068        let mut parser = mini_parser(vec![
12069            CommonToken::new(1).with_text("x"),
12070            CommonToken::eof("parser-test", 1, 1, 1),
12071        ]);
12072
12073        parser.match_token(1).expect("token should match");
12074        let action = parser.parser_action_at_current(7, 0, 0, false);
12075        assert_eq!(action.source_state(), 7);
12076        assert_eq!(action.rule_index(), 0);
12077        assert_eq!(action.start_index(), 0);
12078        assert_eq!(action.stop_index(), Some(0));
12079
12080        parser.match_eof().expect("EOF should match");
12081        let action = parser.parser_action_at_current(8, 0, 0, true);
12082        assert_eq!(action.stop_index(), Some(1));
12083    }
12084
12085    #[test]
12086    fn folds_left_recursive_boundary_into_rule_node() {
12087        let nodes = fold_left_recursive_boundaries(vec![
12088            RecognizedNode::Token { index: 0 },
12089            RecognizedNode::LeftRecursiveBoundary { rule_index: 1 },
12090            RecognizedNode::Token { index: 1 },
12091        ]);
12092
12093        assert_eq!(
12094            nodes,
12095            vec![
12096                RecognizedNode::Rule {
12097                    rule_index: 1,
12098                    invoking_state: -1,
12099                    alt_number: 0,
12100                    start_index: 0,
12101                    stop_index: Some(0),
12102                    return_values: BTreeMap::new(),
12103                    children: vec![RecognizedNode::Token { index: 0 }],
12104                },
12105                RecognizedNode::Token { index: 1 },
12106            ]
12107        );
12108    }
12109
12110    #[test]
12111    fn outcome_ties_keep_later_non_recursive_alternative() {
12112        let first = RecognizeOutcome {
12113            index: 1,
12114            consumed_eof: false,
12115            alt_number: 0,
12116            member_values: BTreeMap::new(),
12117            return_values: BTreeMap::new(),
12118            diagnostics: Vec::new(),
12119            decisions: Vec::new(),
12120            actions: vec![ParserAction::new(1, 0, 0, None)],
12121            nodes: vec![RecognizedNode::Token { index: 0 }],
12122        };
12123        let second = RecognizeOutcome {
12124            actions: vec![ParserAction::new(2, 0, 0, None)],
12125            ..first.clone()
12126        };
12127
12128        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
12129            .expect("one outcome should be selected");
12130        assert_eq!(selected.actions[0].source_state(), 2);
12131    }
12132
12133    #[test]
12134    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
12135        let first = RecognizeOutcome {
12136            index: 1,
12137            consumed_eof: false,
12138            alt_number: 0,
12139            member_values: BTreeMap::new(),
12140            return_values: BTreeMap::new(),
12141            diagnostics: Vec::new(),
12142            decisions: Vec::new(),
12143            actions: vec![ParserAction::new(1, 0, 0, None)],
12144            nodes: vec![RecognizedNode::Token { index: 0 }],
12145        };
12146        let second = RecognizeOutcome {
12147            actions: vec![
12148                ParserAction::new(2, 0, 0, None),
12149                ParserAction::new(3, 0, 0, None),
12150            ],
12151            ..first.clone()
12152        };
12153
12154        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll)
12155            .expect("one outcome should be selected");
12156        assert_eq!(selected.actions.len(), 2);
12157    }
12158
12159    #[test]
12160    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
12161        let first = RecognizeOutcome {
12162            index: 7,
12163            consumed_eof: false,
12164            alt_number: 0,
12165            member_values: BTreeMap::new(),
12166            return_values: BTreeMap::new(),
12167            diagnostics: Vec::new(),
12168            decisions: vec![1, 0],
12169            actions: vec![
12170                ParserAction::new(23, 2, 2, Some(4)),
12171                ParserAction::new(23, 2, 0, Some(6)),
12172            ],
12173            nodes: vec![RecognizedNode::Token { index: 0 }],
12174        };
12175        let second = RecognizeOutcome {
12176            decisions: vec![0, 1],
12177            actions: vec![
12178                ParserAction::new(23, 2, 2, Some(6)),
12179                ParserAction::new(23, 2, 0, Some(6)),
12180            ],
12181            ..first.clone()
12182        };
12183
12184        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
12185            .expect("one outcome should be selected");
12186        assert_eq!(selected.actions[0].stop_index(), Some(6));
12187    }
12188
12189    #[test]
12190    fn outcome_ties_keep_first_recursive_tree_shape() {
12191        let recursive_nodes = vec![RecognizedNode::Rule {
12192            rule_index: 1,
12193            invoking_state: -1,
12194            alt_number: 0,
12195            start_index: 0,
12196            stop_index: Some(0),
12197            return_values: BTreeMap::new(),
12198            children: vec![RecognizedNode::Rule {
12199                rule_index: 1,
12200                invoking_state: -1,
12201                alt_number: 0,
12202                start_index: 0,
12203                stop_index: Some(0),
12204                return_values: BTreeMap::new(),
12205                children: vec![RecognizedNode::Token { index: 0 }],
12206            }],
12207        }];
12208        let first = RecognizeOutcome {
12209            index: 1,
12210            consumed_eof: false,
12211            alt_number: 0,
12212            member_values: BTreeMap::new(),
12213            return_values: BTreeMap::new(),
12214            diagnostics: Vec::new(),
12215            decisions: Vec::new(),
12216            actions: vec![ParserAction::new(1, 0, 0, None)],
12217            nodes: recursive_nodes.clone(),
12218        };
12219        let second = RecognizeOutcome {
12220            index: 1,
12221            consumed_eof: false,
12222            alt_number: 0,
12223            member_values: BTreeMap::new(),
12224            return_values: BTreeMap::new(),
12225            diagnostics: Vec::new(),
12226            decisions: Vec::new(),
12227            actions: vec![ParserAction::new(2, 0, 0, None)],
12228            nodes: recursive_nodes,
12229        };
12230
12231        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
12232            .expect("one outcome should be selected");
12233        assert_eq!(selected.actions[0].source_state(), 1);
12234    }
12235
12236    #[test]
12237    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
12238        let first_alt = RecognizeOutcome {
12239            index: 2,
12240            consumed_eof: true,
12241            alt_number: 0,
12242            member_values: BTreeMap::new(),
12243            return_values: BTreeMap::new(),
12244            diagnostics: vec![ParserDiagnostic {
12245                line: 1,
12246                column: 3,
12247                message: "missing 'Y' at '<EOF>'".to_owned(),
12248            }],
12249            decisions: vec![0],
12250            actions: vec![ParserAction::new(1, 0, 0, None)],
12251            nodes: vec![RecognizedNode::Token { index: 0 }],
12252        };
12253        let second_alt = RecognizeOutcome {
12254            diagnostics: Vec::new(),
12255            decisions: vec![1],
12256            actions: vec![ParserAction::new(2, 0, 0, None)],
12257            ..first_alt.clone()
12258        };
12259
12260        let selected =
12261            select_best_outcome([second_alt, first_alt].into_iter(), PredictionMode::Sll)
12262                .expect("one outcome should be selected");
12263        assert_eq!(selected.diagnostics.len(), 1);
12264        assert_eq!(selected.decisions, [0]);
12265    }
12266}